Code

small cleanup in src/trace (warnings and trailing spaces)
[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 "preferences.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 <2geom/bezier-utils.h>
52 #include <vector>
53 #include <algorithm>
54 #include <cstring>
55 #include <cmath>
56 #include "live_effects/lpeobject.h"
57 #include "live_effects/lpeobject-reference.h"
58 #include "live_effects/effect.h"
59 #include "live_effects/parameter/parameter.h"
60 #include "live_effects/parameter/path.h"
61 #include "util/mathfns.h"
62 #include "display/snap-indicator.h"
63 #include "snapped-point.h"
65 namespace Geom { class Matrix; }
67 /// \todo
68 /// evil evil evil. FIXME: conflict of two different Path classes!
69 /// There is a conflict in the namespace between two classes named Path.
70 /// #include "sp-flowtext.h"
71 /// #include "sp-flowregion.h"
73 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
74 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
75 GType sp_flowregion_get_type (void);
76 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
77 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
78 GType sp_flowtext_get_type (void);
79 // end evil workaround
81 #include "helper/stlport.h"
84 /// \todo fixme: Implement these via preferences */
86 #define NODE_FILL          0xbfbfbf00
87 #define NODE_STROKE        0x000000ff
88 #define NODE_FILL_HI       0xff000000
89 #define NODE_STROKE_HI     0x000000ff
90 #define NODE_FILL_SEL      0x0000ffff
91 #define NODE_STROKE_SEL    0x000000ff
92 #define NODE_FILL_SEL_HI   0xff000000
93 #define NODE_STROKE_SEL_HI 0x000000ff
94 #define KNOT_FILL          0xffffffff
95 #define KNOT_STROKE        0x000000ff
96 #define KNOT_FILL_HI       0xff000000
97 #define KNOT_STROKE_HI     0x000000ff
99 static GMemChunk *nodechunk = NULL;
101 /* Creation from object */
103 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
104 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
106 /* Object updating */
108 static void stamp_repr(Inkscape::NodePath::Path *np);
109 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
110 static gchar *create_typestr(Inkscape::NodePath::Path *np);
112 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
114 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
116 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
118 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
120 /* Adjust handle placement, if the node or the other handle is moved */
121 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
122 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
123 static void sp_node_adjust_handles_auto(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, Geom::Point const &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, Geom::Point &p, guint state, gpointer data);
136 static void node_handle_moved(SPKnot *knot, Geom::Point const &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                                          Geom::Point *ppos, Geom::Point *pos, Geom::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->get_lpe();
195         if (lpe) {
196             // create new canvas items from the effect's helper paths
197             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
198             for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
199                 np->helper_path_vec[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
200             }
201         }
202     }
205 void
206 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
207     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
208     if (!SP_IS_LPE_ITEM(np->item)) {
209         g_print ("Only LPEItems can have helperpaths!\n");
210         return;
211     }
213     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
214     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
215     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
216         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
217         if (lpe) {
218             /* update canvas items from the effect's helper paths; note that this code relies on the
219              * fact that getHelperPaths() will always return the same number of helperpaths in the same
220              * order as during their creation in sp_nodepath_create_helperpaths
221              */
222             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
223             for (unsigned int j = 0; j < hpaths.size(); ++j) {
224                 SPCurve *curve = new SPCurve(hpaths[j]);
225                 curve->transform(np->i2d);
226                 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH((np->helper_path_vec[lpe])[j]), curve);
227                 curve = curve->unref();
228             }
229         }
230     }
233 static void
234 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
235     for (HelperPathList::iterator i = np->helper_path_vec.begin(); i != np->helper_path_vec.end(); ++i) {
236         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
237             GtkObject *temp = *j;
238             *j = NULL;
239             gtk_object_destroy(temp);
240         }
241     }
242     np->helper_path_vec.clear();
246 /**
247  * \brief Creates new nodepath from item
248  *
249  * \todo create proper constructor for nodepath::path, this method returns null a constructor cannot so this cannot be simply converted to constructor.
250  */
251 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
253     Inkscape::XML::Node *repr = object->repr;
255     /** \todo
256      * FIXME: remove this. We don't want to edit paths inside flowtext.
257      * Instead we will build our flowtext with cloned paths, so that the
258      * real paths are outside the flowtext and thus editable as usual.
259      */
260     if (SP_IS_FLOWTEXT(object)) {
261         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
262             if SP_IS_FLOWREGION(child) {
263                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
264                 if (grandchild && SP_IS_PATH(grandchild)) {
265                     object = SP_ITEM(grandchild);
266                     break;
267                 }
268             }
269         }
270     }
272     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
274     if (curve == NULL) {
275         return NULL;
276     }
278     if (curve->get_segment_count() < 1) {
279         curve->unref();
280         return NULL; // prevent crash for one-node paths
281     }
283     //Create new nodepath
284     Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path();
285     if (!np) {
286         curve->unref();
287         return NULL;
288     }
290     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
292     // Set defaults
293     np->desktop     = desktop;
294     np->object      = object;
295     np->subpaths    = NULL;
296     np->selected    = NULL;
297     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
298     np->local_change = 0;
299     np->show_handles = show_handles;
300     np->helper_path = NULL;
301     np->helperpath_rgba = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
302     np->helperpath_width = 1.0;
303     np->curve = curve->copy();
304     np->show_helperpath = prefs->getBool("/tools/nodes/show_helperpath");
305     if (SP_IS_LPE_ITEM(object)) {
306         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
307         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
308             np->show_helperpath = true;
309         }            
310     }
311     np->straight_path = false;
312     if (IS_LIVEPATHEFFECT(object) && item) {
313         np->item = item;
314     } else {
315         np->item = SP_ITEM(object);
316     }
317     
318     np->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
320     // we need to update item's transform from the repr here,
321     // because they may be out of sync when we respond
322     // to a change in repr by regenerating nodepath     --bb
323     sp_object_read_attr(SP_OBJECT(np->item), "transform");
325     np->i2d  = sp_item_i2d_affine(np->item);
326     np->d2i  = np->i2d.inverse();
328     np->repr = repr;
329     if (repr_key_in) { // apparently the object is an LPEObject (this is a dirty check, hopefully nobody tries feeding non-lpeobjects into this method with non-null repr_key_in)
330         np->repr_key = g_strdup(repr_key_in);
331         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
332         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe();
333         if (!lpe) {
334             g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!");
335             delete np;
336         }
337         Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in);
338         if (lpeparam) {
339             lpeparam->param_setup_nodepath(np);
340         }
341     } else {
342         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
343         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
344             np->repr_key = g_strdup("inkscape:original-d");
346             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
347             if (lpe) {
348                 lpe->setup_nodepath(np);
349             }
350         } else {
351             np->repr_key = g_strdup("d");
352         }
353     }
355     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
356      * So for example a closed rectangle has a nodetypestring of length 5.
357      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
358     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
359     np->curve->set_pathvector(pathv_sanitized);
360     guint length = np->curve->get_segment_count();
361     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
362         length += pit->empty() ? 0 : 1;
363     }
365     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
366     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
368     // create the subpath(s) from the bpath
369     subpaths_from_pathvector(np, pathv_sanitized, typestr);
371     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
372     np->subpaths = g_list_reverse(np->subpaths);
374     delete[] typestr;
375     curve->unref();
377     // Draw helper curve
378     if (np->show_helperpath) {
379         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
380     }
382     sp_nodepath_create_helperpaths(np);
384     return np;
387 /**
388  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
389  */
390 Inkscape::NodePath::Path::~Path() {
391     while (this->subpaths) {
392         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data);
393     }
395     //Inform the ShapeEditor that made me, if any, that I am gone.
396     if (this->shape_editor)
397         this->shape_editor->nodepath_destroyed();
399     g_assert(!this->selected);
401     if (this->helper_path) {
402         GtkObject *temp = this->helper_path;
403         this->helper_path = NULL;
404         gtk_object_destroy(temp);
405     }
406     if (this->curve) {
407         this->curve->unref();
408         this->curve = NULL;
409     }
411     if (this->repr_key) {
412         g_free(this->repr_key);
413         this->repr_key = NULL;
414     }
415     if (this->repr_nodetypes_key) {
416         g_free(this->repr_nodetypes_key);
417         this->repr_nodetypes_key = NULL;
418     }
420     sp_nodepath_destroy_helperpaths(this);
422     this->desktop = NULL;
425 /**
426  *  Return the node count of a given NodeSubPath.
427  */
428 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
430     int nodeCount = 0;
432     if (subpath) {
433         nodeCount = g_list_length(subpath->nodes);
434     }
436     return nodeCount;
439 /**
440  *  Return the node count of a given NodePath.
441  */
442 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
444     gint nodeCount = 0;
445     if (np) {
446         for (GList *item = np->subpaths ; item ; item=item->next) {
447             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
448             nodeCount += g_list_length(subpath->nodes);
449         }
450     }
451     return nodeCount;
454 /**
455  *  Return the subpath count of a given NodePath.
456  */
457 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
459     gint nodeCount = 0;
460     if (np) {
461         nodeCount = g_list_length(np->subpaths);
462     }
463     return nodeCount;
466 /**
467  *  Return the selected node count of a given NodePath.
468  */
469 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
471     gint nodeCount = 0;
472     if (np) {
473         nodeCount = g_list_length(np->selected);
474     }
475     return nodeCount;
478 /**
479  *  Return the number of subpaths where nodes are selected in a given NodePath.
480  */
481 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
483     gint nodeCount = 0;
484     if (np && np->selected) {
485         if (!np->selected->next) {
486             nodeCount = 1;
487         } else {
488             for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
489                 Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
490                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
491                     Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
492                     if (node->selected) {
493                         nodeCount++;
494                         break;
495                     }
496                 }
497             }
498         }
499     }
500     return nodeCount;
503 /**
504  * Clean up a nodepath after editing.
505  *
506  * Currently we are deleting trivial subpaths.
507  */
508 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
510     GList *badSubPaths = NULL;
512     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
513     for (GList *l = nodepath->subpaths; l ; l=l->next) {
514        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
515        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
516             badSubPaths = g_list_append(badSubPaths, sp);
517     }
519     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
520     //also removes the subpath from nodepath->subpaths
521     for (GList *l = badSubPaths; l ; l=l->next) {
522        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
523         sp_nodepath_subpath_destroy(sp);
524     }
526     g_list_free(badSubPaths);
529 /**
530  * Create new nodepaths from pathvector, make it subpaths of np.
531  * \param t The node type array.
532  */
533 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
535     guint i = 0;  // index into node type array
536     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
537         if (pit->empty())
538             continue;  // don't add single knot paths
540         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
542         Geom::Point ppos = pit->initialPoint() * np->i2d;
543         NRPathcode pcode = NR_MOVETO;
545         /* 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)*/
546         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
547             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
548                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
549                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
550             {
551                 Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
552                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
554                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
555                 pcode = NR_LINETO;
556             }
557             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
558                 std::vector<Geom::Point> points = cubic_bezier->points();
559                 Geom::Point pos = points[0] * (Geom::Matrix)np->i2d;
560                 Geom::Point npos = points[1] * (Geom::Matrix)np->i2d;
561                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
563                 ppos = points[2] * (Geom::Matrix)np->i2d;
564                 pcode = NR_CURVETO;
565             }
566         }
568         if (pit->closed()) {
569             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
570             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
571              * If the length is zero, don't add it to the nodepath. */
572             Geom::Curve const &closing_seg = pit->back_closed();
573             // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289)
574             if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) {
575                 Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
576                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
577             }
579             sp_nodepath_subpath_close(sp);
580         }
581     }
584 /**
585  * Convert from sodipodi:nodetypes to new style type array.
586  */
587 static
588 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
590     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
592     guint pos = 0;
594     if (types) {
595         for (guint i = 0; types[i] && ( i < length ); i++) {
596             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
597             if (types[i] != '\0') {
598                 switch (types[i]) {
599                     case 's':
600                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
601                         break;
602                     case 'a':
603                         typestr[pos++] =Inkscape::NodePath::NODE_AUTO;
604                         break;
605                     case 'z':
606                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
607                         break;
608                     case 'c':
609                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
610                         break;
611                     default:
612                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
613                         break;
614                 }
615             }
616         }
617     }
619     while (pos < length) {
620         typestr[pos++] = Inkscape::NodePath::NODE_NONE;
621     }
623     return typestr;
626 /**
627  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
628  * updated but repr is not (for speed). Used during curve and node drag.
629  */
630 static void update_object(Inkscape::NodePath::Path *np)
632     g_assert(np);
634     np->curve->unref();
635     np->curve = create_curve(np);
637     sp_nodepath_set_curve(np, np->curve);
639     if (np->show_helperpath) {
640         SPCurve * helper_curve = np->curve->copy();
641         helper_curve->transform(np->i2d);
642         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
643         helper_curve->unref();
644     }
646     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
647     //sp_nodepath_update_helperpaths(np);
649     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
650     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
651     np->shape_editor->update_knotholder();
654 /**
655  * Update XML path node with data from path object.
656  */
657 static void update_repr_internal(Inkscape::NodePath::Path *np)
659     g_assert(np);
661     Inkscape::XML::Node *repr = np->object->repr;
663     np->curve->unref();
664     np->curve = create_curve(np);
666     gchar *typestr = create_typestr(np);
667     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
669     // determine if path has an effect applied and write to correct "d" attribute.
670     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
671         np->local_change++;
672         repr->setAttribute(np->repr_key, svgpath);
673     }
675     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
676         np->local_change++;
677         repr->setAttribute(np->repr_nodetypes_key, typestr);
678     }
680     g_free(svgpath);
681     g_free(typestr);
683     if (np->show_helperpath) {
684         SPCurve * helper_curve = np->curve->copy();
685         helper_curve->transform(np->i2d);
686         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
687         helper_curve->unref();
688     }
690     // TODO: do we need this call here? after all, update_object() should have been called just before
691     //sp_nodepath_update_helperpaths(np);
694 /**
695  * Update XML path node with data from path object, commit changes forever.
696  */
697 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
699     //fixme: np can be NULL, so check before proceeding
700     g_return_if_fail(np != NULL);
702     update_repr_internal(np);
703     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
705     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
706                      annotation);
709 /**
710  * Update XML path node with data from path object, commit changes with undo.
711  */
712 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
714     update_repr_internal(np);
715     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
716                            annotation);
719 /**
720  * Make duplicate of path, replace corresponding XML node in tree, commit.
721  */
722 static void stamp_repr(Inkscape::NodePath::Path *np)
724     g_assert(np);
726     Inkscape::XML::Node *old_repr = np->object->repr;
727     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
729     // remember the position of the item
730     gint pos = old_repr->position();
731     // remember parent
732     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
734     SPCurve *curve = create_curve(np);
735     gchar *typestr = create_typestr(np);
737     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
739     new_repr->setAttribute(np->repr_key, svgpath);
740     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
742     // add the new repr to the parent
743     parent->appendChild(new_repr);
744     // move to the saved position
745     new_repr->setPosition(pos > 0 ? pos : 0);
747     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
748                      _("Stamp"));
750     Inkscape::GC::release(new_repr);
751     g_free(svgpath);
752     g_free(typestr);
753     curve->unref();
756 /**
757  * Create curve from path.
758  */
759 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
761     SPCurve *curve = new SPCurve();
763     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
764        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
765        curve->moveto(sp->first->pos * np->d2i);
766        Inkscape::NodePath::Node *n = sp->first->n.other;
767         while (n) {
768             Geom::Point const end_pt = n->pos * np->d2i;
769             if (!IS_FINITE(n->pos[0]) || !IS_FINITE(n->pos[1])){
770                 g_message("niet finite");
771             }
772             switch (n->code) {
773                 case NR_LINETO:
774                     curve->lineto(end_pt);
775                     break;
776                 case NR_CURVETO:
777                     curve->curveto(n->p.other->n.pos * np->d2i,
778                                      n->p.pos * np->d2i,
779                                      end_pt);
780                     break;
781                 default:
782                     g_assert_not_reached();
783                     break;
784             }
785             if (n != sp->last) {
786                 n = n->n.other;
787             } else {
788                 n = NULL;
789             }
790         }
791         if (sp->closed) {
792             curve->closepath();
793         }
794     }
796     return curve;
799 /**
800  * Convert path type string to sodipodi:nodetypes style.
801  */
802 static gchar *create_typestr(Inkscape::NodePath::Path *np)
804     gchar *typestr = g_new(gchar, 32);
805     gint len = 32;
806     gint pos = 0;
808     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
809        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
811         if (pos >= len) {
812             typestr = g_renew(gchar, typestr, len + 32);
813             len += 32;
814         }
816         typestr[pos++] = 'c';
818        Inkscape::NodePath::Node *n;
819         n = sp->first->n.other;
820         while (n) {
821             gchar code;
823             switch (n->type) {
824                 case Inkscape::NodePath::NODE_CUSP:
825                     code = 'c';
826                     break;
827                 case Inkscape::NodePath::NODE_SMOOTH:
828                     code = 's';
829                     break;
830                 case Inkscape::NodePath::NODE_AUTO:
831                     code = 'a';
832                     break;
833                 case Inkscape::NodePath::NODE_SYMM:
834                     code = 'z';
835                     break;
836                 default:
837                     g_assert_not_reached();
838                     code = '\0';
839                     break;
840             }
842             if (pos >= len) {
843                 typestr = g_renew(gchar, typestr, len + 32);
844                 len += 32;
845             }
847             typestr[pos++] = code;
849             if (n != sp->last) {
850                 n = n->n.other;
851             } else {
852                 n = NULL;
853             }
854         }
855     }
857     if (pos >= len) {
858         typestr = g_renew(gchar, typestr, len + 1);
859         len += 1;
860     }
862     typestr[pos++] = '\0';
864     return typestr;
867 // Returns different message contexts depending on the current context. This function should only
868 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
869 // other cases.
870 static Inkscape::MessageContext *
871 get_message_context(SPEventContext *ec)
873     Inkscape::MessageContext *mc = 0;
875     if (SP_IS_NODE_CONTEXT(ec)) {
876         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
877     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
878         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
879     } else {
880         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
881     }
883     return mc;
886 /**
887  \brief Fills node and handle positions for three nodes, splitting line
888   marked by end at distance t.
889  */
890 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
892     g_assert(new_path != NULL);
893     g_assert(end      != NULL);
895     g_assert(end->p.other == new_path);
896    Inkscape::NodePath::Node *start = new_path->p.other;
897     g_assert(start);
899     if (end->code == NR_LINETO) {
900         new_path->type =Inkscape::NodePath::NODE_CUSP;
901         new_path->code = NR_LINETO;
902         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
903     } else {
904         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
905         new_path->code = NR_CURVETO;
906         gdouble s      = 1 - t;
907         for (int dim = 0; dim < 2; dim++) {
908             Geom::Coord const f000 = start->pos[dim];
909             Geom::Coord const f001 = start->n.pos[dim];
910             Geom::Coord const f011 = end->p.pos[dim];
911             Geom::Coord const f111 = end->pos[dim];
912             Geom::Coord const f00t = s * f000 + t * f001;
913             Geom::Coord const f01t = s * f001 + t * f011;
914             Geom::Coord const f11t = s * f011 + t * f111;
915             Geom::Coord const f0tt = s * f00t + t * f01t;
916             Geom::Coord const f1tt = s * f01t + t * f11t;
917             Geom::Coord const fttt = s * f0tt + t * f1tt;
918             start->n.pos[dim]    = f00t;
919             new_path->p.pos[dim] = f0tt;
920             new_path->pos[dim]   = fttt;
921             new_path->n.pos[dim] = f1tt;
922             end->p.pos[dim]      = f11t;
923         }
924     }
927 /**
928  * Adds new node on direct line between two nodes, activates handles of all
929  * three nodes.
930  */
931 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
933     g_assert(end);
934     g_assert(end->subpath);
935     g_assert(g_list_find(end->subpath->nodes, end));
937    Inkscape::NodePath::Node *start = end->p.other;
938     g_assert( start->n.other == end );
939    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
940                                                end,
941                                                (NRPathcode)end->code == NR_LINETO?
942                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
943                                                (NRPathcode)end->code,
944                                                &start->pos, &start->pos, &start->n.pos);
945     sp_nodepath_line_midpoint(newnode, end, t);
947     sp_node_adjust_handles(start);
948     sp_node_update_handles(start);
949     sp_node_update_handles(newnode);
950     sp_node_adjust_handles(end);
951     sp_node_update_handles(end);
953     return newnode;
956 /**
957 \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
958 */
959 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
961     g_assert(node);
962     g_assert(node->subpath);
963     g_assert(g_list_find(node->subpath->nodes, node));
965     Inkscape::NodePath::Node* result = 0;
966     Inkscape::NodePath::SubPath *sp = node->subpath;
967     Inkscape::NodePath::Path *np    = sp->nodepath;
969     if (sp->closed) {
970         sp_nodepath_subpath_open(sp, node);
971         result = sp->first;
972     } else if ( (node == sp->first) || (node == sp->last ) ){
973         // no break for end nodes
974         result = 0;
975     } else {
976         // create a new subpath
977         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
979         // duplicate the break node as start of the new subpath
980         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
981                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
982                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
984         // attach rest of curve to new node
985         g_assert(node->n.other);
986         newnode->n.other = node->n.other; node->n.other = NULL;
987         newnode->n.other->p.other = newnode;
988         newsubpath->last = sp->last;
989         sp->last = node;
990         node = newnode;
991         while (node->n.other) {
992             node = node->n.other;
993             node->subpath = newsubpath;
994             sp->nodes = g_list_remove(sp->nodes, node);
995             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
996         }
999         result = newnode;
1000     }
1001     return result;
1004 /**
1005  * Duplicate node and connect to neighbours.
1006  */
1007 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
1009     g_assert(node);
1010     g_assert(node->subpath);
1011     g_assert(g_list_find(node->subpath->nodes, node));
1013    Inkscape::NodePath::SubPath *sp = node->subpath;
1015     NRPathcode code = (NRPathcode) node->code;
1016     if (code == NR_MOVETO) { // if node is the endnode,
1017         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1018     }
1020     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1022     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1023         return node;
1024     } else {
1025         return newnode; // otherwise select the newly created node
1026     }
1029 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1031     node->p.pos = (node->pos + (node->pos - node->n.pos));
1034 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1036     node->n.pos = (node->pos + (node->pos - node->p.pos));
1039 /**
1040  * Change line type at node, with side effects on neighbours.
1041  */
1042 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1044     g_assert(end);
1045     g_assert(end->subpath);
1046     g_assert(end->p.other);
1048     if (end->code != static_cast<guint>(code) ) {
1049         Inkscape::NodePath::Node *start = end->p.other;
1051         end->code = code;
1053         if (code == NR_LINETO) {
1054             if (start->code == NR_LINETO) {
1055                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1056             }
1057             if (end->n.other) {
1058                 if (end->n.other->code == NR_LINETO) {
1059                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1060                 }
1061             }
1063             if (start->type == Inkscape::NodePath::NODE_AUTO)
1064                 start->type = Inkscape::NodePath::NODE_SMOOTH;
1065             if (end->type == Inkscape::NodePath::NODE_AUTO)
1066                 end->type = Inkscape::NodePath::NODE_SMOOTH;
1067     
1068             start->n.pos = start->pos;
1069             end->p.pos = end->pos;
1071             sp_node_adjust_handle(start, -1);
1072             sp_node_adjust_handle(end, 1);
1074         } else {
1075             Geom::Point delta = end->pos - start->pos;
1076             start->n.pos = start->pos + delta / 3;
1077             end->p.pos = end->pos - delta / 3;
1078             sp_node_adjust_handle(start, 1);
1079             sp_node_adjust_handle(end, -1);
1080         }
1082         sp_node_update_handles(start);
1083         sp_node_update_handles(end);
1084     }
1087 static void
1088 sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
1090     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1091         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1092         node->knot->setSize (node->selected? 11 : 9);
1093         sp_knot_update_ctrl(node->knot);
1094     } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
1095         node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1096         node->knot->setSize (node->selected? 11 : 9);
1097         sp_knot_update_ctrl(node->knot);
1098     } else {
1099         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1100         node->knot->setSize (node->selected? 9 : 7);
1101         sp_knot_update_ctrl(node->knot);
1102     }
1106 /**
1107  * Change node type, and its handles accordingly.
1108  */
1109 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1111     g_assert(node);
1112     g_assert(node->subpath);
1114     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1115         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1116             type =Inkscape::NodePath::NODE_CUSP;
1117         }
1118     }
1120     node->type = type;
1122     sp_nodepath_update_node_knot(node);
1124     // if one of handles is mouseovered, preserve its position
1125     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1126         sp_node_adjust_handle(node, 1);
1127     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1128         sp_node_adjust_handle(node, -1);
1129     } else {
1130         sp_node_adjust_handles(node);
1131     }
1133     sp_node_update_handles(node);
1135     sp_nodepath_update_statusbar(node->subpath->nodepath);
1137     return node;
1140 bool
1141 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1143 // TODO clean up multiple returns
1144         Inkscape::NodePath::Node *othernode = side->other;
1145         if (!othernode)
1146             return false;
1147         NRPathcode const code = sp_node_path_code_from_side(node, side);
1148         if (code == NR_LINETO)
1149             return true;
1150         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1151         if (&node->p == side) {
1152             other_to_me = &othernode->n;
1153         } else if (&node->n == side) {
1154             other_to_me = &othernode->p;
1155         } 
1156         if (!other_to_me)
1157             return false;
1158         bool is_line = 
1159              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1160               Geom::L2(node->pos - side->pos) < 1e-6);
1161         return is_line;
1164 /**
1165  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1166  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1167  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1168  * If already cusp and set to cusp, retracts handles.
1169 */
1170 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1172     if (type == Inkscape::NodePath::NODE_AUTO) {
1173         if (node->p.other != NULL)
1174             node->code = NR_CURVETO;
1175         if (node->n.other != NULL)
1176             node->n.other->code = NR_CURVETO;
1177     }
1179     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1181 /* 
1182   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1183  
1184         if (two_handles) {
1185             // do nothing, adjust_handles called via set_node_type will line them up
1186         } else if (one_handle) {
1187             if (opposite_to_handle_is_line) {
1188                 if (lined_up) {
1189                     // already half-smooth; pull opposite handle too making it fully smooth
1190                 } else {
1191                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1192                 }
1193             } else {
1194                 // pull opposite handle in line with the existing one
1195             }
1196         } else if (no_handles) {
1197             if (both_segments_are_lines OR both_segments_are_curves) {
1198                 //pull both handles
1199             } else {
1200                 // pull the handle opposite to line segment, making node half-smooth
1201             }
1202         }
1203 */
1204         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1205         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1206         bool p_is_line = sp_node_side_is_line(node, &node->p);
1207         bool n_is_line = sp_node_side_is_line(node, &node->n);
1209         if (p_has_handle && n_has_handle) {
1210             // do nothing, adjust_handles will line them up
1211         } else if (p_has_handle || n_has_handle) {
1212             if (p_has_handle && n_is_line) {
1213                 Radial line (node->n.other->pos - node->pos);
1214                 Radial handle (node->pos - node->p.pos);
1215                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1216                     // already half-smooth; pull opposite handle too making it fully smooth
1217                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1218                 } else {
1219                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1220                 }
1221             } else if (n_has_handle && p_is_line) {
1222                 Radial line (node->p.other->pos - node->pos);
1223                 Radial handle (node->pos - node->n.pos);
1224                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1225                     // already half-smooth; pull opposite handle too making it fully smooth
1226                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1227                 } else {
1228                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1229                 }
1230             } else if (p_has_handle && node->n.other) {
1231                 // pull n handle
1232                 node->n.other->code = NR_CURVETO;
1233                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1234                     Geom::L2(node->p.pos - node->pos) :
1235                     Geom::L2(node->n.other->pos - node->pos) / 3;
1236                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1237             } else if (n_has_handle && node->p.other) {
1238                 // pull p handle
1239                 node->code = NR_CURVETO;
1240                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1241                     Geom::L2(node->n.pos - node->pos) :
1242                     Geom::L2(node->p.other->pos - node->pos) / 3;
1243                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1244             }
1245         } else if (!p_has_handle && !n_has_handle) {
1246             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1247                 // no handles, but both segments are either lnes or curves:
1248                 //pull both handles
1250                 // convert both to curves:
1251                 node->code = NR_CURVETO;
1252                 node->n.other->code = NR_CURVETO;
1254                 sp_node_adjust_handles_auto(node);
1255             } else {
1256                 // pull the handle opposite to line segment, making it half-smooth
1257                 if (p_is_line && node->n.other) {
1258                     if (type != Inkscape::NodePath::NODE_SYMM) {
1259                         // pull n handle
1260                         node->n.other->code = NR_CURVETO;
1261                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1262                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1263                     }
1264                 } else if (n_is_line && node->p.other) {
1265                     if (type != Inkscape::NodePath::NODE_SYMM) {
1266                         // pull p handle
1267                         node->code = NR_CURVETO;
1268                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1269                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1270                     }
1271                 }
1272             }
1273         }
1274     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1275         // cusping a cusp: retract nodes
1276         node->p.pos = node->pos;
1277         node->n.pos = node->pos;
1278     }
1280     sp_nodepath_set_node_type (node, type);
1283 /**
1284  * Move node to point, and adjust its and neighbouring handles.
1285  */
1286 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1288     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1289         node->pos = p;
1290         sp_node_adjust_handles_auto(node);
1291     } else {
1292         Geom::Point delta = p - node->pos;
1293         node->pos = p;
1295         node->p.pos += delta;
1296         node->n.pos += delta;
1297     }
1299     Inkscape::NodePath::Node *node_p = NULL;
1300     Inkscape::NodePath::Node *node_n = NULL;
1302     if (node->p.other) {
1303         if (node->code == NR_LINETO) {
1304             sp_node_adjust_handle(node, 1);
1305             sp_node_adjust_handle(node->p.other, -1);
1306             node_p = node->p.other;
1307         }
1308         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1309             sp_node_adjust_handles_auto(node->p.other);
1310             node_p = node->p.other;
1311         }
1312     }
1313     if (node->n.other) {
1314         if (node->n.other->code == NR_LINETO) {
1315             sp_node_adjust_handle(node, -1);
1316             sp_node_adjust_handle(node->n.other, 1);
1317             node_n = node->n.other;
1318         }
1319         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1320             sp_node_adjust_handles_auto(node->n.other);
1321             node_n = node->n.other;
1322         }
1323     }
1325     // this function is only called from batch movers that will update display at the end
1326     // themselves, so here we just move all the knots without emitting move signals, for speed
1327     sp_node_update_handles(node, false);
1328     if (node_n) {
1329         sp_node_update_handles(node_n, false);
1330     }
1331     if (node_p) {
1332         sp_node_update_handles(node_p, false);
1333     }
1336 /**
1337  * Call sp_node_moveto() for node selection and handle possible snapping.
1338  */
1339 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1340                                             bool const snap, bool constrained = false, 
1341                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1343     Geom::Point delta(dx, dy);
1344     Geom::Point best_pt = delta;
1345     Inkscape::SnappedPoint best;
1346     
1347     if (snap) {    
1348         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1349          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1350          * must provide that information. */
1351           
1352         // Build a list of the unselected nodes to which the snapper should snap 
1353         std::vector<Geom::Point> unselected_nodes;
1354         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1355             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1356             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1357                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1358                 if (!node->selected) {
1359                     unselected_nodes.push_back(to_2geom(node->pos));
1360                 }    
1361             }
1362         }        
1363         
1364         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1365         
1366         // When only the node closest to the mouse pointer is to be snapped 
1367         // then we will not even try to snap to other points and discard those immediately
1368         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1369         bool closest_only = prefs->getBool("/options/snapclosestonly/value", false); 
1370         
1371         Inkscape::NodePath::Node *closest_node = NULL;
1372         Geom::Coord closest_dist = NR_HUGE;
1373         
1374         if (closest_only) {
1375                 for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1376                         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1377                         Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
1378                         if (dist < closest_dist) {
1379                                 closest_node = n;
1380                                 closest_dist = dist;
1381                         }
1382                 }                       
1383         }
1384         
1385         // Iterate through all selected nodes
1386         m.setup(nodepath->desktop, false, SP_PATH(nodepath->item), &unselected_nodes);
1387         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1388             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1389             if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
1390                     Inkscape::SnappedPoint s;            
1391                     if (constrained) {
1392                         Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1393                         dedicated_constraint.setPoint(n->pos);
1394                         s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
1395                     } else {
1396                         s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta));
1397                     }            
1398                     
1399                     if (s.getSnapped()) {
1400                         s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));                            
1401                         if (!s.isOtherSnapBetter(best, true)) {
1402                                 best = s;
1403                                 best_pt = from_2geom(s.getPoint()) - n->pos;
1404                         }
1405                     }
1406             }
1407         }
1408                         
1409         if (best.getSnapped()) {
1410             nodepath->desktop->snapindicator->set_new_snaptarget(best);
1411         } else {
1412             nodepath->desktop->snapindicator->remove_snaptarget();    
1413         }
1414     }
1416     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1417         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1418         sp_node_moveto(n, n->pos + best_pt);
1419     }
1421     // do not update repr here so that node dragging is acceptably fast
1422     update_object(nodepath);
1425 /**
1426 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1427 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1428 near x = 0.
1429  */
1430 double
1431 sculpt_profile (double x, double alpha, guint profile)
1433     double result = 1;
1435     if (x >= 1) {
1436         result = 0;
1437     } else if (x <= 0) {
1438         result = 1;
1439     } else {
1440         switch (profile) {
1441             case SCULPT_PROFILE_LINEAR:
1442                 result = 1 - x;
1443                 break;
1444             case SCULPT_PROFILE_BELL:
1445                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1446                 break;
1447             case SCULPT_PROFILE_ELLIPTIC:
1448                 result = sqrt(1 - x*x);
1449                 break;
1450             default:
1451                 g_assert_not_reached();
1452         }
1453     }
1455     return result;
1458 double
1459 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1461     // extremely primitive for now, don't have time to look for the real one
1462     double lower = Geom::L2(b - a);
1463     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1464     return (lower + upper)/2;
1467 void
1468 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1470     n->pos = n->origin + delta;
1471     n->n.pos = n->n.origin + delta_n;
1472     n->p.pos = n->p.origin + delta_p;
1473     sp_node_adjust_handles(n);
1474     sp_node_update_handles(n, false);
1477 /**
1478  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1479  * on how far they are from the dragged node n.
1480  */
1481 static void
1482 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1484     g_assert (n);
1485     g_assert (nodepath);
1486     g_assert (n->subpath->nodepath == nodepath);
1488     double pressure = n->knot->pressure;
1489     if (pressure == 0)
1490         pressure = 0.5; // default
1491     pressure = CLAMP (pressure, 0.2, 0.8);
1493     // map pressure to alpha = 1/5 ... 5
1494     double alpha = 1 - 2 * fabs(pressure - 0.5);
1495     if (pressure > 0.5)
1496         alpha = 1/alpha;
1498     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1499     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1501     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1502         // Only one subpath has selected nodes:
1503         // use linear mode, where the distance from n to node being dragged is calculated along the path
1505         double n_sel_range = 0, p_sel_range = 0;
1506         guint n_nodes = 0, p_nodes = 0;
1507         guint n_sel_nodes = 0, p_sel_nodes = 0;
1509         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1510         {
1511             double n_range = 0, p_range = 0;
1512             bool n_going = true, p_going = true;
1513             Inkscape::NodePath::Node *n_node = n;
1514             Inkscape::NodePath::Node *p_node = n;
1515             do {
1516                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1517                 if (n_node && n_going)
1518                     n_node = n_node->n.other;
1519                 if (n_node == NULL) {
1520                     n_going = false;
1521                 } else {
1522                     n_nodes ++;
1523                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1524                     if (n_node->selected) {
1525                         n_sel_nodes ++;
1526                         n_sel_range = n_range;
1527                     }
1528                     if (n_node == p_node) {
1529                         n_going = false;
1530                         p_going = false;
1531                     }
1532                 }
1533                 if (p_node && p_going)
1534                     p_node = p_node->p.other;
1535                 if (p_node == NULL) {
1536                     p_going = false;
1537                 } else {
1538                     p_nodes ++;
1539                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1540                     if (p_node->selected) {
1541                         p_sel_nodes ++;
1542                         p_sel_range = p_range;
1543                     }
1544                     if (p_node == n_node) {
1545                         n_going = false;
1546                         p_going = false;
1547                     }
1548                 }
1549             } while (n_going || p_going);
1550         }
1552         // Second pass: actually move nodes in this subpath
1553         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1554         {
1555             double n_range = 0, p_range = 0;
1556             bool n_going = true, p_going = true;
1557             Inkscape::NodePath::Node *n_node = n;
1558             Inkscape::NodePath::Node *p_node = n;
1559             do {
1560                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1561                 if (n_node && n_going)
1562                     n_node = n_node->n.other;
1563                 if (n_node == NULL) {
1564                     n_going = false;
1565                 } else {
1566                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1567                     if (n_node->selected) {
1568                         sp_nodepath_move_node_and_handles (n_node,
1569                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1570                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1571                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1572                     }
1573                     if (n_node == p_node) {
1574                         n_going = false;
1575                         p_going = false;
1576                     }
1577                 }
1578                 if (p_node && p_going)
1579                     p_node = p_node->p.other;
1580                 if (p_node == NULL) {
1581                     p_going = false;
1582                 } else {
1583                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1584                     if (p_node->selected) {
1585                         sp_nodepath_move_node_and_handles (p_node,
1586                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1587                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1588                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1589                     }
1590                     if (p_node == n_node) {
1591                         n_going = false;
1592                         p_going = false;
1593                     }
1594                 }
1595             } while (n_going || p_going);
1596         }
1598     } else {
1599         // Multiple subpaths have selected nodes:
1600         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1601         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1602         // fix the pear-like shape when sculpting e.g. a ring
1604         // First pass: calculate range
1605         gdouble direct_range = 0;
1606         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1607             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1608             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1609                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1610                 if (node->selected) {
1611                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1612                 }
1613             }
1614         }
1616         // Second pass: actually move nodes
1617         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1618             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1619             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1620                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1621                 if (node->selected) {
1622                     if (direct_range > 1e-6) {
1623                         sp_nodepath_move_node_and_handles (node,
1624                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1625                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1626                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1627                     } else {
1628                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1629                     }
1631                 }
1632             }
1633         }
1634     }
1636     // do not update repr here so that node dragging is acceptably fast
1637     update_object(nodepath);
1641 /**
1642  * Move node selection to point, adjust its and neighbouring handles,
1643  * handle possible snapping, and commit the change with possible undo.
1644  */
1645 void
1646 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1648     if (!nodepath) return;
1650     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1652     if (dx == 0) {
1653         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1654     } else if (dy == 0) {
1655         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1656     } else {
1657         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1658     }
1661 /**
1662  * Move node selection off screen and commit the change.
1663  */
1664 void
1665 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1667     // borrowed from sp_selection_move_screen in selection-chemistry.c
1668     // we find out the current zoom factor and divide deltas by it
1670     gdouble zoom = desktop->current_zoom();
1671     gdouble zdx = dx / zoom;
1672     gdouble zdy = dy / zoom;
1674     if (!nodepath) return;
1676     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1678     if (dx == 0) {
1679         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1680     } else if (dy == 0) {
1681         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1682     } else {
1683         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1684     }
1687 /**
1688  * Move selected nodes to the absolute position given
1689  */
1690 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1692     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1693         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1694         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1695         sp_node_moveto(n, npos);
1696     }
1698     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1701 /**
1702  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1703  */
1704 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1706     boost::optional<Geom::Coord> no_coord;
1707     g_return_val_if_fail(nodepath->selected, no_coord);
1709     // determine coordinate of first selected node
1710     GList *nsel = nodepath->selected;
1711     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1712     Geom::Coord coord = n->pos[axis];
1713     bool coincide = true;
1715     // compare it to the coordinates of all the other selected nodes
1716     for (GList *l = nsel->next; l != NULL; l = l->next) {
1717         n = (Inkscape::NodePath::Node *) l->data;
1718         if (n->pos[axis] != coord) {
1719             coincide = false;
1720         }
1721     }
1722     if (coincide) {
1723         return coord;
1724     } else {
1725         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1726         // currently we return the coordinate of the bounding box midpoint because I don't know how
1727         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1728         return bbox.midpoint()[axis];
1729     }
1732 /** If they don't yet exist, creates knot and line for the given side of the node */
1733 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1735     if (!side->knot) {
1736         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"));
1738         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1739         side->knot->setSize (7);
1740         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1741         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1742         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1743         sp_knot_update_ctrl(side->knot);
1745         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1746         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1747         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1748         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1749         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1750         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1751     }
1753     if (!side->line) {
1754         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1755                                         SP_TYPE_CTRLLINE, NULL);
1756     }
1759 /**
1760  * Ensure the given handle of the node is visible/invisible, update its screen position
1761  */
1762 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1764     g_assert(node != NULL);
1766    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1767     NRPathcode code = sp_node_path_code_from_side(node, side);
1769     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1771     if (show_handle) {
1772         if (!side->knot) { // No handle knot at all
1773             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1774             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1775             side->knot->pos = side->pos;
1776             if (side->knot->item)
1777                 SP_CTRL(side->knot->item)->moveto(side->pos);
1778             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1779             sp_knot_show(side->knot);
1780         } else {
1781             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1782                 if (fire_move_signals) {
1783                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1784                 } else {
1785                     sp_knot_moveto(side->knot, side->pos);
1786                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1787                 }
1788             }
1789             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1790                 sp_knot_show(side->knot);
1791             }
1792         }
1793         sp_canvas_item_show(side->line);
1794     } else {
1795         if (side->knot) {
1796             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1797                 sp_knot_hide(side->knot);
1798             }
1799         }
1800         if (side->line) {
1801             sp_canvas_item_hide(side->line);
1802         }
1803     }
1806 /**
1807  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1808  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1809  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1810  * updated; otherwise, just move the knots silently (used in batch moves).
1811  */
1812 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1814     g_assert(node != NULL);
1816     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1817         sp_knot_show(node->knot);
1818     }
1820     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1821         if (fire_move_signals)
1822             sp_knot_set_position(node->knot, node->pos, 0);
1823         else
1824             sp_knot_moveto(node->knot, node->pos);
1825     }
1827     gboolean show_handles = node->selected;
1828     if (node->p.other != NULL) {
1829         if (node->p.other->selected) show_handles = TRUE;
1830     }
1831     if (node->n.other != NULL) {
1832         if (node->n.other->selected) show_handles = TRUE;
1833     }
1835     if (node->subpath->nodepath->show_handles == false)
1836         show_handles = FALSE;
1838     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1839     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1842 /**
1843  * Call sp_node_update_handles() for all nodes on subpath.
1844  */
1845 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1847     g_assert(subpath != NULL);
1849     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1850         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1851     }
1854 /**
1855  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1856  */
1857 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1859     g_assert(nodepath != NULL);
1861     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1862         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1863     }
1866 void
1867 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1869     if (nodepath) {
1870         nodepath->show_handles = show;
1871         sp_nodepath_update_handles(nodepath);
1872     }
1875 /**
1876  * Adds all selected nodes in nodepath to list.
1877  */
1878 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1880     StlConv<Node *>::list(l, selected);
1881 /// \todo this adds a copying, rework when the selection becomes a stl list
1884 /**
1885  * Align selected nodes on the specified axis.
1886  */
1887 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1889     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1890         return;
1891     }
1893     if ( !nodepath->selected->next ) { // only one node selected
1894         return;
1895     }
1896    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1897     Geom::Point dest(pNode->pos);
1898     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1899         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1900         if (pNode) {
1901             dest[axis] = pNode->pos[axis];
1902             sp_node_moveto(pNode, dest);
1903         }
1904     }
1906     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1909 /// Helper struct.
1910 struct NodeSort
1912    Inkscape::NodePath::Node *_node;
1913     Geom::Coord _coord;
1914     /// \todo use vectorof pointers instead of calling copy ctor
1915     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1916         _node(node), _coord(node->pos[axis])
1917     {}
1919 };
1921 static bool operator<(NodeSort const &a, NodeSort const &b)
1923     return (a._coord < b._coord);
1926 /**
1927  * Distribute selected nodes on the specified axis.
1928  */
1929 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1931     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1932         return;
1933     }
1935     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1936         return;
1937     }
1939    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1940     std::vector<NodeSort> sorted;
1941     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1942         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1943         if (pNode) {
1944             NodeSort n(pNode, axis);
1945             sorted.push_back(n);
1946             //dest[axis] = pNode->pos[axis];
1947             //sp_node_moveto(pNode, dest);
1948         }
1949     }
1950     std::sort(sorted.begin(), sorted.end());
1951     unsigned int len = sorted.size();
1952     //overall bboxes span
1953     float dist = (sorted.back()._coord -
1954                   sorted.front()._coord);
1955     //new distance between each bbox
1956     float step = (dist) / (len - 1);
1957     float pos = sorted.front()._coord;
1958     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1959           it < sorted.end();
1960           it ++ )
1961     {
1962         Geom::Point dest((*it)._node->pos);
1963         dest[axis] = pos;
1964         sp_node_moveto((*it)._node, dest);
1965         pos += step;
1966     }
1968     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1972 /**
1973  * Call sp_nodepath_line_add_node() for all selected segments.
1974  */
1975 void
1976 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1978     if (!nodepath) {
1979         return;
1980     }
1982     GList *nl = NULL;
1984     int n_added = 0;
1986     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1987        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1988         g_assert(t->selected);
1989         if (t->p.other && t->p.other->selected) {
1990             nl = g_list_prepend(nl, t);
1991         }
1992     }
1994     while (nl) {
1995        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1996        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1997        sp_nodepath_node_select(n, TRUE, FALSE);
1998        n_added ++;
1999        nl = g_list_remove(nl, t);
2000     }
2002     /** \todo fixme: adjust ? */
2003     sp_nodepath_update_handles(nodepath);
2005     if (n_added > 1) {
2006         sp_nodepath_update_repr(nodepath, _("Add nodes"));
2007     } else if (n_added > 0) {
2008         sp_nodepath_update_repr(nodepath, _("Add node"));
2009     }
2011     sp_nodepath_update_statusbar(nodepath);
2014 /**
2015  * Select segment nearest to point
2016  */
2017 void
2018 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
2020     if (!nodepath) {
2021         return;
2022     }
2024     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2025     Geom::PathVector const &pathv = curve->get_pathvector();
2026     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2027     if (!pvpos) {
2028         g_print ("Possible error?\n");
2029         return;
2030     }
2032     // calculate index for nodepath's representation.
2033     unsigned int segment_index = floor(pvpos->t) + 1;
2034     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2035         segment_index += pathv[i].size() + 1;
2036         if (pathv[i].closed()) {
2037             segment_index += 1;
2038         }
2039     }
2041     curve->unref();
2043     //find segment to segment
2044     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2046     //fixme: this can return NULL, so check before proceeding.
2047     g_return_if_fail(e != NULL);
2049     gboolean force = FALSE;
2050     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2051         force = TRUE;
2052     }
2053     sp_nodepath_node_select(e, (gboolean) toggle, force);
2054     if (e->p.other)
2055         sp_nodepath_node_select(e->p.other, TRUE, force);
2057     sp_nodepath_update_handles(nodepath);
2059     sp_nodepath_update_statusbar(nodepath);
2062 /**
2063  * Add a node nearest to point
2064  */
2065 void
2066 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2068     if (!nodepath) {
2069         return;
2070     }
2072     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2073     Geom::PathVector const &pathv = curve->get_pathvector();
2074     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2075     if (!pvpos) {
2076         g_print ("Possible error?\n");
2077         return;
2078     }
2080     // calculate index for nodepath's representation.
2081     double int_part;
2082     double t = std::modf(pvpos->t, &int_part);
2083     unsigned int segment_index = (unsigned int)int_part + 1;
2084     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2085         segment_index += pathv[i].size() + 1;
2086         if (pathv[i].closed()) {
2087             segment_index += 1;
2088         }
2089     }
2091     curve->unref();
2093     //find segment to split
2094     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2095     if (!e) {
2096         return;
2097     }
2099     //don't know why but t seems to flip for lines
2100     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2101         t = 1.0 - t;
2102     }
2104     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2105     sp_nodepath_node_select(n, FALSE, TRUE);
2107     /* fixme: adjust ? */
2108     sp_nodepath_update_handles(nodepath);
2110     sp_nodepath_update_repr(nodepath, _("Add node"));
2112     sp_nodepath_update_statusbar(nodepath);
2115 /*
2116  * Adjusts a segment so that t moves by a certain delta for dragging
2117  * converts lines to curves
2118  *
2119  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2120  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2121  */
2122 void
2123 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2125     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2127     //fixme: e and e->p can be NULL, so check for those before proceeding
2128     g_return_if_fail(e != NULL);
2129     g_return_if_fail(&e->p != NULL);
2131     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2132         e->type = Inkscape::NodePath::NODE_SMOOTH;
2133         sp_nodepath_update_node_knot (e);
2134     }
2135     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2136         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2137         sp_nodepath_update_node_knot (e->p.other);
2138     }
2140     /* feel good is an arbitrary parameter that distributes the delta between handles
2141      * if t of the drag point is less than 1/6 distance form the endpoint only
2142      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2143      */
2144     double feel_good;
2145     if (t <= 1.0 / 6.0)
2146         feel_good = 0;
2147     else if (t <= 0.5)
2148         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2149     else if (t <= 5.0 / 6.0)
2150         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2151     else
2152         feel_good = 1;
2154     //if we're dragging a line convert it to a curve
2155     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2156         sp_nodepath_set_line_type(e, NR_CURVETO);
2157     }
2159     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2160     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2161     e->p.other->n.pos += offsetcoord0;
2162     e->p.pos += offsetcoord1;
2164     // adjust handles of adjacent nodes where necessary
2165     sp_node_adjust_handle(e,1);
2166     sp_node_adjust_handle(e->p.other,-1);
2168     sp_nodepath_update_handles(e->subpath->nodepath);
2170     update_object(e->subpath->nodepath);
2172     sp_nodepath_update_statusbar(e->subpath->nodepath);
2176 /**
2177  * Call sp_nodepath_break() for all selected segments.
2178  */
2179 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2181     if (!nodepath) return;
2183     GList *tempin = g_list_copy(nodepath->selected);
2184     GList *temp = NULL;
2185     for (GList *l = tempin; l != NULL; l = l->next) {
2186        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2187        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2188         if (nn == NULL) continue; // no break, no new node
2189         temp = g_list_prepend(temp, nn);
2190     }
2191     g_list_free(tempin);
2193     if (temp) {
2194         sp_nodepath_deselect(nodepath);
2195     }
2196     for (GList *l = temp; l != NULL; l = l->next) {
2197         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2198     }
2200     sp_nodepath_update_handles(nodepath);
2202     sp_nodepath_update_repr(nodepath, _("Break path"));
2205 /**
2206  * Duplicate the selected node(s).
2207  */
2208 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2210     if (!nodepath) {
2211         return;
2212     }
2214     GList *temp = NULL;
2215     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2216        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2217        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2218         if (nn == NULL) continue; // could not duplicate
2219         temp = g_list_prepend(temp, nn);
2220     }
2222     if (temp) {
2223         sp_nodepath_deselect(nodepath);
2224     }
2225     for (GList *l = temp; l != NULL; l = l->next) {
2226         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2227     }
2229     sp_nodepath_update_handles(nodepath);
2231     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2234 /**
2235  *  Internal function to join two nodes by merging them into one.
2236  */
2237 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2239     /* a and b are endpoints */
2241     // if one of the two nodes is mouseovered, fix its position
2242     Geom::Point c;
2243     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2244         c = a->pos;
2245     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2246         c = b->pos;
2247     } else {
2248         // otherwise, move joined node to the midpoint
2249         c = (a->pos + b->pos) / 2;
2250     }
2252     if (a->subpath == b->subpath) {
2253        Inkscape::NodePath::SubPath *sp = a->subpath;
2254         sp_nodepath_subpath_close(sp);
2255         sp_node_moveto (sp->first, c);
2257         sp_nodepath_update_handles(sp->nodepath);
2258         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2259         return;
2260     }
2262     /* a and b are separate subpaths */
2263     Inkscape::NodePath::SubPath *sa = a->subpath;
2264     Inkscape::NodePath::SubPath *sb = b->subpath;
2265     Geom::Point p;
2266     Inkscape::NodePath::Node *n;
2267     NRPathcode code;
2268     if (a == sa->first) {
2269         // we will now reverse sa, so that a is its last node, not first, and drop that node
2270         p = sa->first->n.pos;
2271         code = (NRPathcode)sa->first->n.other->code;
2272         // create new subpath
2273        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2274        // create a first moveto node on it
2275         n = sa->last;
2276         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2277         n = n->p.other;
2278         if (n == sa->first) n = NULL;
2279         while (n) {
2280             // copy the rest of the nodes from sa to t, going backwards
2281             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2282             n = n->p.other;
2283             if (n == sa->first) n = NULL;
2284         }
2285         // replace sa with t
2286         sp_nodepath_subpath_destroy(sa);
2287         sa = t;
2288     } else if (a == sa->last) {
2289         // a is already last, just drop it
2290         p = sa->last->p.pos;
2291         code = (NRPathcode)sa->last->code;
2292         sp_nodepath_node_destroy(sa->last);
2293     } else {
2294         code = NR_END;
2295         g_assert_not_reached();
2296     }
2298     if (b == sb->first) {
2299         // copy all nodes from b to a, forward 
2300         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2301         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2302             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2303         }
2304     } else if (b == sb->last) {
2305         // copy all nodes from b to a, backward 
2306         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2307         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2308             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2309         }
2310     } else {
2311         g_assert_not_reached();
2312     }
2313     /* and now destroy sb */
2315     sp_nodepath_subpath_destroy(sb);
2317     sp_nodepath_update_handles(sa->nodepath);
2319     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2321     sp_nodepath_update_statusbar(nodepath);
2324 /**
2325  *  Internal function to join two nodes by adding a segment between them.
2326  */
2327 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2329     if (a->subpath == b->subpath) {
2330        Inkscape::NodePath::SubPath *sp = a->subpath;
2332         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2333         sp->closed = TRUE;
2335         sp->first->p.other = sp->last;
2336         sp->last->n.other  = sp->first;
2338         sp_node_handle_mirror_p_to_n(sp->last);
2339         sp_node_handle_mirror_n_to_p(sp->first);
2341         sp->first->code = sp->last->code;
2342         sp->first       = sp->last;
2344         sp_nodepath_update_handles(sp->nodepath);
2346         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2348         return;
2349     }
2351     /* a and b are separate subpaths */
2352     Inkscape::NodePath::SubPath *sa = a->subpath;
2353     Inkscape::NodePath::SubPath *sb = b->subpath;
2355     Inkscape::NodePath::Node *n;
2356     Geom::Point p;
2357     NRPathcode code;
2358     if (a == sa->first) {
2359         code = (NRPathcode) sa->first->n.other->code;
2360        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2361         n = sa->last;
2362         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2363         for (n = n->p.other; n != NULL; n = n->p.other) {
2364             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2365         }
2366         sp_nodepath_subpath_destroy(sa);
2367         sa = t;
2368     } else if (a == sa->last) {
2369         code = (NRPathcode)sa->last->code;
2370     } else {
2371         code = NR_END;
2372         g_assert_not_reached();
2373     }
2375     if (b == sb->first) {
2376         n = sb->first;
2377         sp_node_handle_mirror_p_to_n(sa->last);
2378         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2379         sp_node_handle_mirror_n_to_p(sa->last);
2380         for (n = n->n.other; n != NULL; n = n->n.other) {
2381             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2382         }
2383     } else if (b == sb->last) {
2384         n = sb->last;
2385         sp_node_handle_mirror_p_to_n(sa->last);
2386         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2387         sp_node_handle_mirror_n_to_p(sa->last);
2388         for (n = n->p.other; n != NULL; n = n->p.other) {
2389             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2390         }
2391     } else {
2392         g_assert_not_reached();
2393     }
2394     /* and now destroy sb */
2396     sp_nodepath_subpath_destroy(sb);
2398     sp_nodepath_update_handles(sa->nodepath);
2400     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2403 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2405 /**
2406  * Internal function to handle joining two nodes.
2407  */
2408 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2410     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2412     if (g_list_length(nodepath->selected) != 2) {
2413         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2414         return;
2415     }
2417     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2418     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2420     g_assert(a != b);
2421     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2422         // someone tried to join an orphan node (i.e. a single-node subpath).
2423         // this is not worth an error message, just fail silently.
2424         return;
2425     }
2427     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2428         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2429         return;
2430     }
2432     switch(mode) {
2433         case NODE_JOIN_ENDPOINTS:
2434             do_node_selected_join(nodepath, a, b);
2435             break;
2436         case NODE_JOIN_SEGMENT:
2437             do_node_selected_join_segment(nodepath, a, b);
2438             break;
2439     }
2442 /**
2443  *  Join two nodes by merging them into one.
2444  */
2445 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2447     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2450 /**
2451  *  Join two nodes by adding a segment between them.
2452  */
2453 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2455     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2458 /**
2459  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2460  */
2461 void sp_node_delete_preserve(GList *nodes_to_delete)
2463     GSList *nodepaths = NULL;
2465     while (nodes_to_delete) {
2466         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2467         Inkscape::NodePath::SubPath *sp = node->subpath;
2468         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2469         Inkscape::NodePath::Node *sample_cursor = NULL;
2470         Inkscape::NodePath::Node *sample_end = NULL;
2471         Inkscape::NodePath::Node *delete_cursor = node;
2472         bool just_delete = false;
2474         //find the start of this contiguous selection
2475         //move left to the first node that is not selected
2476         //or the start of the non-closed path
2477         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2478             delete_cursor = curr;
2479         }
2481         //just delete at the beginning of an open path
2482         if (!delete_cursor->p.other) {
2483             sample_cursor = delete_cursor;
2484             just_delete = true;
2485         } else {
2486             sample_cursor = delete_cursor->p.other;
2487         }
2489         //calculate points for each segment
2490         int rate = 5;
2491         float period = 1.0 / rate;
2492         std::vector<Geom::Point> data;
2493         if (!just_delete) {
2494             data.push_back(sample_cursor->pos);
2495             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2496                 //just delete at the end of an open path
2497                 if (!sp->closed && curr == sp->last) {
2498                     just_delete = true;
2499                     break;
2500                 }
2502                 //sample points on the contiguous selected segment
2503                 Geom::Point *bez;
2504                 bez = new Geom::Point [4];
2505                 bez[0] = curr->pos;
2506                 bez[1] = curr->n.pos;
2507                 bez[2] = curr->n.other->p.pos;
2508                 bez[3] = curr->n.other->pos;
2509                 for (int i=1; i<rate; i++) {
2510                     gdouble t = i * period;
2511                     Geom::Point p = bezier_pt(3, bez, t);
2512                     data.push_back(p);
2513                 }
2514                 data.push_back(curr->n.other->pos);
2516                 sample_end = curr->n.other;
2517                 //break if we've come full circle or hit the end of the selection
2518                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2519                     break;
2520                 }
2521             }
2522         }
2524         if (!just_delete) {
2525             //calculate the best fitting single segment and adjust the endpoints
2526             Geom::Point *adata;
2527             adata = new Geom::Point [data.size()];
2528             copy(data.begin(), data.end(), adata);
2530             Geom::Point *bez;
2531             bez = new Geom::Point [4];
2532             //would decreasing error create a better fitting approximation?
2533             gdouble error = 1.0;
2534             gint ret;
2535             ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error);
2537             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2538             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2539             //the resulting nodes behave as expected.
2540             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2541                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2542             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2543                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2545             //adjust endpoints
2546             sample_cursor->n.pos = bez[1];
2547             sample_end->p.pos = bez[2];
2548         }
2550         //destroy this contiguous selection
2551         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2552             Inkscape::NodePath::Node *temp = delete_cursor;
2553             if (delete_cursor->n.other == delete_cursor) {
2554                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2555                 delete_cursor = NULL;
2556             } else {
2557                 delete_cursor = delete_cursor->n.other;
2558             }
2559             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2560             sp_nodepath_node_destroy(temp);
2561         }
2563         sp_nodepath_update_handles(nodepath);
2565         if (!g_slist_find(nodepaths, nodepath))
2566             nodepaths = g_slist_prepend (nodepaths, nodepath);
2567     }
2569     for (GSList *i = nodepaths; i; i = i->next) {
2570         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2571         // different nodepaths will give us one undo event per nodepath
2572         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2574         // if the entire nodepath is removed, delete the selected object.
2575         if (nodepath->subpaths == NULL ||
2576             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2577             //at least 2
2578             sp_nodepath_get_node_count(nodepath) < 2) {
2579             SPDocument *document = sp_desktop_document (nodepath->desktop);
2580             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2581             //delete this nodepath's object, not the entire selection! (though at this time, this
2582             //does not matter)
2583             sp_selection_delete(nodepath->desktop);
2584             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2585                               _("Delete nodes"));
2586         } else {
2587             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2588             sp_nodepath_update_statusbar(nodepath);
2589         }
2590     }
2592     g_slist_free (nodepaths);
2595 /**
2596  * Delete one or more selected nodes.
2597  */
2598 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2600     if (!nodepath) return;
2601     if (!nodepath->selected) return;
2603     /** \todo fixme: do it the right way */
2604     while (nodepath->selected) {
2605        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2606         sp_nodepath_node_destroy(node);
2607     }
2610     //clean up the nodepath (such as for trivial subpaths)
2611     sp_nodepath_cleanup(nodepath);
2613     sp_nodepath_update_handles(nodepath);
2615     // if the entire nodepath is removed, delete the selected object.
2616     if (nodepath->subpaths == NULL ||
2617         sp_nodepath_get_node_count(nodepath) < 2) {
2618         SPDocument *document = sp_desktop_document (nodepath->desktop);
2619         sp_selection_delete(nodepath->desktop);
2620         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2621                           _("Delete nodes"));
2622         return;
2623     }
2625     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2627     sp_nodepath_update_statusbar(nodepath);
2630 /**
2631  * Delete one or more segments between two selected nodes.
2632  * This is the code for 'split'.
2633  */
2634 void
2635 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2637    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2638    Inkscape::NodePath::Node *curr, *next;     //Iterators
2640     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2642     if (g_list_length(nodepath->selected) != 2) {
2643         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2644                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2645         return;
2646     }
2648     //Selected nodes, not inclusive
2649    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2650    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2652     if ( ( a==b)                       ||  //same node
2653          (a->subpath  != b->subpath )  ||  //not the same path
2654          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2655          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2656     {
2657         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2658                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2659         return;
2660     }
2662     //###########################################
2663     //# BEGIN EDITS
2664     //###########################################
2665     //##################################
2666     //# CLOSED PATH
2667     //##################################
2668     if (a->subpath->closed) {
2671         gboolean reversed = FALSE;
2673         //Since we can go in a circle, we need to find the shorter distance.
2674         //  a->b or b->a
2675         start = end = NULL;
2676         int distance    = 0;
2677         int minDistance = 0;
2678         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2679             if (curr==b) {
2680                 //printf("a to b:%d\n", distance);
2681                 start = a;//go from a to b
2682                 end   = b;
2683                 minDistance = distance;
2684                 //printf("A to B :\n");
2685                 break;
2686             }
2687             distance++;
2688         }
2690         //try again, the other direction
2691         distance = 0;
2692         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2693             if (curr==a) {
2694                 //printf("b to a:%d\n", distance);
2695                 if (distance < minDistance) {
2696                     start    = b;  //we go from b to a
2697                     end      = a;
2698                     reversed = TRUE;
2699                     //printf("B to A\n");
2700                 }
2701                 break;
2702             }
2703             distance++;
2704         }
2707         //Copy everything from 'end' to 'start' to a new subpath
2708        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2709         for (curr=end ; curr ; curr=curr->n.other) {
2710             NRPathcode code = (NRPathcode) curr->code;
2711             if (curr == end)
2712                 code = NR_MOVETO;
2713             sp_nodepath_node_new(t, NULL,
2714                                  (Inkscape::NodePath::NodeType)curr->type, code,
2715                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2716             if (curr == start)
2717                 break;
2718         }
2719         sp_nodepath_subpath_destroy(a->subpath);
2722     }
2726     //##################################
2727     //# OPEN PATH
2728     //##################################
2729     else {
2731         //We need to get the direction of the list between A and B
2732         //Can we walk from a to b?
2733         start = end = NULL;
2734         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2735             if (curr==b) {
2736                 start = a;  //did it!  we go from a to b
2737                 end   = b;
2738                 //printf("A to B\n");
2739                 break;
2740             }
2741         }
2742         if (!start) {//didn't work?  let's try the other direction
2743             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2744                 if (curr==a) {
2745                     start = b;  //did it!  we go from b to a
2746                     end   = a;
2747                     //printf("B to A\n");
2748                     break;
2749                 }
2750             }
2751         }
2752         if (!start) {
2753             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2754                                                      _("Cannot find path between nodes."));
2755             return;
2756         }
2760         //Copy everything after 'end' to a new subpath
2761        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2762         for (curr=end ; curr ; curr=curr->n.other) {
2763             NRPathcode code = (NRPathcode) curr->code;
2764             if (curr == end)
2765                 code = NR_MOVETO;
2766             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2767                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2768         }
2770         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2771         for (curr = start->n.other ; curr  ; curr=next) {
2772             next = curr->n.other;
2773             sp_nodepath_node_destroy(curr);
2774         }
2776     }
2777     //###########################################
2778     //# END EDITS
2779     //###########################################
2781     //clean up the nodepath (such as for trivial subpaths)
2782     sp_nodepath_cleanup(nodepath);
2784     sp_nodepath_update_handles(nodepath);
2786     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2788     sp_nodepath_update_statusbar(nodepath);
2791 /**
2792  * Call sp_nodepath_set_line() for all selected segments.
2793  */
2794 void
2795 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2797     if (nodepath == NULL) return;
2799     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2800        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2801         g_assert(n->selected);
2802         if (n->p.other && n->p.other->selected) {
2803             sp_nodepath_set_line_type(n, code);
2804         }
2805     }
2807     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2810 /**
2811  * Call sp_nodepath_convert_node_type() for all selected nodes.
2812  */
2813 void
2814 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2816     if (nodepath == NULL) return;
2818     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2820     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2821         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2822     }
2824     sp_nodepath_update_repr(nodepath, _("Change node type"));
2827 /**
2828  * Change select status of node, update its own and neighbour handles.
2829  */
2830 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2832     node->selected = selected;
2834     if (selected) {
2835         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2836         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2837         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2838         sp_knot_update_ctrl(node->knot);
2839     } else {
2840         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2841         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2842         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2843         sp_knot_update_ctrl(node->knot);
2844     }
2846     sp_node_update_handles(node);
2847     if (node->n.other) sp_node_update_handles(node->n.other);
2848     if (node->p.other) sp_node_update_handles(node->p.other);
2851 /**
2852 \brief Select a node
2853 \param node     The node to select
2854 \param incremental   If true, add to selection, otherwise deselect others
2855 \param override   If true, always select this node, otherwise toggle selected status
2856 */
2857 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2859     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2861     if (incremental) {
2862         if (override) {
2863             if (!g_list_find(nodepath->selected, node)) {
2864                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2865             }
2866             sp_node_set_selected(node, TRUE);
2867         } else { // toggle
2868             if (node->selected) {
2869                 g_assert(g_list_find(nodepath->selected, node));
2870                 nodepath->selected = g_list_remove(nodepath->selected, node);
2871             } else {
2872                 g_assert(!g_list_find(nodepath->selected, node));
2873                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2874             }
2875             sp_node_set_selected(node, !node->selected);
2876         }
2877     } else {
2878         sp_nodepath_deselect(nodepath);
2879         nodepath->selected = g_list_prepend(nodepath->selected, node);
2880         sp_node_set_selected(node, TRUE);
2881     }
2883     sp_nodepath_update_statusbar(nodepath);
2887 /**
2888 \brief Deselect all nodes in the nodepath
2889 */
2890 void
2891 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2893     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2895     while (nodepath->selected) {
2896         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2897         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2898     }
2899     sp_nodepath_update_statusbar(nodepath);
2902 /**
2903 \brief Select or invert selection of all nodes in the nodepath
2904 */
2905 void
2906 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2908     if (!nodepath) return;
2910     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2911        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2912         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2913            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2914            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2915         }
2916     }
2919 /**
2920  * If nothing selected, does the same as sp_nodepath_select_all();
2921  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2922  * (i.e., similar to "select all in layer", with the "selected" subpaths
2923  * being treated as "layers" in the path).
2924  */
2925 void
2926 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2928     if (!nodepath) return;
2930     if (g_list_length (nodepath->selected) == 0) {
2931         sp_nodepath_select_all (nodepath, invert);
2932         return;
2933     }
2935     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2936     GSList *subpaths = NULL;
2938     for (GList *l = copy; l != NULL; l = l->next) {
2939         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2940         Inkscape::NodePath::SubPath *subpath = n->subpath;
2941         if (!g_slist_find (subpaths, subpath))
2942             subpaths = g_slist_prepend (subpaths, subpath);
2943     }
2945     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2946         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2947         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2948             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2949             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2950         }
2951     }
2953     g_slist_free (subpaths);
2954     g_list_free (copy);
2957 /**
2958  * \brief Select the node after the last selected; if none is selected,
2959  * select the first within path.
2960  */
2961 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2963     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2965    Inkscape::NodePath::Node *last = NULL;
2966     if (nodepath->selected) {
2967         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2968            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2969             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2970             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2971                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2972                 if (node->selected) {
2973                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2974                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2975                             if (spl->next) { // there's a next subpath
2976                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2977                                 last = subpath_next->first;
2978                             } else if (spl->prev) { // there's a previous subpath
2979                                 last = NULL; // to be set later to the first node of first subpath
2980                             } else {
2981                                 last = node->n.other;
2982                             }
2983                         } else {
2984                             last = node->n.other;
2985                         }
2986                     } else {
2987                         if (node->n.other) {
2988                             last = node->n.other;
2989                         } else {
2990                             if (spl->next) { // there's a next subpath
2991                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2992                                 last = subpath_next->first;
2993                             } else if (spl->prev) { // there's a previous subpath
2994                                 last = NULL; // to be set later to the first node of first subpath
2995                             } else {
2996                                 last = (Inkscape::NodePath::Node *) subpath->first;
2997                             }
2998                         }
2999                     }
3000                 }
3001             }
3002         }
3003         sp_nodepath_deselect(nodepath);
3004     }
3006     if (last) { // there's at least one more node after selected
3007         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3008     } else { // no more nodes, select the first one in first subpath
3009        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
3010         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
3011     }
3014 /**
3015  * \brief Select the node before the first selected; if none is selected,
3016  * select the last within path
3017  */
3018 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
3020     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
3022    Inkscape::NodePath::Node *last = NULL;
3023     if (nodepath->selected) {
3024         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
3025            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3026             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
3027                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3028                 if (node->selected) {
3029                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3030                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3031                             if (spl->prev) { // there's a prev subpath
3032                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3033                                 last = subpath_prev->last;
3034                             } else if (spl->next) { // there's a next subpath
3035                                 last = NULL; // to be set later to the last node of last subpath
3036                             } else {
3037                                 last = node->p.other;
3038                             }
3039                         } else {
3040                             last = node->p.other;
3041                         }
3042                     } else {
3043                         if (node->p.other) {
3044                             last = node->p.other;
3045                         } else {
3046                             if (spl->prev) { // there's a prev subpath
3047                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3048                                 last = subpath_prev->last;
3049                             } else if (spl->next) { // there's a next subpath
3050                                 last = NULL; // to be set later to the last node of last subpath
3051                             } else {
3052                                 last = (Inkscape::NodePath::Node *) subpath->last;
3053                             }
3054                         }
3055                     }
3056                 }
3057             }
3058         }
3059         sp_nodepath_deselect(nodepath);
3060     }
3062     if (last) { // there's at least one more node before selected
3063         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3064     } else { // no more nodes, select the last one in last subpath
3065         GList *spl = g_list_last(nodepath->subpaths);
3066        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3067         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3068     }
3071 /**
3072  * \brief Select all nodes that are within the rectangle.
3073  */
3074 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3076     if (!incremental) {
3077         sp_nodepath_deselect(nodepath);
3078     }
3080     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3081        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3082         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3083            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3085             if (b.contains(node->pos)) {
3086                 sp_nodepath_node_select(node, TRUE, TRUE);
3087             }
3088         }
3089     }
3093 void
3094 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3096     g_assert (n);
3097     g_assert (nodepath);
3098     g_assert (n->subpath->nodepath == nodepath);
3100     if (g_list_length (nodepath->selected) == 0) {
3101         if (grow > 0) {
3102             sp_nodepath_node_select(n, TRUE, TRUE);
3103         }
3104         return;
3105     }
3107     if (g_list_length (nodepath->selected) == 1) {
3108         if (grow < 0) {
3109             sp_nodepath_deselect (nodepath);
3110             return;
3111         }
3112     }
3114         double n_sel_range = 0, p_sel_range = 0;
3115             Inkscape::NodePath::Node *farthest_n_node = n;
3116             Inkscape::NodePath::Node *farthest_p_node = n;
3118         // Calculate ranges
3119         {
3120             double n_range = 0, p_range = 0;
3121             bool n_going = true, p_going = true;
3122             Inkscape::NodePath::Node *n_node = n;
3123             Inkscape::NodePath::Node *p_node = n;
3124             do {
3125                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3126                 if (n_node && n_going)
3127                     n_node = n_node->n.other;
3128                 if (n_node == NULL) {
3129                     n_going = false;
3130                 } else {
3131                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3132                     if (n_node->selected) {
3133                         n_sel_range = n_range;
3134                         farthest_n_node = n_node;
3135                     }
3136                     if (n_node == p_node) {
3137                         n_going = false;
3138                         p_going = false;
3139                     }
3140                 }
3141                 if (p_node && p_going)
3142                     p_node = p_node->p.other;
3143                 if (p_node == NULL) {
3144                     p_going = false;
3145                 } else {
3146                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3147                     if (p_node->selected) {
3148                         p_sel_range = p_range;
3149                         farthest_p_node = p_node;
3150                     }
3151                     if (p_node == n_node) {
3152                         n_going = false;
3153                         p_going = false;
3154                     }
3155                 }
3156             } while (n_going || p_going);
3157         }
3159     if (grow > 0) {
3160         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3161                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3162         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3163                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3164         }
3165     } else {
3166         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3167                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3168         } else if (farthest_p_node && farthest_p_node->selected) {
3169                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3170         }
3171     }
3174 void
3175 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3177     g_assert (n);
3178     g_assert (nodepath);
3179     g_assert (n->subpath->nodepath == nodepath);
3181     if (g_list_length (nodepath->selected) == 0) {
3182         if (grow > 0) {
3183             sp_nodepath_node_select(n, TRUE, TRUE);
3184         }
3185         return;
3186     }
3188     if (g_list_length (nodepath->selected) == 1) {
3189         if (grow < 0) {
3190             sp_nodepath_deselect (nodepath);
3191             return;
3192         }
3193     }
3195     Inkscape::NodePath::Node *farthest_selected = NULL;
3196     double farthest_dist = 0;
3198     Inkscape::NodePath::Node *closest_unselected = NULL;
3199     double closest_dist = NR_HUGE;
3201     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3202        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3203         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3204            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3205            if (node == n)
3206                continue;
3207            if (node->selected) {
3208                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3209                    farthest_dist = Geom::L2(node->pos - n->pos);
3210                    farthest_selected = node;
3211                }
3212            } else {
3213                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3214                    closest_dist = Geom::L2(node->pos - n->pos);
3215                    closest_unselected = node;
3216                }
3217            }
3218         }
3219     }
3221     if (grow > 0) {
3222         if (closest_unselected) {
3223             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3224         }
3225     } else {
3226         if (farthest_selected) {
3227             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3228         }
3229     }
3233 /**
3234 \brief  Saves all nodes' and handles' current positions in their origin members
3235 */
3236 void
3237 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3239     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3240        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3241         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3242            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3243            n->origin = n->pos;
3244            n->p.origin = n->p.pos;
3245            n->n.origin = n->n.pos;
3246         }
3247     }
3250 /**
3251 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3252 */
3253 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3255     GList *r = NULL;
3256     if (nodepath->selected) {
3257         guint i = 0;
3258         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3259             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3260             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3261                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3262                 i++;
3263                 if (node->selected) {
3264                     r = g_list_append(r, GINT_TO_POINTER(i));
3265                 }
3266             }
3267         }
3268     }
3269     return r;
3272 /**
3273 \brief  Restores selection by selecting nodes whose positions are in the list
3274 */
3275 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3277     sp_nodepath_deselect(nodepath);
3279     guint i = 0;
3280     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3281        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3282         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3283            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3284             i++;
3285             if (g_list_find(r, GINT_TO_POINTER(i))) {
3286                 sp_nodepath_node_select(node, TRUE, TRUE);
3287             }
3288         }
3289     }
3293 /**
3294 \brief Adjusts handle according to node type and line code.
3295 */
3296 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3298     g_assert(node);
3300     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3301     if (node->type == Inkscape::NodePath::NODE_AUTO)
3302         return;
3304    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3305    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3307    // nothing to do if we are an end node
3308     if (me->other == NULL) return;
3309     if (other->other == NULL) return;
3311     // nothing to do if we are a cusp node
3312     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3314     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3315     NRPathcode mecode;
3316     if (which_adjust == 1) {
3317         mecode = (NRPathcode)me->other->code;
3318     } else {
3319         mecode = (NRPathcode)node->code;
3320     }
3321     if (mecode == NR_LINETO) return;
3323     if (sp_node_side_is_line(node, other)) {
3324         // other is a line, and we are either smooth or symm
3325        Inkscape::NodePath::Node *othernode = other->other;
3326         double len = Geom::L2(me->pos - node->pos);
3327         Geom::Point delta = node->pos - othernode->pos;
3328         double linelen = Geom::L2(delta);
3329         if (linelen < 1e-18)
3330             return;
3331         me->pos = node->pos + (len / linelen)*delta;
3332         return;
3333     }
3335     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3336         // symmetrize 
3337         me->pos = 2 * node->pos - other->pos;
3338         return;
3339     } else {
3340         // smoothify
3341         double len = Geom::L2(me->pos - node->pos);
3342         Geom::Point delta = other->pos - node->pos;
3343         double otherlen = Geom::L2(delta);
3344         if (otherlen < 1e-18) return;
3345         me->pos = node->pos - (len / otherlen) * delta;
3346     }
3349 /**
3350  \brief Adjusts both handles according to node type and line code
3351  */
3352 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3354     g_assert(node);
3356     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3358     /* we are either smooth or symm */
3360     if (node->p.other == NULL) return;
3361     if (node->n.other == NULL) return;
3363     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3364         sp_node_adjust_handles_auto(node);
3365         return;
3366     }
3368     if (sp_node_side_is_line(node, &node->p)) {
3369         sp_node_adjust_handle(node, 1);
3370         return;
3371     }
3373     if (sp_node_side_is_line(node, &node->n)) {
3374         sp_node_adjust_handle(node, -1);
3375         return;
3376     }
3378     /* both are curves */
3379     Geom::Point const delta( node->n.pos - node->p.pos );
3381     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3382         node->p.pos = node->pos - delta / 2;
3383         node->n.pos = node->pos + delta / 2;
3384         return;
3385     }
3387     /* We are smooth */
3388     double plen = Geom::L2(node->p.pos - node->pos);
3389     if (plen < 1e-18) return;
3390     double nlen = Geom::L2(node->n.pos - node->pos);
3391     if (nlen < 1e-18) return;
3392     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3393     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3396 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3398     if (node->p.other == NULL || node->n.other == NULL) {
3399         node->p.pos = node->pos;
3400         node->n.pos = node->pos;
3401         return;
3402     }
3404     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3405     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3406  
3407     double norm_leg_prev = Geom::L2(leg_prev);
3408     double norm_leg_next = Geom::L2(leg_next);
3409  
3410     Geom::Point delta;
3411     if (norm_leg_next > 0.0) {
3412         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3413         delta.normalize();
3414     }
3415  
3416     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3417     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3420 /**
3421  * Node event callback.
3422  */
3423 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3425     gboolean ret = FALSE;
3426     switch (event->type) {
3427         case GDK_ENTER_NOTIFY:
3428             Inkscape::NodePath::Path::active_node = n;
3429             break;
3430         case GDK_LEAVE_NOTIFY:
3431             Inkscape::NodePath::Path::active_node = NULL;
3432             break;
3433         case GDK_SCROLL:
3434             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3435                 switch (event->scroll.direction) {
3436                     case GDK_SCROLL_UP:
3437                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3438                         break;
3439                     case GDK_SCROLL_DOWN:
3440                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3441                         break;
3442                     default:
3443                         break;
3444                 }
3445                 ret = TRUE;
3446             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3447                 switch (event->scroll.direction) {
3448                     case GDK_SCROLL_UP:
3449                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3450                         break;
3451                     case GDK_SCROLL_DOWN:
3452                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3453                         break;
3454                     default:
3455                         break;
3456                 }
3457                 ret = TRUE;
3458             }
3459             break;
3460         case GDK_KEY_PRESS:
3461             switch (get_group0_keyval (&event->key)) {
3462                 case GDK_space:
3463                     if (event->key.state & GDK_BUTTON1_MASK) {
3464                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3465                         stamp_repr(nodepath);
3466                         ret = TRUE;
3467                     }
3468                     break;
3469                 case GDK_Page_Up:
3470                     if (event->key.state & GDK_CONTROL_MASK) {
3471                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3472                     } else {
3473                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3474                     }
3475                     break;
3476                 case GDK_Page_Down:
3477                     if (event->key.state & GDK_CONTROL_MASK) {
3478                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3479                     } else {
3480                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3481                     }
3482                     break;
3483                 default:
3484                     break;
3485             }
3486             break;
3487         default:
3488             break;
3489     }
3491     return ret;
3494 /**
3495  * Handle keypress on node; directly called.
3496  */
3497 gboolean node_key(GdkEvent *event)
3499     Inkscape::NodePath::Path *np;
3501     // there is no way to verify nodes so set active_node to nil when deleting!!
3502     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3504     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3505         gint ret = FALSE;
3506         switch (get_group0_keyval (&event->key)) {
3507             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3508             case GDK_BackSpace:
3509                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3510                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3511                 sp_nodepath_update_repr(np, _("Delete node"));
3512                 Inkscape::NodePath::Path::active_node = NULL;
3513                 ret = TRUE;
3514                 break;
3515             case GDK_c:
3516                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3517                 ret = TRUE;
3518                 break;
3519             case GDK_s:
3520                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3521                 ret = TRUE;
3522                 break;
3523             case GDK_a:
3524                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3525                 ret = TRUE;
3526                 break;
3527             case GDK_y:
3528                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3529                 ret = TRUE;
3530                 break;
3531             case GDK_b:
3532                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3533                 ret = TRUE;
3534                 break;
3535         }
3536         return ret;
3537     }
3538     return FALSE;
3541 /**
3542  * Mouseclick on node callback.
3543  */
3544 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3546    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3548     if (state & GDK_CONTROL_MASK) {
3549         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3551         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3552             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3553                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3554             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3555                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3556             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3557                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3558             } else {
3559                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3560             }
3561             sp_nodepath_update_repr(nodepath, _("Change node type"));
3562             sp_nodepath_update_statusbar(nodepath);
3564         } else { //ctrl+alt+click: delete node
3565             GList *node_to_delete = NULL;
3566             node_to_delete = g_list_append(node_to_delete, n);
3567             sp_node_delete_preserve(node_to_delete);
3568         }
3570     } else {
3571         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3572     }
3575 /**
3576  * Mouse grabbed node callback.
3577  */
3578 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3580    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3582     if (!n->selected) {
3583         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3584     }
3586     n->is_dragging = true;
3587     // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping)
3588     n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin;  
3589     
3590     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3592     sp_nodepath_remember_origins (n->subpath->nodepath);
3595 /**
3596  * Mouse ungrabbed node callback.
3597  */
3598 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3600    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3602    n->dragging_out = NULL;
3603    n->is_dragging = false;
3604    n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
3605    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3607    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3610 /**
3611  * The point on a line, given by its angle, closest to the given point.
3612  * \param p  A point.
3613  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3614  * \param closest  Pointer to the point struct where the result is stored.
3615  * \todo FIXME: use dot product perhaps?
3616  */
3617 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3619     if (a == HUGE_VAL) { // vertical
3620         *closest = Geom::Point(0, (*p)[Geom::Y]);
3621     } else {
3622         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3623         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3624     }
3627 /**
3628  * Distance from the point to a line given by its angle.
3629  * \param p  A point.
3630  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3631  */
3632 static double point_line_distance(Geom::Point *p, double a)
3634     Geom::Point c;
3635     point_line_closest(p, a, &c);
3636     return sqrt(((*p)[Geom::X] - c[Geom::X])*((*p)[Geom::X] - c[Geom::X]) + ((*p)[Geom::Y] - c[Geom::Y])*((*p)[Geom::Y] - c[Geom::Y]));
3639 /**
3640  * Callback for node "request" signal.
3641  * \todo fixme: This goes to "moved" event? (lauris)
3642  */
3643 static gboolean
3644 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3646     double yn, xn, yp, xp;
3647     double an, ap, na, pa;
3648     double d_an, d_ap, d_na, d_pa;
3649     gboolean collinear = FALSE;
3650     Geom::Point c;
3651     Geom::Point pr;
3653     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3655     n->subpath->nodepath->desktop->snapindicator->remove_snaptarget();
3657     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3658     if ( (!n->subpath->nodepath->straight_path) &&
3659          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3660            || n->dragging_out ) )
3661     {
3662        Geom::Point mouse = p;
3664        if (!n->dragging_out) {
3665            // This is the first drag-out event; find out which handle to drag out
3666            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3667            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3669            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3670                return FALSE;
3672            Inkscape::NodePath::NodeSide *opposite;
3673            if (appr_p > appr_n) { // closer to p
3674                n->dragging_out = &n->p;
3675                opposite = &n->n;
3676                n->code = NR_CURVETO;
3677            } else if (appr_p < appr_n) { // closer to n
3678                n->dragging_out = &n->n;
3679                opposite = &n->p;
3680                n->n.other->code = NR_CURVETO;
3681            } else { // p and n nodes are the same
3682                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3683                    n->dragging_out = &n->p;
3684                    opposite = &n->n;
3685                    n->code = NR_CURVETO;
3686                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3687                    n->dragging_out = &n->n;
3688                    opposite = &n->p;
3689                    n->n.other->code = NR_CURVETO;
3690                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3691                    double appr_other_n = (n->n.other ? Geom::L2(n->n.other->n.pos - n->pos) - Geom::L2(n->n.other->n.pos - p) : -HUGE_VAL);
3692                    double appr_other_p = (n->n.other ? Geom::L2(n->n.other->p.pos - n->pos) - Geom::L2(n->n.other->p.pos - p) : -HUGE_VAL);
3693                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3694                        n->dragging_out = &n->n;
3695                        opposite = &n->p;
3696                        n->n.other->code = NR_CURVETO;
3697                    } else { // closer to other's n handle
3698                        n->dragging_out = &n->p;
3699                        opposite = &n->n;
3700                        n->code = NR_CURVETO;
3701                    }
3702                }
3703            }
3705            // if there's another handle, make sure the one we drag out starts parallel to it
3706            if (opposite->pos != n->pos) {
3707                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3708            }
3710            // knots might not be created yet!
3711            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3712            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3713        }
3715        // pass this on to the handle-moved callback
3716        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3717        sp_node_update_handles(n);
3718        return TRUE;
3719    }
3721     if (state & GDK_CONTROL_MASK) { // constrained motion
3723         // calculate relative distances of handles
3724         // n handle:
3725         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3726         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3727         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3728         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3729             if (n->n.other) { // if there is the next point
3730                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3731                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3732                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3733             }
3734         }
3735         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3736         if (yn < 0) { xn = -xn; yn = -yn; }
3738         // p handle:
3739         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3740         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3741         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3742         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3743             if (n->p.other) {
3744                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3745                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3746                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3747             }
3748         }
3749         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3750         if (yp < 0) { xp = -xp; yp = -yp; }
3752         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3753             // sliding on handles, only if at least one of the handles is non-vertical
3754             // (otherwise it's the same as ctrl+drag anyway)
3756             // calculate angles of the handles
3757             if (xn == 0) {
3758                 if (yn == 0) { // no handle, consider it the continuation of the other one
3759                     an = 0;
3760                     collinear = TRUE;
3761                 }
3762                 else an = 0; // vertical; set the angle to horizontal
3763             } else an = yn/xn;
3765             if (xp == 0) {
3766                 if (yp == 0) { // no handle, consider it the continuation of the other one
3767                     ap = an;
3768                 }
3769                 else ap = 0; // vertical; set the angle to horizontal
3770             } else  ap = yp/xp;
3772             if (collinear) an = ap;
3774             // angles of the perpendiculars; HUGE_VAL means vertical
3775             if (an == 0) na = HUGE_VAL; else na = -1/an;
3776             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3778             // mouse point relative to the node's original pos
3779             pr = p - n->origin;
3781             // distances to the four lines (two handles and two perpendiculars)
3782             d_an = point_line_distance(&pr, an);
3783             d_na = point_line_distance(&pr, na);
3784             d_ap = point_line_distance(&pr, ap);
3785             d_pa = point_line_distance(&pr, pa);
3787             // find out which line is the closest, save its closest point in c
3788             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3789                 point_line_closest(&pr, an, &c);
3790             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3791                 point_line_closest(&pr, ap, &c);
3792             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3793                 point_line_closest(&pr, na, &c);
3794             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3795                 point_line_closest(&pr, pa, &c);
3796             }
3798             // move the node to the closest point
3799             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3800                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3801                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y], 
3802                                             true);
3804         } else {  // constraining to hor/vert
3806             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3807                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3808                                                 p[Geom::X] - n->pos[Geom::X], 
3809                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3810                                                 true, 
3811                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3812             } else { // snap to vert
3813                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3814                                                 n->origin[Geom::X] - n->pos[Geom::X],
3815                                                 p[Geom::Y] - n->pos[Geom::Y],
3816                                                 true,
3817                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3818             }
3819         }
3820     } else { // move freely
3821         if (n->is_dragging) {
3822             if (state & GDK_MOD1_MASK) { // sculpt
3823                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3824             } else {
3825                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3826                                             p[Geom::X] - n->pos[Geom::X],
3827                                             p[Geom::Y] - n->pos[Geom::Y],
3828                                             (state & GDK_SHIFT_MASK) == 0);
3829             }
3830         }
3831     }
3833     n->subpath->nodepath->desktop->scroll_to_point(p);
3835     return TRUE;
3838 /**
3839  * Node handle clicked callback.
3840  */
3841 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3843    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3845     if (state & GDK_CONTROL_MASK) { // "delete" handle
3846         if (n->p.knot == knot) {
3847             n->p.pos = n->pos;
3848         } else if (n->n.knot == knot) {
3849             n->n.pos = n->pos;
3850         }
3851         sp_node_update_handles(n);
3852         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3853         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3854         sp_nodepath_update_statusbar(nodepath);
3856     } else { // just select or add to selection, depending in Shift
3857         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3858     }
3861 /**
3862  * Node handle grabbed callback.
3863  */
3864 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3866    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3868     // convert auto -> smooth when dragging handle
3869    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3870         n->type = Inkscape::NodePath::NODE_SMOOTH;
3871         sp_nodepath_update_node_knot (n);
3872    }
3874     if (!n->selected) {
3875         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3876     }
3878     // remember the origin point of the handle
3879     if (n->p.knot == knot) {
3880         n->p.origin_radial = n->p.pos - n->pos;
3881     } else if (n->n.knot == knot) {
3882         n->n.origin_radial = n->n.pos - n->pos;
3883     } else {
3884         g_assert_not_reached();
3885     }
3887     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3890 /**
3891  * Node handle ungrabbed callback.
3892  */
3893 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3895    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3897     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3898     if (n->p.knot == knot) {
3899         n->p.origin_radial.a = 0;
3900         sp_knot_set_position(knot, n->p.pos, state);
3901     } else if (n->n.knot == knot) {
3902         n->n.origin_radial.a = 0;
3903         sp_knot_set_position(knot, n->n.pos, state);
3904     } else {
3905         g_assert_not_reached();
3906     }
3908     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3911 /**
3912  * Node handle "request" signal callback.
3913  */
3914 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data)
3916     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3918     Inkscape::NodePath::NodeSide *me, *opposite;
3919     gint which;
3920     if (n->p.knot == knot) {
3921         me = &n->p;
3922         opposite = &n->n;
3923         which = -1;
3924     } else if (n->n.knot == knot) {
3925         me = &n->n;
3926         opposite = &n->p;
3927         which = 1;
3928     } else {
3929         me = opposite = NULL;
3930         which = 0;
3931         g_assert_not_reached();
3932     }
3934     SPDesktop *desktop = n->subpath->nodepath->desktop;
3935     SnapManager &m = desktop->namedview->snap_manager;
3936     m.setup(desktop, true, n->subpath->nodepath->item);
3937     Inkscape::SnappedPoint s;
3938     
3939     if ((state & GDK_SHIFT_MASK) != 0) {
3940         // We will not try to snap when the shift-key is pressed
3941         // so remove the old snap indicator and don't wait for it to time-out  
3942         desktop->snapindicator->remove_snaptarget();
3943     }
3945     Inkscape::NodePath::Node *othernode = opposite->other;
3946     if (othernode) {
3947         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3948             /* We are smooth node adjacent with line */
3949             Geom::Point const delta = p - n->pos;
3950             Geom::Coord const len = Geom::L2(delta);
3951             Inkscape::NodePath::Node *othernode = opposite->other;
3952             Geom::Point const ndelta = n->pos - othernode->pos;
3953             Geom::Coord const linelen = Geom::L2(ndelta);
3954             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3955                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3956                 p = n->pos + (scal / linelen) * ndelta;
3957             }
3958             if ((state & GDK_SHIFT_MASK) == 0) {
3959                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, Inkscape::Snapper::ConstraintLine(p, ndelta));
3960             }
3961         } else {
3962             if ((state & GDK_SHIFT_MASK) == 0) {
3963                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p);
3964             }
3965         }
3966     } else {
3967         if ((state & GDK_SHIFT_MASK) == 0) {
3968             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p);
3969         }
3970     }
3971     
3972     s.getPoint(p);
3974     sp_node_adjust_handle(n, -which);
3976     return FALSE;
3979 /**
3980  * Node handle moved callback.
3981  */
3982 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3984    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3985    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3987    Inkscape::NodePath::NodeSide *me;
3988    Inkscape::NodePath::NodeSide *other;
3989     if (n->p.knot == knot) {
3990         me = &n->p;
3991         other = &n->n;
3992     } else if (n->n.knot == knot) {
3993         me = &n->n;
3994         other = &n->p;
3995     } else {
3996         me = NULL;
3997         other = NULL;
3998         g_assert_not_reached();
3999     }
4001     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
4002     Radial rme(me->pos - n->pos);
4003     Radial rother(other->pos - n->pos);
4004     Radial rnew(p - n->pos);
4006     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
4007         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
4008         /* 0 interpreted as "no snapping". */
4010         // 1. Snap to the closest PI/snaps angle, starting from zero.
4011         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
4013         // 2. Snap to the original angle, its opposite and perpendiculars
4014         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
4015             /* The closest PI/2 angle, starting from original angle */
4016             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
4018             // Snap to the closest.
4019             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
4020                        ? a_snapped
4021                        : a_ortho );
4022         }
4024         // 3. Snap to the angle of the opposite line, if any
4025         Inkscape::NodePath::Node *othernode = other->other;
4026         if (othernode) {
4027             Geom::Point other_to_snap(0,0);
4028             if (sp_node_side_is_line(n, other)) {
4029                 other_to_snap = othernode->pos - n->pos;
4030             } else {
4031                 other_to_snap = other->pos - n->pos;
4032             }
4033             if (Geom::L2(other_to_snap) > 1e-3) {
4034                 Radial rother_to_snap(other_to_snap);
4035                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4036                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4038                 // Snap to the closest.
4039                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4040                        ? a_snapped
4041                        : a_oppo );
4042             }
4043         }
4045         rnew.a = a_snapped;
4046     }
4048     if (state & GDK_MOD1_MASK) {
4049         // lock handle length
4050         rnew.r = me->origin_radial.r;
4051     }
4053     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
4054         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
4055         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4056         rother.a += rnew.a - rme.a;
4057         other->pos = Geom::Point(rother) + n->pos;
4058         if (other->knot) {
4059             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4060             sp_knot_moveto(other->knot, other->pos);
4061         }
4062     }
4064     me->pos = Geom::Point(rnew) + n->pos;
4065     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4067     // move knot, but without emitting the signal:
4068     // we cannot emit a "moved" signal because we're now processing it
4069     sp_knot_moveto(me->knot, me->pos);
4071     update_object(n->subpath->nodepath);
4073     /* status text */
4074     SPDesktop *desktop = n->subpath->nodepath->desktop;
4075     if (!desktop) return;
4076     SPEventContext *ec = desktop->event_context;
4077     if (!ec) return;
4079     Inkscape::MessageContext *mc = get_message_context(ec);
4081     if (!mc) return;
4083     double degrees = 180 / M_PI * rnew.a;
4084     if (degrees > 180) degrees -= 360;
4085     if (degrees < -180) degrees += 360;
4086     if (prefs->getBool("/options/compassangledisplay/value"))
4087         degrees = angle_to_compass (degrees);
4089     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4091     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4092          _("<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);
4094     g_string_free(length, TRUE);
4097 /**
4098  * Node handle event callback.
4099  */
4100 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4102     gboolean ret = FALSE;
4103     switch (event->type) {
4104         case GDK_KEY_PRESS:
4105             switch (get_group0_keyval (&event->key)) {
4106                 case GDK_space:
4107                     if (event->key.state & GDK_BUTTON1_MASK) {
4108                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4109                         stamp_repr(nodepath);
4110                         ret = TRUE;
4111                     }
4112                     break;
4113                 default:
4114                     break;
4115             }
4116             break;
4117         case GDK_ENTER_NOTIFY:
4118             // we use an experimentally determined threshold that seems to work fine
4119             if (Geom::L2(n->pos - knot->pos) < 0.75)
4120                 Inkscape::NodePath::Path::active_node = n;
4121             break;
4122         case GDK_LEAVE_NOTIFY:
4123             // we use an experimentally determined threshold that seems to work fine
4124             if (Geom::L2(n->pos - knot->pos) < 0.75)
4125                 Inkscape::NodePath::Path::active_node = NULL;
4126             break;
4127         default:
4128             break;
4129     }
4131     return ret;
4134 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4135                                  Radial &rme, Radial &rother, gboolean const both)
4137     rme.a += angle;
4138     if ( both
4139          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4140          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4141     {
4142         rother.a += angle;
4143     }
4146 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4147                                         Radial &rme, Radial &rother, gboolean const both)
4149     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4151     gdouble r;
4152     if ( both
4153          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4154          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4155     {
4156         r = MAX(rme.r, rother.r);
4157     } else {
4158         r = rme.r;
4159     }
4161     gdouble const weird_angle = atan2(norm_angle, r);
4162 /* Bulia says norm_angle is just the visible distance that the
4163  * object's end must travel on the screen.  Left as 'angle' for want of
4164  * a better name.*/
4166     rme.a += weird_angle;
4167     if ( both
4168          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4169          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4170     {
4171         rother.a += weird_angle;
4172     }
4175 /**
4176  * Rotate one node.
4177  */
4178 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4180     Inkscape::NodePath::NodeSide *me, *other;
4181     bool both = false;
4183     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4184     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4186     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4187         me = &(n->p);
4188         other = &(n->n);
4189     } else if (!n->p.other) {
4190         me = &(n->n);
4191         other = &(n->p);
4192     } else {
4193         if (which > 0) { // right handle
4194             if (xn > xp) {
4195                 me = &(n->n);
4196                 other = &(n->p);
4197             } else {
4198                 me = &(n->p);
4199                 other = &(n->n);
4200             }
4201         } else if (which < 0){ // left handle
4202             if (xn <= xp) {
4203                 me = &(n->n);
4204                 other = &(n->p);
4205             } else {
4206                 me = &(n->p);
4207                 other = &(n->n);
4208             }
4209         } else { // both handles
4210             me = &(n->n);
4211             other = &(n->p);
4212             both = true;
4213         }
4214     }
4216     Radial rme(me->pos - n->pos);
4217     Radial rother(other->pos - n->pos);
4219     if (screen) {
4220         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4221     } else {
4222         node_rotate_one_internal (*n, angle, rme, rother, both);
4223     }
4225     me->pos = n->pos + Geom::Point(rme);
4227     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4228         other->pos =  n->pos + Geom::Point(rother);
4229     }
4231     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4232     // so here we just move all the knots without emitting move signals, for speed
4233     sp_node_update_handles(n, false);
4236 /**
4237  * Rotate selected nodes.
4238  */
4239 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4241     if (!nodepath || !nodepath->selected) return;
4243     if (g_list_length(nodepath->selected) == 1) {
4244        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4245         node_rotate_one (n, angle, which, screen);
4246     } else {
4247        // rotate as an object:
4249         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4250         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4251         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4252             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4253             box.expandTo (n->pos); // contain all selected nodes
4254         }
4256         gdouble rot;
4257         if (screen) {
4258             gdouble const zoom = nodepath->desktop->current_zoom();
4259             gdouble const zmove = angle / zoom;
4260             gdouble const r = Geom::L2(box.max() - box.midpoint());
4261             rot = atan2(zmove, r);
4262         } else {
4263             rot = angle;
4264         }
4266         Geom::Point rot_center;
4267         if (Inkscape::NodePath::Path::active_node == NULL)
4268             rot_center = box.midpoint();
4269         else
4270             rot_center = Inkscape::NodePath::Path::active_node->pos;
4272         Geom::Matrix t =
4273             Geom::Matrix (Geom::Translate(-rot_center)) *
4274             Geom::Matrix (Geom::Rotate(rot)) *
4275             Geom::Matrix (Geom::Translate(rot_center));
4277         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4278             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4279             n->pos *= t;
4280             n->n.pos *= t;
4281             n->p.pos *= t;
4282             sp_node_update_handles(n, false);
4283         }
4284     }
4286     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4289 /**
4290  * Scale one node.
4291  */
4292 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4294     bool both = false;
4295     Inkscape::NodePath::NodeSide *me, *other;
4297     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4298     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4300     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4301         me = &(n->p);
4302         other = &(n->n);
4303         n->code = NR_CURVETO;
4304     } else if (!n->p.other) {
4305         me = &(n->n);
4306         other = &(n->p);
4307         if (n->n.other)
4308             n->n.other->code = NR_CURVETO;
4309     } else {
4310         if (which > 0) { // right handle
4311             if (xn > xp) {
4312                 me = &(n->n);
4313                 other = &(n->p);
4314                 if (n->n.other)
4315                     n->n.other->code = NR_CURVETO;
4316             } else {
4317                 me = &(n->p);
4318                 other = &(n->n);
4319                 n->code = NR_CURVETO;
4320             }
4321         } else if (which < 0){ // left handle
4322             if (xn <= xp) {
4323                 me = &(n->n);
4324                 other = &(n->p);
4325                 if (n->n.other)
4326                     n->n.other->code = NR_CURVETO;
4327             } else {
4328                 me = &(n->p);
4329                 other = &(n->n);
4330                 n->code = NR_CURVETO;
4331             }
4332         } else { // both handles
4333             me = &(n->n);
4334             other = &(n->p);
4335             both = true;
4336             n->code = NR_CURVETO;
4337             if (n->n.other)
4338                 n->n.other->code = NR_CURVETO;
4339         }
4340     }
4342     Radial rme(me->pos - n->pos);
4343     Radial rother(other->pos - n->pos);
4345     rme.r += grow;
4346     if (rme.r < 0) rme.r = 0;
4347     if (rme.a == HUGE_VAL) {
4348         if (me->other) { // if direction is unknown, initialize it towards the next node
4349             Radial rme_next(me->other->pos - n->pos);
4350             rme.a = rme_next.a;
4351         } else { // if there's no next, initialize to 0
4352             rme.a = 0;
4353         }
4354     }
4355     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4356         rother.r += grow;
4357         if (rother.r < 0) rother.r = 0;
4358         if (rother.a == HUGE_VAL) {
4359             rother.a = rme.a + M_PI;
4360         }
4361     }
4363     me->pos = n->pos + Geom::Point(rme);
4365     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4366         other->pos = n->pos + Geom::Point(rother);
4367     }
4369     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4370     // so here we just move all the knots without emitting move signals, for speed
4371     sp_node_update_handles(n, false);
4374 /**
4375  * Scale selected nodes.
4376  */
4377 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4379     if (!nodepath || !nodepath->selected) return;
4381     if (g_list_length(nodepath->selected) == 1) {
4382         // scale handles of the single selected node
4383         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4384         node_scale_one (n, grow, which);
4385     } else {
4386         // scale nodes as an "object":
4388         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4389         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4390         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4391             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4392             box.expandTo (n->pos); // contain all selected nodes
4393         }
4395         if ( Geom::are_near(box.maxExtent(), 0) ) {
4396             SPEventContext *ec = nodepath->desktop->event_context;
4397             if (!ec) return;
4398             Inkscape::MessageContext *mc = get_message_context(ec);
4399             if (!mc) return;
4400             mc->setF(Inkscape::WARNING_MESSAGE,
4401                              _("Cannot scale nodes when all are at the same location."));
4402             return;
4403         }
4404         double scale = (box.maxExtent() + grow)/box.maxExtent();
4407         Geom::Point scale_center;
4408         if (Inkscape::NodePath::Path::active_node == NULL)
4409             scale_center = box.midpoint();
4410         else
4411             scale_center = Inkscape::NodePath::Path::active_node->pos;
4413         Geom::Matrix t =
4414             Geom::Translate(-scale_center) *
4415             Geom::Scale(scale, scale) *
4416             Geom::Translate(scale_center);
4418         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4419             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4420             n->pos *= t;
4421             n->n.pos *= t;
4422             n->p.pos *= t;
4423             sp_node_update_handles(n, false);
4424         }
4425     }
4427     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4430 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4432     if (!nodepath) return;
4433     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4436 /**
4437  * Flip selected nodes horizontally/vertically.
4438  */
4439 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4441     if (!nodepath || !nodepath->selected) return;
4443     if (g_list_length(nodepath->selected) == 1 && !center) {
4444         // flip handles of the single selected node
4445         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4446         double temp = n->p.pos[axis];
4447         n->p.pos[axis] = n->n.pos[axis];
4448         n->n.pos[axis] = temp;
4449         sp_node_update_handles(n, false);
4450     } else {
4451         // scale nodes as an "object":
4453         Geom::Rect box = sp_node_selected_bbox (nodepath);
4454         if (!center) {
4455             center = box.midpoint();
4456         }
4457         Geom::Matrix t =
4458             Geom::Matrix (Geom::Translate(- *center)) *
4459             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4460             Geom::Matrix (Geom::Translate(*center));
4462         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4463             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4464             n->pos *= t;
4465             n->n.pos *= t;
4466             n->p.pos *= t;
4467             sp_node_update_handles(n, false);
4468         }
4469     }
4471     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4474 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4476     g_assert (nodepath->selected);
4478     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4479     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4480     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4481         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4482         box.expandTo (n->pos); // contain all selected nodes
4483     }
4484     return box;
4487 //-----------------------------------------------
4488 /**
4489  * Return new subpath under given nodepath.
4490  */
4491 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4493     g_assert(nodepath);
4494     g_assert(nodepath->desktop);
4496    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4498     s->nodepath = nodepath;
4499     s->closed = FALSE;
4500     s->nodes = NULL;
4501     s->first = NULL;
4502     s->last = NULL;
4504     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4505     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4506     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4508     return s;
4511 /**
4512  * Destroy nodes in subpath, then subpath itself.
4513  */
4514 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4516     g_assert(subpath);
4517     g_assert(subpath->nodepath);
4518     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4520     while (subpath->nodes) {
4521         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4522     }
4524     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4526     g_free(subpath);
4529 /**
4530  * Link head to tail in subpath.
4531  */
4532 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4534     g_assert(!sp->closed);
4535     g_assert(sp->last != sp->first);
4536     g_assert(sp->first->code == NR_MOVETO);
4538     sp->closed = TRUE;
4540     //Link the head to the tail
4541     sp->first->p.other = sp->last;
4542     sp->last->n.other  = sp->first;
4543     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4544     sp->first          = sp->last;
4546     //Remove the extra end node
4547     sp_nodepath_node_destroy(sp->last->n.other);
4550 /**
4551  * Open closed (loopy) subpath at node.
4552  */
4553 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4555     g_assert(sp->closed);
4556     g_assert(n->subpath == sp);
4557     g_assert(sp->first == sp->last);
4559     /* We create new startpoint, current node will become last one */
4561    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4562                                                 &n->pos, &n->pos, &n->n.pos);
4565     sp->closed        = FALSE;
4567     //Unlink to make a head and tail
4568     sp->first         = new_path;
4569     sp->last          = n;
4570     n->n.other        = NULL;
4571     new_path->p.other = NULL;
4574 /**
4575  * Return new node in subpath with given properties.
4576  * \param pos Position of node.
4577  * \param ppos Handle position in previous direction
4578  * \param npos Handle position in previous direction
4579  */
4580 Inkscape::NodePath::Node *
4581 sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node *next, Inkscape::NodePath::NodeType type, NRPathcode code, Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos)
4583     g_assert(sp);
4584     g_assert(sp->nodepath);
4585     g_assert(sp->nodepath->desktop);
4587     if (nodechunk == NULL)
4588         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4590     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4592     n->subpath  = sp;
4594     if (type != Inkscape::NodePath::NODE_NONE) {
4595         // use the type from sodipodi:nodetypes
4596         n->type = type;
4597     } else {
4598         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4599             // points are (almost) collinear
4600             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4601                 // endnode, or a node with a retracted handle
4602                 n->type = Inkscape::NodePath::NODE_CUSP;
4603             } else {
4604                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4605             }
4606         } else {
4607             n->type = Inkscape::NodePath::NODE_CUSP;
4608         }
4609     }
4611     n->code     = code;
4612     n->selected = FALSE;
4613     n->pos      = *pos;
4614     n->p.pos    = *ppos;
4615     n->n.pos    = *npos;
4616     
4617     n->dragging_out = NULL;
4619     Inkscape::NodePath::Node *prev;
4620     if (next) {
4621         //g_assert(g_list_find(sp->nodes, next));
4622         prev = next->p.other;
4623     } else {
4624         prev = sp->last;
4625     }
4627     if (prev)
4628         prev->n.other = n;
4629     else
4630         sp->first = n;
4632     if (next)
4633         next->p.other = n;
4634     else
4635         sp->last = n;
4637     n->p.other = prev;
4638     n->n.other = next;
4640     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"));
4641     sp_knot_set_position(n->knot, *pos, 0);
4643     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4644     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4645     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4647     sp_nodepath_update_node_knot(n);
4649     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4650     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4651     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4652     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4653     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4654     sp_knot_show(n->knot);
4656     // We only create handle knots and lines on demand
4657     n->p.knot = NULL;
4658     n->p.line = NULL;
4659     n->n.knot = NULL;
4660     n->n.line = NULL;
4662     sp->nodes = g_list_prepend(sp->nodes, n);
4664     return n;
4667 /**
4668  * Destroy node and its knots, link neighbors in subpath.
4669  */
4670 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4672     g_assert(node);
4673     g_assert(node->subpath);
4674     g_assert(SP_IS_KNOT(node->knot));
4676    Inkscape::NodePath::SubPath *sp = node->subpath;
4678     if (node->selected) { // first, deselect
4679         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4680         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4681     }
4683     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4685     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4686     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4687     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4688     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4689     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4690     g_object_unref(G_OBJECT(node->knot));
4692     if (node->p.knot) {
4693         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4694         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4695         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4696         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4697         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4698         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4699         g_object_unref(G_OBJECT(node->p.knot));
4700         node->p.knot = NULL;
4701     }
4703     if (node->n.knot) {
4704         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4705         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4706         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4707         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4708         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4709         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4710         g_object_unref(G_OBJECT(node->n.knot));
4711         node->n.knot = NULL;
4712     }
4714     if (node->p.line)
4715         gtk_object_destroy(GTK_OBJECT(node->p.line));
4716     if (node->n.line)
4717         gtk_object_destroy(GTK_OBJECT(node->n.line));
4719     if (sp->nodes) { // there are others nodes on the subpath
4720         if (sp->closed) {
4721             if (sp->first == node) {
4722                 g_assert(sp->last == node);
4723                 sp->first = node->n.other;
4724                 sp->last = sp->first;
4725             }
4726             node->p.other->n.other = node->n.other;
4727             node->n.other->p.other = node->p.other;
4728         } else {
4729             if (sp->first == node) {
4730                 sp->first = node->n.other;
4731                 sp->first->code = NR_MOVETO;
4732             }
4733             if (sp->last == node) sp->last = node->p.other;
4734             if (node->p.other) node->p.other->n.other = node->n.other;
4735             if (node->n.other) node->n.other->p.other = node->p.other;
4736         }
4737     } else { // this was the last node on subpath
4738         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4739     }
4741     g_mem_chunk_free(nodechunk, node);
4744 /**
4745  * Returns one of the node's two sides.
4746  * \param which Indicates which side.
4747  * \return Pointer to previous node side if which==-1, next if which==1.
4748  */
4749 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4751     g_assert(node);
4752     Inkscape::NodePath::NodeSide * result = 0;
4753     switch (which) {
4754         case -1:
4755             result = &node->p;
4756             break;
4757         case 1:
4758             result = &node->n;
4759             break;
4760         default:
4761             g_assert_not_reached();
4762     }
4764     return result;
4767 /**
4768  * Return the other side of the node, given one of its sides.
4769  */
4770 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4772     g_assert(node);
4773     Inkscape::NodePath::NodeSide *result = 0;
4775     if (me == &node->p) {
4776         result = &node->n;
4777     } else if (me == &node->n) {
4778         result = &node->p;
4779     } else {
4780         g_assert_not_reached();
4781     }
4783     return result;
4786 /**
4787  * Return NRPathcode on the given side of the node.
4788  */
4789 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4791     g_assert(node);
4793     NRPathcode result = NR_END;
4794     if (me == &node->p) {
4795         if (node->p.other) {
4796             result = (NRPathcode)node->code;
4797         } else {
4798             result = NR_MOVETO;
4799         }
4800     } else if (me == &node->n) {
4801         if (node->n.other) {
4802             result = (NRPathcode)node->n.other->code;
4803         } else {
4804             result = NR_MOVETO;
4805         }
4806     } else {
4807         g_assert_not_reached();
4808     }
4810     return result;
4813 /**
4814  * Return node with the given index
4815  */
4816 Inkscape::NodePath::Node *
4817 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4819     Inkscape::NodePath::Node *e = NULL;
4821     if (!nodepath) {
4822         return e;
4823     }
4825     //find segment
4826     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4828         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4829         int n = g_list_length(sp->nodes);
4830         if (sp->closed) {
4831             n++;
4832         }
4834         //if the piece belongs to this subpath grab it
4835         //otherwise move onto the next subpath
4836         if (index < n) {
4837             e = sp->first;
4838             for (int i = 0; i < index; ++i) {
4839                 e = e->n.other;
4840             }
4841             break;
4842         } else {
4843             if (sp->closed) {
4844                 index -= (n+1);
4845             } else {
4846                 index -= n;
4847             }
4848         }
4849     }
4851     return e;
4854 /**
4855  * Returns plain text meaning of node type.
4856  */
4857 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4859     unsigned retracted = 0;
4860     bool endnode = false;
4862     for (int which = -1; which <= 1; which += 2) {
4863         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4864         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4865             retracted ++;
4866         if (!side->other)
4867             endnode = true;
4868     }
4870     if (retracted == 0) {
4871         if (endnode) {
4872                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4873                 return _("end node");
4874         } else {
4875             switch (node->type) {
4876                 case Inkscape::NodePath::NODE_CUSP:
4877                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4878                     return _("cusp");
4879                 case Inkscape::NodePath::NODE_SMOOTH:
4880                     // TRANSLATORS: "smooth" is an adjective here
4881                     return _("smooth");
4882                 case Inkscape::NodePath::NODE_AUTO:
4883                     return _("auto");
4884                 case Inkscape::NodePath::NODE_SYMM:
4885                     return _("symmetric");
4886             }
4887         }
4888     } else if (retracted == 1) {
4889         if (endnode) {
4890             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4891             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4892         } else {
4893             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4894         }
4895     } else {
4896         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4897     }
4899     return NULL;
4902 /**
4903  * Handles content of statusbar as long as node tool is active.
4904  */
4905 void
4906 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4908     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");
4909     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4911     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4912     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4913     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4914     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4916     SPDesktop *desktop = NULL;
4917     if (nodepath) {
4918         desktop = nodepath->desktop;
4919     } else {
4920         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4921     }
4923     SPEventContext *ec = desktop->event_context;
4924     if (!ec) return;
4926     Inkscape::MessageContext *mc = get_message_context(ec);
4927     if (!mc) return;
4929     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4931     if (selected_nodes == 0) {
4932         Inkscape::Selection *sel = desktop->selection;
4933         if (!sel || sel->isEmpty()) {
4934             mc->setF(Inkscape::NORMAL_MESSAGE,
4935                      _("Select a single object to edit its nodes or handles."));
4936         } else {
4937             if (nodepath) {
4938             mc->setF(Inkscape::NORMAL_MESSAGE,
4939                      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.",
4940                               "<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.",
4941                               total_nodes),
4942                      total_nodes);
4943             } else {
4944                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4945                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4946                 } else {
4947                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4948                 }
4949             }
4950         }
4951     } else if (nodepath && selected_nodes == 1) {
4952         mc->setF(Inkscape::NORMAL_MESSAGE,
4953                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4954                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4955                           total_nodes),
4956                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4957     } else {
4958         if (selected_subpaths > 1) {
4959             mc->setF(Inkscape::NORMAL_MESSAGE,
4960                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4961                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4962                               total_nodes),
4963                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4964         } else {
4965             mc->setF(Inkscape::NORMAL_MESSAGE,
4966                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4967                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4968                               total_nodes),
4969                      selected_nodes, total_nodes, when_selected);
4970         }
4971     }
4974 /*
4975  * returns a *copy* of the curve of that object.
4976  */
4977 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4978     if (!object)
4979         return NULL;
4981     SPCurve *curve = NULL;
4982     if (SP_IS_PATH(object)) {
4983         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4984         curve = curve_new->copy();
4985     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4986         const gchar *svgd = object->repr->attribute(key);
4987         if (svgd) {
4988             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4989             SPCurve *curve_new = new SPCurve(pv);
4990             if (curve_new) {
4991                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4992             }
4993         }
4994     }
4996     return curve;
4999 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
5000     if (!np || !np->object || !curve)
5001         return;
5003     if (SP_IS_PATH(np->object)) {
5004         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
5005             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
5006         } else {
5007             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
5008         }
5009     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
5010         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
5011         if (lpe) {
5012             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
5013             if (pathparam) {
5014                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
5015                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
5016             }
5017         }
5018     }
5021 /*
5022 SPCanvasItem *
5023 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
5024     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
5026 */
5028 /*
5029 SPCanvasItem *
5030 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
5031     SPCurve *flash_curve = curve->copy();
5032     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
5033     flash_curve->transform(i2d);
5034     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5035     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5036     // unless we also flash the nodes...
5037     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5038     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5039     sp_canvas_item_show(canvasitem);
5040     flash_curve->unref();
5041     return canvasitem;
5044 SPCanvasItem *
5045 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
5046     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5047     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
5048                                            prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff));
5050 */
5052 SPCanvasItem *
5053 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
5054     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
5055     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
5056     flash_curve->transform(i2d);
5057     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5058     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5059     // unless we also flash the nodes...
5060     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5061     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5062     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5063     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5064     sp_canvas_item_show(canvasitem);
5065     flash_curve->unref();
5066     return canvasitem;
5069 // TODO: Merge this with sp_nodepath_make_helper_item()!
5070 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5071     np->show_helperpath = show;
5073     if (show) {
5074         SPCurve *helper_curve = np->curve->copy();
5075         helper_curve->transform(np->i2d);
5076         if (!np->helper_path) {
5077             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5079             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5080             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);
5081             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5082             sp_canvas_item_move_to_z(np->helper_path, 0);
5083             sp_canvas_item_show(np->helper_path);
5084         } else {
5085             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5086         }
5087         helper_curve->unref();
5088     } else {
5089         if (np->helper_path) {
5090             GtkObject *temp = np->helper_path;
5091             np->helper_path = NULL;
5092             gtk_object_destroy(temp);
5093         }
5094     }
5097 /* sp_nodepath_make_straight_path:
5098  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5099  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5100  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5101  */
5102 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5103     np->straight_path = true;
5104     np->show_handles = false;
5105     g_message("add code to make the path straight.");
5106     // do sp_nodepath_convert_node_type on all nodes?
5107     // coding tip: search for this text : "Make selected segments lines"
5110 /*
5111   Local Variables:
5112   mode:c++
5113   c-file-style:"stroustrup"
5114   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5115   indent-tabs-mode:nil
5116   fill-column:99
5117   End:
5118 */
5119 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :