Code

1) Improve the way the distance to the pointer is taken into account when finding...
[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 "display/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 const &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             sp_node_adjust_handle(start, -1);
1069             sp_node_adjust_handle(end, 1);
1071         } else {
1072             Geom::Point delta = end->pos - start->pos;
1073             start->n.pos = start->pos + delta / 3;
1074             end->p.pos = end->pos - delta / 3;
1075             sp_node_adjust_handle(start, 1);
1076             sp_node_adjust_handle(end, -1);
1077         }
1079         sp_node_update_handles(start);
1080         sp_node_update_handles(end);
1081     }
1084 static void
1085 sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
1087     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1088         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1089         node->knot->setSize (node->selected? 11 : 9);
1090         sp_knot_update_ctrl(node->knot);
1091     } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
1092         node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1093         node->knot->setSize (node->selected? 11 : 9);
1094         sp_knot_update_ctrl(node->knot);
1095     } else {
1096         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1097         node->knot->setSize (node->selected? 9 : 7);
1098         sp_knot_update_ctrl(node->knot);
1099     }
1103 /**
1104  * Change node type, and its handles accordingly.
1105  */
1106 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1108     g_assert(node);
1109     g_assert(node->subpath);
1111     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1112         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1113             type =Inkscape::NodePath::NODE_CUSP;
1114         }
1115     }
1117     node->type = type;
1119     sp_nodepath_update_node_knot(node);
1121     // if one of handles is mouseovered, preserve its position
1122     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1123         sp_node_adjust_handle(node, 1);
1124     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1125         sp_node_adjust_handle(node, -1);
1126     } else {
1127         sp_node_adjust_handles(node);
1128     }
1130     sp_node_update_handles(node);
1132     sp_nodepath_update_statusbar(node->subpath->nodepath);
1134     return node;
1137 bool
1138 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1140 // TODO clean up multiple returns
1141         Inkscape::NodePath::Node *othernode = side->other;
1142         if (!othernode)
1143             return false;
1144         NRPathcode const code = sp_node_path_code_from_side(node, side);
1145         if (code == NR_LINETO)
1146             return true;
1147         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1148         if (&node->p == side) {
1149             other_to_me = &othernode->n;
1150         } else if (&node->n == side) {
1151             other_to_me = &othernode->p;
1152         } 
1153         if (!other_to_me)
1154             return false;
1155         bool is_line = 
1156              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1157               Geom::L2(node->pos - side->pos) < 1e-6);
1158         return is_line;
1161 /**
1162  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1163  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1164  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1165  * If already cusp and set to cusp, retracts handles.
1166 */
1167 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1169     if (type == Inkscape::NodePath::NODE_AUTO) {
1170         if (node->p.other != NULL)
1171             node->code = NR_CURVETO;
1172         if (node->n.other != NULL)
1173             node->n.other->code = NR_CURVETO;
1174     }
1176     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1178 /* 
1179   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1180  
1181         if (two_handles) {
1182             // do nothing, adjust_handles called via set_node_type will line them up
1183         } else if (one_handle) {
1184             if (opposite_to_handle_is_line) {
1185                 if (lined_up) {
1186                     // already half-smooth; pull opposite handle too making it fully smooth
1187                 } else {
1188                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1189                 }
1190             } else {
1191                 // pull opposite handle in line with the existing one
1192             }
1193         } else if (no_handles) {
1194             if (both_segments_are_lines OR both_segments_are_curves) {
1195                 //pull both handles
1196             } else {
1197                 // pull the handle opposite to line segment, making node half-smooth
1198             }
1199         }
1200 */
1201         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1202         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1203         bool p_is_line = sp_node_side_is_line(node, &node->p);
1204         bool n_is_line = sp_node_side_is_line(node, &node->n);
1206         if (p_has_handle && n_has_handle) {
1207             // do nothing, adjust_handles will line them up
1208         } else if (p_has_handle || n_has_handle) {
1209             if (p_has_handle && n_is_line) {
1210                 Radial line (node->n.other->pos - node->pos);
1211                 Radial handle (node->pos - node->p.pos);
1212                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1213                     // already half-smooth; pull opposite handle too making it fully smooth
1214                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1215                 } else {
1216                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1217                 }
1218             } else if (n_has_handle && p_is_line) {
1219                 Radial line (node->p.other->pos - node->pos);
1220                 Radial handle (node->pos - node->n.pos);
1221                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1222                     // already half-smooth; pull opposite handle too making it fully smooth
1223                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1224                 } else {
1225                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1226                 }
1227             } else if (p_has_handle && node->n.other) {
1228                 // pull n handle
1229                 node->n.other->code = NR_CURVETO;
1230                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1231                     Geom::L2(node->p.pos - node->pos) :
1232                     Geom::L2(node->n.other->pos - node->pos) / 3;
1233                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1234             } else if (n_has_handle && node->p.other) {
1235                 // pull p handle
1236                 node->code = NR_CURVETO;
1237                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1238                     Geom::L2(node->n.pos - node->pos) :
1239                     Geom::L2(node->p.other->pos - node->pos) / 3;
1240                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1241             }
1242         } else if (!p_has_handle && !n_has_handle) {
1243             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1244                 // no handles, but both segments are either lnes or curves:
1245                 //pull both handles
1247                 // convert both to curves:
1248                 node->code = NR_CURVETO;
1249                 node->n.other->code = NR_CURVETO;
1251                 sp_node_adjust_handles_auto(node);
1252             } else {
1253                 // pull the handle opposite to line segment, making it half-smooth
1254                 if (p_is_line && node->n.other) {
1255                     if (type != Inkscape::NodePath::NODE_SYMM) {
1256                         // pull n handle
1257                         node->n.other->code = NR_CURVETO;
1258                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1259                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1260                     }
1261                 } else if (n_is_line && node->p.other) {
1262                     if (type != Inkscape::NodePath::NODE_SYMM) {
1263                         // pull p handle
1264                         node->code = NR_CURVETO;
1265                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1266                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1267                     }
1268                 }
1269             }
1270         }
1271     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1272         // cusping a cusp: retract nodes
1273         node->p.pos = node->pos;
1274         node->n.pos = node->pos;
1275     }
1277     sp_nodepath_set_node_type (node, type);
1280 /**
1281  * Move node to point, and adjust its and neighbouring handles.
1282  */
1283 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1285     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1286         node->pos = p;
1287         sp_node_adjust_handles_auto(node);
1288     } else {
1289         Geom::Point delta = p - node->pos;
1290         node->pos = p;
1292         node->p.pos += delta;
1293         node->n.pos += delta;
1294     }
1296     Inkscape::NodePath::Node *node_p = NULL;
1297     Inkscape::NodePath::Node *node_n = NULL;
1299     if (node->p.other) {
1300         if (node->code == NR_LINETO) {
1301             sp_node_adjust_handle(node, 1);
1302             sp_node_adjust_handle(node->p.other, -1);
1303             node_p = node->p.other;
1304         }
1305         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1306             sp_node_adjust_handles_auto(node->p.other);
1307             node_p = node->p.other;
1308         }
1309     }
1310     if (node->n.other) {
1311         if (node->n.other->code == NR_LINETO) {
1312             sp_node_adjust_handle(node, -1);
1313             sp_node_adjust_handle(node->n.other, 1);
1314             node_n = node->n.other;
1315         }
1316         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1317             sp_node_adjust_handles_auto(node->n.other);
1318             node_n = node->n.other;
1319         }
1320     }
1322     // this function is only called from batch movers that will update display at the end
1323     // themselves, so here we just move all the knots without emitting move signals, for speed
1324     sp_node_update_handles(node, false);
1325     if (node_n) {
1326         sp_node_update_handles(node_n, false);
1327     }
1328     if (node_p) {
1329         sp_node_update_handles(node_p, false);
1330     }
1333 /**
1334  * Call sp_node_moveto() for node selection and handle possible snapping.
1335  */
1336 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1337                                             bool const snap, bool constrained = false, 
1338                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1340     Geom::Point delta(dx, dy);
1341     Geom::Point best_pt = delta;
1342     Inkscape::SnappedPoint best;
1343     
1344     if (snap) {    
1345         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1346          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1347          * must provide that information. */
1348           
1349         // Build a list of the unselected nodes to which the snapper should snap 
1350         std::vector<Geom::Point> unselected_nodes;
1351         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1352             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1353             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1354                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1355                 if (!node->selected) {
1356                     unselected_nodes.push_back(to_2geom(node->pos));
1357                 }    
1358             }
1359         }        
1360         
1361         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1362         
1363         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1364             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1365             m.setup(nodepath->desktop, false, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1366             Inkscape::SnappedPoint s;
1367             
1368             if (constrained) {
1369                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1370                 dedicated_constraint.setPoint(n->pos);
1371                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
1372             } else {
1373                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta));
1374             }            
1375             
1376             if (s.getSnapped()) {
1377                 s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));                            
1378                 if (!s.isOtherOneBetter(best, true)) {
1379                         best = s;
1380                         best_pt = from_2geom(s.getPoint()) - n->pos;
1381                 }
1382             }
1383         }
1384                         
1385         if (best.getSnapped()) {
1386             nodepath->desktop->snapindicator->set_new_snappoint(best);
1387         } else {
1388             nodepath->desktop->snapindicator->remove_snappoint();    
1389         }
1390     }
1392     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1393         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1394         sp_node_moveto(n, n->pos + best_pt);
1395     }
1397     // do not update repr here so that node dragging is acceptably fast
1398     update_object(nodepath);
1401 /**
1402 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1403 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1404 near x = 0.
1405  */
1406 double
1407 sculpt_profile (double x, double alpha, guint profile)
1409     double result = 1;
1411     if (x >= 1) {
1412         result = 0;
1413     } else if (x <= 0) {
1414         result = 1;
1415     } else {
1416         switch (profile) {
1417             case SCULPT_PROFILE_LINEAR:
1418                 result = 1 - x;
1419                 break;
1420             case SCULPT_PROFILE_BELL:
1421                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1422                 break;
1423             case SCULPT_PROFILE_ELLIPTIC:
1424                 result = sqrt(1 - x*x);
1425                 break;
1426             default:
1427                 g_assert_not_reached();
1428         }
1429     }
1431     return result;
1434 double
1435 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1437     // extremely primitive for now, don't have time to look for the real one
1438     double lower = Geom::L2(b - a);
1439     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1440     return (lower + upper)/2;
1443 void
1444 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1446     n->pos = n->origin + delta;
1447     n->n.pos = n->n.origin + delta_n;
1448     n->p.pos = n->p.origin + delta_p;
1449     sp_node_adjust_handles(n);
1450     sp_node_update_handles(n, false);
1453 /**
1454  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1455  * on how far they are from the dragged node n.
1456  */
1457 static void
1458 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1460     g_assert (n);
1461     g_assert (nodepath);
1462     g_assert (n->subpath->nodepath == nodepath);
1464     double pressure = n->knot->pressure;
1465     if (pressure == 0)
1466         pressure = 0.5; // default
1467     pressure = CLAMP (pressure, 0.2, 0.8);
1469     // map pressure to alpha = 1/5 ... 5
1470     double alpha = 1 - 2 * fabs(pressure - 0.5);
1471     if (pressure > 0.5)
1472         alpha = 1/alpha;
1474     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1475     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1477     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1478         // Only one subpath has selected nodes:
1479         // use linear mode, where the distance from n to node being dragged is calculated along the path
1481         double n_sel_range = 0, p_sel_range = 0;
1482         guint n_nodes = 0, p_nodes = 0;
1483         guint n_sel_nodes = 0, p_sel_nodes = 0;
1485         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1486         {
1487             double n_range = 0, p_range = 0;
1488             bool n_going = true, p_going = true;
1489             Inkscape::NodePath::Node *n_node = n;
1490             Inkscape::NodePath::Node *p_node = n;
1491             do {
1492                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1493                 if (n_node && n_going)
1494                     n_node = n_node->n.other;
1495                 if (n_node == NULL) {
1496                     n_going = false;
1497                 } else {
1498                     n_nodes ++;
1499                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1500                     if (n_node->selected) {
1501                         n_sel_nodes ++;
1502                         n_sel_range = n_range;
1503                     }
1504                     if (n_node == p_node) {
1505                         n_going = false;
1506                         p_going = false;
1507                     }
1508                 }
1509                 if (p_node && p_going)
1510                     p_node = p_node->p.other;
1511                 if (p_node == NULL) {
1512                     p_going = false;
1513                 } else {
1514                     p_nodes ++;
1515                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1516                     if (p_node->selected) {
1517                         p_sel_nodes ++;
1518                         p_sel_range = p_range;
1519                     }
1520                     if (p_node == n_node) {
1521                         n_going = false;
1522                         p_going = false;
1523                     }
1524                 }
1525             } while (n_going || p_going);
1526         }
1528         // Second pass: actually move nodes in this subpath
1529         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1530         {
1531             double n_range = 0, p_range = 0;
1532             bool n_going = true, p_going = true;
1533             Inkscape::NodePath::Node *n_node = n;
1534             Inkscape::NodePath::Node *p_node = n;
1535             do {
1536                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1537                 if (n_node && n_going)
1538                     n_node = n_node->n.other;
1539                 if (n_node == NULL) {
1540                     n_going = false;
1541                 } else {
1542                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1543                     if (n_node->selected) {
1544                         sp_nodepath_move_node_and_handles (n_node,
1545                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1546                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1547                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1548                     }
1549                     if (n_node == p_node) {
1550                         n_going = false;
1551                         p_going = false;
1552                     }
1553                 }
1554                 if (p_node && p_going)
1555                     p_node = p_node->p.other;
1556                 if (p_node == NULL) {
1557                     p_going = false;
1558                 } else {
1559                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1560                     if (p_node->selected) {
1561                         sp_nodepath_move_node_and_handles (p_node,
1562                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1563                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1564                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1565                     }
1566                     if (p_node == n_node) {
1567                         n_going = false;
1568                         p_going = false;
1569                     }
1570                 }
1571             } while (n_going || p_going);
1572         }
1574     } else {
1575         // Multiple subpaths have selected nodes:
1576         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1577         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1578         // fix the pear-like shape when sculpting e.g. a ring
1580         // First pass: calculate range
1581         gdouble direct_range = 0;
1582         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1583             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1584             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1585                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1586                 if (node->selected) {
1587                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1588                 }
1589             }
1590         }
1592         // Second pass: actually move nodes
1593         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1594             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1595             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1596                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1597                 if (node->selected) {
1598                     if (direct_range > 1e-6) {
1599                         sp_nodepath_move_node_and_handles (node,
1600                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1601                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1602                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1603                     } else {
1604                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1605                     }
1607                 }
1608             }
1609         }
1610     }
1612     // do not update repr here so that node dragging is acceptably fast
1613     update_object(nodepath);
1617 /**
1618  * Move node selection to point, adjust its and neighbouring handles,
1619  * handle possible snapping, and commit the change with possible undo.
1620  */
1621 void
1622 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1624     if (!nodepath) return;
1626     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1628     if (dx == 0) {
1629         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1630     } else if (dy == 0) {
1631         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1632     } else {
1633         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1634     }
1637 /**
1638  * Move node selection off screen and commit the change.
1639  */
1640 void
1641 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1643     // borrowed from sp_selection_move_screen in selection-chemistry.c
1644     // we find out the current zoom factor and divide deltas by it
1646     gdouble zoom = desktop->current_zoom();
1647     gdouble zdx = dx / zoom;
1648     gdouble zdy = dy / zoom;
1650     if (!nodepath) return;
1652     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1654     if (dx == 0) {
1655         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1656     } else if (dy == 0) {
1657         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1658     } else {
1659         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1660     }
1663 /**
1664  * Move selected nodes to the absolute position given
1665  */
1666 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1668     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1669         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1670         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1671         sp_node_moveto(n, npos);
1672     }
1674     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1677 /**
1678  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1679  */
1680 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1682     boost::optional<Geom::Coord> no_coord;
1683     g_return_val_if_fail(nodepath->selected, no_coord);
1685     // determine coordinate of first selected node
1686     GList *nsel = nodepath->selected;
1687     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1688     Geom::Coord coord = n->pos[axis];
1689     bool coincide = true;
1691     // compare it to the coordinates of all the other selected nodes
1692     for (GList *l = nsel->next; l != NULL; l = l->next) {
1693         n = (Inkscape::NodePath::Node *) l->data;
1694         if (n->pos[axis] != coord) {
1695             coincide = false;
1696         }
1697     }
1698     if (coincide) {
1699         return coord;
1700     } else {
1701         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1702         // currently we return the coordinate of the bounding box midpoint because I don't know how
1703         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1704         return bbox.midpoint()[axis];
1705     }
1708 /** If they don't yet exist, creates knot and line for the given side of the node */
1709 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1711     if (!side->knot) {
1712         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"));
1714         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1715         side->knot->setSize (7);
1716         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1717         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1718         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1719         sp_knot_update_ctrl(side->knot);
1721         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1722         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1723         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1724         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1725         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1726         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1727     }
1729     if (!side->line) {
1730         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1731                                         SP_TYPE_CTRLLINE, NULL);
1732     }
1735 /**
1736  * Ensure the given handle of the node is visible/invisible, update its screen position
1737  */
1738 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1740     g_assert(node != NULL);
1742    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1743     NRPathcode code = sp_node_path_code_from_side(node, side);
1745     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1747     if (show_handle) {
1748         if (!side->knot) { // No handle knot at all
1749             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1750             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1751             side->knot->pos = side->pos;
1752             if (side->knot->item)
1753                 SP_CTRL(side->knot->item)->moveto(side->pos);
1754             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1755             sp_knot_show(side->knot);
1756         } else {
1757             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1758                 if (fire_move_signals) {
1759                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1760                 } else {
1761                     sp_knot_moveto(side->knot, side->pos);
1762                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1763                 }
1764             }
1765             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1766                 sp_knot_show(side->knot);
1767             }
1768         }
1769         sp_canvas_item_show(side->line);
1770     } else {
1771         if (side->knot) {
1772             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1773                 sp_knot_hide(side->knot);
1774             }
1775         }
1776         if (side->line) {
1777             sp_canvas_item_hide(side->line);
1778         }
1779     }
1782 /**
1783  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1784  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1785  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1786  * updated; otherwise, just move the knots silently (used in batch moves).
1787  */
1788 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1790     g_assert(node != NULL);
1792     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1793         sp_knot_show(node->knot);
1794     }
1796     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1797         if (fire_move_signals)
1798             sp_knot_set_position(node->knot, node->pos, 0);
1799         else
1800             sp_knot_moveto(node->knot, node->pos);
1801     }
1803     gboolean show_handles = node->selected;
1804     if (node->p.other != NULL) {
1805         if (node->p.other->selected) show_handles = TRUE;
1806     }
1807     if (node->n.other != NULL) {
1808         if (node->n.other->selected) show_handles = TRUE;
1809     }
1811     if (node->subpath->nodepath->show_handles == false)
1812         show_handles = FALSE;
1814     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1815     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1818 /**
1819  * Call sp_node_update_handles() for all nodes on subpath.
1820  */
1821 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1823     g_assert(subpath != NULL);
1825     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1826         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1827     }
1830 /**
1831  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1832  */
1833 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1835     g_assert(nodepath != NULL);
1837     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1838         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1839     }
1842 void
1843 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1845     if (nodepath) {
1846         nodepath->show_handles = show;
1847         sp_nodepath_update_handles(nodepath);
1848     }
1851 /**
1852  * Adds all selected nodes in nodepath to list.
1853  */
1854 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1856     StlConv<Node *>::list(l, selected);
1857 /// \todo this adds a copying, rework when the selection becomes a stl list
1860 /**
1861  * Align selected nodes on the specified axis.
1862  */
1863 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1865     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1866         return;
1867     }
1869     if ( !nodepath->selected->next ) { // only one node selected
1870         return;
1871     }
1872    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1873     Geom::Point dest(pNode->pos);
1874     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1875         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1876         if (pNode) {
1877             dest[axis] = pNode->pos[axis];
1878             sp_node_moveto(pNode, dest);
1879         }
1880     }
1882     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1885 /// Helper struct.
1886 struct NodeSort
1888    Inkscape::NodePath::Node *_node;
1889     Geom::Coord _coord;
1890     /// \todo use vectorof pointers instead of calling copy ctor
1891     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1892         _node(node), _coord(node->pos[axis])
1893     {}
1895 };
1897 static bool operator<(NodeSort const &a, NodeSort const &b)
1899     return (a._coord < b._coord);
1902 /**
1903  * Distribute selected nodes on the specified axis.
1904  */
1905 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1907     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1908         return;
1909     }
1911     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1912         return;
1913     }
1915    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1916     std::vector<NodeSort> sorted;
1917     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1918         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1919         if (pNode) {
1920             NodeSort n(pNode, axis);
1921             sorted.push_back(n);
1922             //dest[axis] = pNode->pos[axis];
1923             //sp_node_moveto(pNode, dest);
1924         }
1925     }
1926     std::sort(sorted.begin(), sorted.end());
1927     unsigned int len = sorted.size();
1928     //overall bboxes span
1929     float dist = (sorted.back()._coord -
1930                   sorted.front()._coord);
1931     //new distance between each bbox
1932     float step = (dist) / (len - 1);
1933     float pos = sorted.front()._coord;
1934     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1935           it < sorted.end();
1936           it ++ )
1937     {
1938         Geom::Point dest((*it)._node->pos);
1939         dest[axis] = pos;
1940         sp_node_moveto((*it)._node, dest);
1941         pos += step;
1942     }
1944     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1948 /**
1949  * Call sp_nodepath_line_add_node() for all selected segments.
1950  */
1951 void
1952 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1954     if (!nodepath) {
1955         return;
1956     }
1958     GList *nl = NULL;
1960     int n_added = 0;
1962     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1963        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1964         g_assert(t->selected);
1965         if (t->p.other && t->p.other->selected) {
1966             nl = g_list_prepend(nl, t);
1967         }
1968     }
1970     while (nl) {
1971        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1972        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1973        sp_nodepath_node_select(n, TRUE, FALSE);
1974        n_added ++;
1975        nl = g_list_remove(nl, t);
1976     }
1978     /** \todo fixme: adjust ? */
1979     sp_nodepath_update_handles(nodepath);
1981     if (n_added > 1) {
1982         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1983     } else if (n_added > 0) {
1984         sp_nodepath_update_repr(nodepath, _("Add node"));
1985     }
1987     sp_nodepath_update_statusbar(nodepath);
1990 /**
1991  * Select segment nearest to point
1992  */
1993 void
1994 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
1996     if (!nodepath) {
1997         return;
1998     }
2000     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2001     Geom::PathVector const &pathv = curve->get_pathvector();
2002     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2003     if (!pvpos) {
2004         g_print ("Possible error?\n");
2005         return;
2006     }
2008     // calculate index for nodepath's representation.
2009     unsigned int segment_index = floor(pvpos->t) + 1;
2010     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2011         segment_index += pathv[i].size() + 1;
2012         if (pathv[i].closed()) {
2013             segment_index += 1;
2014         }
2015     }
2017     curve->unref();
2019     //find segment to segment
2020     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2022     //fixme: this can return NULL, so check before proceeding.
2023     g_return_if_fail(e != NULL);
2025     gboolean force = FALSE;
2026     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2027         force = TRUE;
2028     }
2029     sp_nodepath_node_select(e, (gboolean) toggle, force);
2030     if (e->p.other)
2031         sp_nodepath_node_select(e->p.other, TRUE, force);
2033     sp_nodepath_update_handles(nodepath);
2035     sp_nodepath_update_statusbar(nodepath);
2038 /**
2039  * Add a node nearest to point
2040  */
2041 void
2042 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2044     if (!nodepath) {
2045         return;
2046     }
2048     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2049     Geom::PathVector const &pathv = curve->get_pathvector();
2050     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2051     if (!pvpos) {
2052         g_print ("Possible error?\n");
2053         return;
2054     }
2056     // calculate index for nodepath's representation.
2057     double int_part;
2058     double t = std::modf(pvpos->t, &int_part);
2059     unsigned int segment_index = (unsigned int)int_part + 1;
2060     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2061         segment_index += pathv[i].size() + 1;
2062         if (pathv[i].closed()) {
2063             segment_index += 1;
2064         }
2065     }
2067     curve->unref();
2069     //find segment to split
2070     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2071     if (!e) {
2072         return;
2073     }
2075     //don't know why but t seems to flip for lines
2076     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2077         t = 1.0 - t;
2078     }
2080     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2081     sp_nodepath_node_select(n, FALSE, TRUE);
2083     /* fixme: adjust ? */
2084     sp_nodepath_update_handles(nodepath);
2086     sp_nodepath_update_repr(nodepath, _("Add node"));
2088     sp_nodepath_update_statusbar(nodepath);
2091 /*
2092  * Adjusts a segment so that t moves by a certain delta for dragging
2093  * converts lines to curves
2094  *
2095  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2096  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2097  */
2098 void
2099 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2101     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2103     //fixme: e and e->p can be NULL, so check for those before proceeding
2104     g_return_if_fail(e != NULL);
2105     g_return_if_fail(&e->p != NULL);
2107     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2108         e->type = Inkscape::NodePath::NODE_SMOOTH;
2109         sp_nodepath_update_node_knot (e);
2110     }
2111     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2112         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2113         sp_nodepath_update_node_knot (e->p.other);
2114     }
2116     /* feel good is an arbitrary parameter that distributes the delta between handles
2117      * if t of the drag point is less than 1/6 distance form the endpoint only
2118      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2119      */
2120     double feel_good;
2121     if (t <= 1.0 / 6.0)
2122         feel_good = 0;
2123     else if (t <= 0.5)
2124         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2125     else if (t <= 5.0 / 6.0)
2126         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2127     else
2128         feel_good = 1;
2130     //if we're dragging a line convert it to a curve
2131     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2132         sp_nodepath_set_line_type(e, NR_CURVETO);
2133     }
2135     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2136     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2137     e->p.other->n.pos += offsetcoord0;
2138     e->p.pos += offsetcoord1;
2140     // adjust handles of adjacent nodes where necessary
2141     sp_node_adjust_handle(e,1);
2142     sp_node_adjust_handle(e->p.other,-1);
2144     sp_nodepath_update_handles(e->subpath->nodepath);
2146     update_object(e->subpath->nodepath);
2148     sp_nodepath_update_statusbar(e->subpath->nodepath);
2152 /**
2153  * Call sp_nodepath_break() for all selected segments.
2154  */
2155 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2157     if (!nodepath) return;
2159     GList *tempin = g_list_copy(nodepath->selected);
2160     GList *temp = NULL;
2161     for (GList *l = tempin; l != NULL; l = l->next) {
2162        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2163        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2164         if (nn == NULL) continue; // no break, no new node
2165         temp = g_list_prepend(temp, nn);
2166     }
2167     g_list_free(tempin);
2169     if (temp) {
2170         sp_nodepath_deselect(nodepath);
2171     }
2172     for (GList *l = temp; l != NULL; l = l->next) {
2173         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2174     }
2176     sp_nodepath_update_handles(nodepath);
2178     sp_nodepath_update_repr(nodepath, _("Break path"));
2181 /**
2182  * Duplicate the selected node(s).
2183  */
2184 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2186     if (!nodepath) {
2187         return;
2188     }
2190     GList *temp = NULL;
2191     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2192        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2193        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2194         if (nn == NULL) continue; // could not duplicate
2195         temp = g_list_prepend(temp, nn);
2196     }
2198     if (temp) {
2199         sp_nodepath_deselect(nodepath);
2200     }
2201     for (GList *l = temp; l != NULL; l = l->next) {
2202         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2203     }
2205     sp_nodepath_update_handles(nodepath);
2207     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2210 /**
2211  *  Internal function to join two nodes by merging them into one.
2212  */
2213 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2215     /* a and b are endpoints */
2217     // if one of the two nodes is mouseovered, fix its position
2218     Geom::Point c;
2219     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2220         c = a->pos;
2221     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2222         c = b->pos;
2223     } else {
2224         // otherwise, move joined node to the midpoint
2225         c = (a->pos + b->pos) / 2;
2226     }
2228     if (a->subpath == b->subpath) {
2229        Inkscape::NodePath::SubPath *sp = a->subpath;
2230         sp_nodepath_subpath_close(sp);
2231         sp_node_moveto (sp->first, c);
2233         sp_nodepath_update_handles(sp->nodepath);
2234         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2235         return;
2236     }
2238     /* a and b are separate subpaths */
2239     Inkscape::NodePath::SubPath *sa = a->subpath;
2240     Inkscape::NodePath::SubPath *sb = b->subpath;
2241     Geom::Point p;
2242     Inkscape::NodePath::Node *n;
2243     NRPathcode code;
2244     if (a == sa->first) {
2245         // we will now reverse sa, so that a is its last node, not first, and drop that node
2246         p = sa->first->n.pos;
2247         code = (NRPathcode)sa->first->n.other->code;
2248         // create new subpath
2249        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2250        // create a first moveto node on it
2251         n = sa->last;
2252         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2253         n = n->p.other;
2254         if (n == sa->first) n = NULL;
2255         while (n) {
2256             // copy the rest of the nodes from sa to t, going backwards
2257             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2258             n = n->p.other;
2259             if (n == sa->first) n = NULL;
2260         }
2261         // replace sa with t
2262         sp_nodepath_subpath_destroy(sa);
2263         sa = t;
2264     } else if (a == sa->last) {
2265         // a is already last, just drop it
2266         p = sa->last->p.pos;
2267         code = (NRPathcode)sa->last->code;
2268         sp_nodepath_node_destroy(sa->last);
2269     } else {
2270         code = NR_END;
2271         g_assert_not_reached();
2272     }
2274     if (b == sb->first) {
2275         // copy all nodes from b to a, forward 
2276         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2277         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2278             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2279         }
2280     } else if (b == sb->last) {
2281         // copy all nodes from b to a, backward 
2282         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2283         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2284             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2285         }
2286     } else {
2287         g_assert_not_reached();
2288     }
2289     /* and now destroy sb */
2291     sp_nodepath_subpath_destroy(sb);
2293     sp_nodepath_update_handles(sa->nodepath);
2295     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2297     sp_nodepath_update_statusbar(nodepath);
2300 /**
2301  *  Internal function to join two nodes by adding a segment between them.
2302  */
2303 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2305     if (a->subpath == b->subpath) {
2306        Inkscape::NodePath::SubPath *sp = a->subpath;
2308         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2309         sp->closed = TRUE;
2311         sp->first->p.other = sp->last;
2312         sp->last->n.other  = sp->first;
2314         sp_node_handle_mirror_p_to_n(sp->last);
2315         sp_node_handle_mirror_n_to_p(sp->first);
2317         sp->first->code = sp->last->code;
2318         sp->first       = sp->last;
2320         sp_nodepath_update_handles(sp->nodepath);
2322         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2324         return;
2325     }
2327     /* a and b are separate subpaths */
2328     Inkscape::NodePath::SubPath *sa = a->subpath;
2329     Inkscape::NodePath::SubPath *sb = b->subpath;
2331     Inkscape::NodePath::Node *n;
2332     Geom::Point p;
2333     NRPathcode code;
2334     if (a == sa->first) {
2335         code = (NRPathcode) sa->first->n.other->code;
2336        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2337         n = sa->last;
2338         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2339         for (n = n->p.other; n != NULL; n = n->p.other) {
2340             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2341         }
2342         sp_nodepath_subpath_destroy(sa);
2343         sa = t;
2344     } else if (a == sa->last) {
2345         code = (NRPathcode)sa->last->code;
2346     } else {
2347         code = NR_END;
2348         g_assert_not_reached();
2349     }
2351     if (b == sb->first) {
2352         n = sb->first;
2353         sp_node_handle_mirror_p_to_n(sa->last);
2354         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2355         sp_node_handle_mirror_n_to_p(sa->last);
2356         for (n = n->n.other; n != NULL; n = n->n.other) {
2357             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2358         }
2359     } else if (b == sb->last) {
2360         n = sb->last;
2361         sp_node_handle_mirror_p_to_n(sa->last);
2362         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2363         sp_node_handle_mirror_n_to_p(sa->last);
2364         for (n = n->p.other; n != NULL; n = n->p.other) {
2365             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2366         }
2367     } else {
2368         g_assert_not_reached();
2369     }
2370     /* and now destroy sb */
2372     sp_nodepath_subpath_destroy(sb);
2374     sp_nodepath_update_handles(sa->nodepath);
2376     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2379 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2381 /**
2382  * Internal function to handle joining two nodes.
2383  */
2384 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2386     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2388     if (g_list_length(nodepath->selected) != 2) {
2389         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2390         return;
2391     }
2393     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2394     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2396     g_assert(a != b);
2397     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2398         // someone tried to join an orphan node (i.e. a single-node subpath).
2399         // this is not worth an error message, just fail silently.
2400         return;
2401     }
2403     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2404         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2405         return;
2406     }
2408     switch(mode) {
2409         case NODE_JOIN_ENDPOINTS:
2410             do_node_selected_join(nodepath, a, b);
2411             break;
2412         case NODE_JOIN_SEGMENT:
2413             do_node_selected_join_segment(nodepath, a, b);
2414             break;
2415     }
2418 /**
2419  *  Join two nodes by merging them into one.
2420  */
2421 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2423     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2426 /**
2427  *  Join two nodes by adding a segment between them.
2428  */
2429 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2431     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2434 /**
2435  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2436  */
2437 void sp_node_delete_preserve(GList *nodes_to_delete)
2439     GSList *nodepaths = NULL;
2441     while (nodes_to_delete) {
2442         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2443         Inkscape::NodePath::SubPath *sp = node->subpath;
2444         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2445         Inkscape::NodePath::Node *sample_cursor = NULL;
2446         Inkscape::NodePath::Node *sample_end = NULL;
2447         Inkscape::NodePath::Node *delete_cursor = node;
2448         bool just_delete = false;
2450         //find the start of this contiguous selection
2451         //move left to the first node that is not selected
2452         //or the start of the non-closed path
2453         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2454             delete_cursor = curr;
2455         }
2457         //just delete at the beginning of an open path
2458         if (!delete_cursor->p.other) {
2459             sample_cursor = delete_cursor;
2460             just_delete = true;
2461         } else {
2462             sample_cursor = delete_cursor->p.other;
2463         }
2465         //calculate points for each segment
2466         int rate = 5;
2467         float period = 1.0 / rate;
2468         std::vector<Geom::Point> data;
2469         if (!just_delete) {
2470             data.push_back(sample_cursor->pos);
2471             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2472                 //just delete at the end of an open path
2473                 if (!sp->closed && curr == sp->last) {
2474                     just_delete = true;
2475                     break;
2476                 }
2478                 //sample points on the contiguous selected segment
2479                 Geom::Point *bez;
2480                 bez = new Geom::Point [4];
2481                 bez[0] = curr->pos;
2482                 bez[1] = curr->n.pos;
2483                 bez[2] = curr->n.other->p.pos;
2484                 bez[3] = curr->n.other->pos;
2485                 for (int i=1; i<rate; i++) {
2486                     gdouble t = i * period;
2487                     Geom::Point p = bezier_pt(3, bez, t);
2488                     data.push_back(p);
2489                 }
2490                 data.push_back(curr->n.other->pos);
2492                 sample_end = curr->n.other;
2493                 //break if we've come full circle or hit the end of the selection
2494                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2495                     break;
2496                 }
2497             }
2498         }
2500         if (!just_delete) {
2501             //calculate the best fitting single segment and adjust the endpoints
2502             Geom::Point *adata;
2503             adata = new Geom::Point [data.size()];
2504             copy(data.begin(), data.end(), adata);
2506             Geom::Point *bez;
2507             bez = new Geom::Point [4];
2508             //would decreasing error create a better fitting approximation?
2509             gdouble error = 1.0;
2510             gint ret;
2511             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2513             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2514             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2515             //the resulting nodes behave as expected.
2516             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2517                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2518             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2519                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2521             //adjust endpoints
2522             sample_cursor->n.pos = bez[1];
2523             sample_end->p.pos = bez[2];
2524         }
2526         //destroy this contiguous selection
2527         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2528             Inkscape::NodePath::Node *temp = delete_cursor;
2529             if (delete_cursor->n.other == delete_cursor) {
2530                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2531                 delete_cursor = NULL;
2532             } else {
2533                 delete_cursor = delete_cursor->n.other;
2534             }
2535             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2536             sp_nodepath_node_destroy(temp);
2537         }
2539         sp_nodepath_update_handles(nodepath);
2541         if (!g_slist_find(nodepaths, nodepath))
2542             nodepaths = g_slist_prepend (nodepaths, nodepath);
2543     }
2545     for (GSList *i = nodepaths; i; i = i->next) {
2546         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2547         // different nodepaths will give us one undo event per nodepath
2548         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2550         // if the entire nodepath is removed, delete the selected object.
2551         if (nodepath->subpaths == NULL ||
2552             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2553             //at least 2
2554             sp_nodepath_get_node_count(nodepath) < 2) {
2555             SPDocument *document = sp_desktop_document (nodepath->desktop);
2556             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2557             //delete this nodepath's object, not the entire selection! (though at this time, this
2558             //does not matter)
2559             sp_selection_delete(nodepath->desktop);
2560             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2561                               _("Delete nodes"));
2562         } else {
2563             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2564             sp_nodepath_update_statusbar(nodepath);
2565         }
2566     }
2568     g_slist_free (nodepaths);
2571 /**
2572  * Delete one or more selected nodes.
2573  */
2574 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2576     if (!nodepath) return;
2577     if (!nodepath->selected) return;
2579     /** \todo fixme: do it the right way */
2580     while (nodepath->selected) {
2581        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2582         sp_nodepath_node_destroy(node);
2583     }
2586     //clean up the nodepath (such as for trivial subpaths)
2587     sp_nodepath_cleanup(nodepath);
2589     sp_nodepath_update_handles(nodepath);
2591     // if the entire nodepath is removed, delete the selected object.
2592     if (nodepath->subpaths == NULL ||
2593         sp_nodepath_get_node_count(nodepath) < 2) {
2594         SPDocument *document = sp_desktop_document (nodepath->desktop);
2595         sp_selection_delete(nodepath->desktop);
2596         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2597                           _("Delete nodes"));
2598         return;
2599     }
2601     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2603     sp_nodepath_update_statusbar(nodepath);
2606 /**
2607  * Delete one or more segments between two selected nodes.
2608  * This is the code for 'split'.
2609  */
2610 void
2611 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2613    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2614    Inkscape::NodePath::Node *curr, *next;     //Iterators
2616     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2618     if (g_list_length(nodepath->selected) != 2) {
2619         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2620                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2621         return;
2622     }
2624     //Selected nodes, not inclusive
2625    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2626    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2628     if ( ( a==b)                       ||  //same node
2629          (a->subpath  != b->subpath )  ||  //not the same path
2630          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2631          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2632     {
2633         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2634                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2635         return;
2636     }
2638     //###########################################
2639     //# BEGIN EDITS
2640     //###########################################
2641     //##################################
2642     //# CLOSED PATH
2643     //##################################
2644     if (a->subpath->closed) {
2647         gboolean reversed = FALSE;
2649         //Since we can go in a circle, we need to find the shorter distance.
2650         //  a->b or b->a
2651         start = end = NULL;
2652         int distance    = 0;
2653         int minDistance = 0;
2654         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2655             if (curr==b) {
2656                 //printf("a to b:%d\n", distance);
2657                 start = a;//go from a to b
2658                 end   = b;
2659                 minDistance = distance;
2660                 //printf("A to B :\n");
2661                 break;
2662             }
2663             distance++;
2664         }
2666         //try again, the other direction
2667         distance = 0;
2668         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2669             if (curr==a) {
2670                 //printf("b to a:%d\n", distance);
2671                 if (distance < minDistance) {
2672                     start    = b;  //we go from b to a
2673                     end      = a;
2674                     reversed = TRUE;
2675                     //printf("B to A\n");
2676                 }
2677                 break;
2678             }
2679             distance++;
2680         }
2683         //Copy everything from 'end' to 'start' to a new subpath
2684        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2685         for (curr=end ; curr ; curr=curr->n.other) {
2686             NRPathcode code = (NRPathcode) curr->code;
2687             if (curr == end)
2688                 code = NR_MOVETO;
2689             sp_nodepath_node_new(t, NULL,
2690                                  (Inkscape::NodePath::NodeType)curr->type, code,
2691                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2692             if (curr == start)
2693                 break;
2694         }
2695         sp_nodepath_subpath_destroy(a->subpath);
2698     }
2702     //##################################
2703     //# OPEN PATH
2704     //##################################
2705     else {
2707         //We need to get the direction of the list between A and B
2708         //Can we walk from a to b?
2709         start = end = NULL;
2710         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2711             if (curr==b) {
2712                 start = a;  //did it!  we go from a to b
2713                 end   = b;
2714                 //printf("A to B\n");
2715                 break;
2716             }
2717         }
2718         if (!start) {//didn't work?  let's try the other direction
2719             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2720                 if (curr==a) {
2721                     start = b;  //did it!  we go from b to a
2722                     end   = a;
2723                     //printf("B to A\n");
2724                     break;
2725                 }
2726             }
2727         }
2728         if (!start) {
2729             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2730                                                      _("Cannot find path between nodes."));
2731             return;
2732         }
2736         //Copy everything after 'end' to a new subpath
2737        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2738         for (curr=end ; curr ; curr=curr->n.other) {
2739             NRPathcode code = (NRPathcode) curr->code;
2740             if (curr == end)
2741                 code = NR_MOVETO;
2742             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2743                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2744         }
2746         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2747         for (curr = start->n.other ; curr  ; curr=next) {
2748             next = curr->n.other;
2749             sp_nodepath_node_destroy(curr);
2750         }
2752     }
2753     //###########################################
2754     //# END EDITS
2755     //###########################################
2757     //clean up the nodepath (such as for trivial subpaths)
2758     sp_nodepath_cleanup(nodepath);
2760     sp_nodepath_update_handles(nodepath);
2762     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2764     sp_nodepath_update_statusbar(nodepath);
2767 /**
2768  * Call sp_nodepath_set_line() for all selected segments.
2769  */
2770 void
2771 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2773     if (nodepath == NULL) return;
2775     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2776        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2777         g_assert(n->selected);
2778         if (n->p.other && n->p.other->selected) {
2779             sp_nodepath_set_line_type(n, code);
2780         }
2781     }
2783     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2786 /**
2787  * Call sp_nodepath_convert_node_type() for all selected nodes.
2788  */
2789 void
2790 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2792     if (nodepath == NULL) return;
2794     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2796     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2797         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2798     }
2800     sp_nodepath_update_repr(nodepath, _("Change node type"));
2803 /**
2804  * Change select status of node, update its own and neighbour handles.
2805  */
2806 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2808     node->selected = selected;
2810     if (selected) {
2811         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2812         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2813         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2814         sp_knot_update_ctrl(node->knot);
2815     } else {
2816         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2817         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2818         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2819         sp_knot_update_ctrl(node->knot);
2820     }
2822     sp_node_update_handles(node);
2823     if (node->n.other) sp_node_update_handles(node->n.other);
2824     if (node->p.other) sp_node_update_handles(node->p.other);
2827 /**
2828 \brief Select a node
2829 \param node     The node to select
2830 \param incremental   If true, add to selection, otherwise deselect others
2831 \param override   If true, always select this node, otherwise toggle selected status
2832 */
2833 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2835     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2837     if (incremental) {
2838         if (override) {
2839             if (!g_list_find(nodepath->selected, node)) {
2840                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2841             }
2842             sp_node_set_selected(node, TRUE);
2843         } else { // toggle
2844             if (node->selected) {
2845                 g_assert(g_list_find(nodepath->selected, node));
2846                 nodepath->selected = g_list_remove(nodepath->selected, node);
2847             } else {
2848                 g_assert(!g_list_find(nodepath->selected, node));
2849                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2850             }
2851             sp_node_set_selected(node, !node->selected);
2852         }
2853     } else {
2854         sp_nodepath_deselect(nodepath);
2855         nodepath->selected = g_list_prepend(nodepath->selected, node);
2856         sp_node_set_selected(node, TRUE);
2857     }
2859     sp_nodepath_update_statusbar(nodepath);
2863 /**
2864 \brief Deselect all nodes in the nodepath
2865 */
2866 void
2867 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2869     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2871     while (nodepath->selected) {
2872         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2873         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2874     }
2875     sp_nodepath_update_statusbar(nodepath);
2878 /**
2879 \brief Select or invert selection of all nodes in the nodepath
2880 */
2881 void
2882 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2884     if (!nodepath) return;
2886     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2887        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2888         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2889            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2890            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2891         }
2892     }
2895 /**
2896  * If nothing selected, does the same as sp_nodepath_select_all();
2897  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2898  * (i.e., similar to "select all in layer", with the "selected" subpaths
2899  * being treated as "layers" in the path).
2900  */
2901 void
2902 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2904     if (!nodepath) return;
2906     if (g_list_length (nodepath->selected) == 0) {
2907         sp_nodepath_select_all (nodepath, invert);
2908         return;
2909     }
2911     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2912     GSList *subpaths = NULL;
2914     for (GList *l = copy; l != NULL; l = l->next) {
2915         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2916         Inkscape::NodePath::SubPath *subpath = n->subpath;
2917         if (!g_slist_find (subpaths, subpath))
2918             subpaths = g_slist_prepend (subpaths, subpath);
2919     }
2921     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2922         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2923         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2924             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2925             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2926         }
2927     }
2929     g_slist_free (subpaths);
2930     g_list_free (copy);
2933 /**
2934  * \brief Select the node after the last selected; if none is selected,
2935  * select the first within path.
2936  */
2937 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2939     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2941    Inkscape::NodePath::Node *last = NULL;
2942     if (nodepath->selected) {
2943         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2944            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2945             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2946             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2947                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2948                 if (node->selected) {
2949                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2950                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2951                             if (spl->next) { // there's a next subpath
2952                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2953                                 last = subpath_next->first;
2954                             } else if (spl->prev) { // there's a previous subpath
2955                                 last = NULL; // to be set later to the first node of first subpath
2956                             } else {
2957                                 last = node->n.other;
2958                             }
2959                         } else {
2960                             last = node->n.other;
2961                         }
2962                     } else {
2963                         if (node->n.other) {
2964                             last = node->n.other;
2965                         } else {
2966                             if (spl->next) { // there's a next subpath
2967                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2968                                 last = subpath_next->first;
2969                             } else if (spl->prev) { // there's a previous subpath
2970                                 last = NULL; // to be set later to the first node of first subpath
2971                             } else {
2972                                 last = (Inkscape::NodePath::Node *) subpath->first;
2973                             }
2974                         }
2975                     }
2976                 }
2977             }
2978         }
2979         sp_nodepath_deselect(nodepath);
2980     }
2982     if (last) { // there's at least one more node after selected
2983         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2984     } else { // no more nodes, select the first one in first subpath
2985        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2986         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2987     }
2990 /**
2991  * \brief Select the node before the first selected; if none is selected,
2992  * select the last within path
2993  */
2994 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2996     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2998    Inkscape::NodePath::Node *last = NULL;
2999     if (nodepath->selected) {
3000         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
3001            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3002             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
3003                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3004                 if (node->selected) {
3005                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3006                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3007                             if (spl->prev) { // there's a prev subpath
3008                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3009                                 last = subpath_prev->last;
3010                             } else if (spl->next) { // there's a next subpath
3011                                 last = NULL; // to be set later to the last node of last subpath
3012                             } else {
3013                                 last = node->p.other;
3014                             }
3015                         } else {
3016                             last = node->p.other;
3017                         }
3018                     } else {
3019                         if (node->p.other) {
3020                             last = node->p.other;
3021                         } else {
3022                             if (spl->prev) { // there's a prev subpath
3023                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3024                                 last = subpath_prev->last;
3025                             } else if (spl->next) { // there's a next subpath
3026                                 last = NULL; // to be set later to the last node of last subpath
3027                             } else {
3028                                 last = (Inkscape::NodePath::Node *) subpath->last;
3029                             }
3030                         }
3031                     }
3032                 }
3033             }
3034         }
3035         sp_nodepath_deselect(nodepath);
3036     }
3038     if (last) { // there's at least one more node before selected
3039         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3040     } else { // no more nodes, select the last one in last subpath
3041         GList *spl = g_list_last(nodepath->subpaths);
3042        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3043         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3044     }
3047 /**
3048  * \brief Select all nodes that are within the rectangle.
3049  */
3050 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3052     if (!incremental) {
3053         sp_nodepath_deselect(nodepath);
3054     }
3056     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3057        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3058         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3059            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3061             if (b.contains(node->pos)) {
3062                 sp_nodepath_node_select(node, TRUE, TRUE);
3063             }
3064         }
3065     }
3069 void
3070 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3072     g_assert (n);
3073     g_assert (nodepath);
3074     g_assert (n->subpath->nodepath == nodepath);
3076     if (g_list_length (nodepath->selected) == 0) {
3077         if (grow > 0) {
3078             sp_nodepath_node_select(n, TRUE, TRUE);
3079         }
3080         return;
3081     }
3083     if (g_list_length (nodepath->selected) == 1) {
3084         if (grow < 0) {
3085             sp_nodepath_deselect (nodepath);
3086             return;
3087         }
3088     }
3090         double n_sel_range = 0, p_sel_range = 0;
3091             Inkscape::NodePath::Node *farthest_n_node = n;
3092             Inkscape::NodePath::Node *farthest_p_node = n;
3094         // Calculate ranges
3095         {
3096             double n_range = 0, p_range = 0;
3097             bool n_going = true, p_going = true;
3098             Inkscape::NodePath::Node *n_node = n;
3099             Inkscape::NodePath::Node *p_node = n;
3100             do {
3101                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3102                 if (n_node && n_going)
3103                     n_node = n_node->n.other;
3104                 if (n_node == NULL) {
3105                     n_going = false;
3106                 } else {
3107                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3108                     if (n_node->selected) {
3109                         n_sel_range = n_range;
3110                         farthest_n_node = n_node;
3111                     }
3112                     if (n_node == p_node) {
3113                         n_going = false;
3114                         p_going = false;
3115                     }
3116                 }
3117                 if (p_node && p_going)
3118                     p_node = p_node->p.other;
3119                 if (p_node == NULL) {
3120                     p_going = false;
3121                 } else {
3122                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3123                     if (p_node->selected) {
3124                         p_sel_range = p_range;
3125                         farthest_p_node = p_node;
3126                     }
3127                     if (p_node == n_node) {
3128                         n_going = false;
3129                         p_going = false;
3130                     }
3131                 }
3132             } while (n_going || p_going);
3133         }
3135     if (grow > 0) {
3136         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3137                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3138         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3139                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3140         }
3141     } else {
3142         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3143                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3144         } else if (farthest_p_node && farthest_p_node->selected) {
3145                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3146         }
3147     }
3150 void
3151 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3153     g_assert (n);
3154     g_assert (nodepath);
3155     g_assert (n->subpath->nodepath == nodepath);
3157     if (g_list_length (nodepath->selected) == 0) {
3158         if (grow > 0) {
3159             sp_nodepath_node_select(n, TRUE, TRUE);
3160         }
3161         return;
3162     }
3164     if (g_list_length (nodepath->selected) == 1) {
3165         if (grow < 0) {
3166             sp_nodepath_deselect (nodepath);
3167             return;
3168         }
3169     }
3171     Inkscape::NodePath::Node *farthest_selected = NULL;
3172     double farthest_dist = 0;
3174     Inkscape::NodePath::Node *closest_unselected = NULL;
3175     double closest_dist = NR_HUGE;
3177     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3178        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3179         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3180            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3181            if (node == n)
3182                continue;
3183            if (node->selected) {
3184                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3185                    farthest_dist = Geom::L2(node->pos - n->pos);
3186                    farthest_selected = node;
3187                }
3188            } else {
3189                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3190                    closest_dist = Geom::L2(node->pos - n->pos);
3191                    closest_unselected = node;
3192                }
3193            }
3194         }
3195     }
3197     if (grow > 0) {
3198         if (closest_unselected) {
3199             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3200         }
3201     } else {
3202         if (farthest_selected) {
3203             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3204         }
3205     }
3209 /**
3210 \brief  Saves all nodes' and handles' current positions in their origin members
3211 */
3212 void
3213 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3215     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3216        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3217         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3218            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3219            n->origin = n->pos;
3220            n->p.origin = n->p.pos;
3221            n->n.origin = n->n.pos;
3222         }
3223     }
3226 /**
3227 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3228 */
3229 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3231     GList *r = NULL;
3232     if (nodepath->selected) {
3233         guint i = 0;
3234         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3235             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3236             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3237                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3238                 i++;
3239                 if (node->selected) {
3240                     r = g_list_append(r, GINT_TO_POINTER(i));
3241                 }
3242             }
3243         }
3244     }
3245     return r;
3248 /**
3249 \brief  Restores selection by selecting nodes whose positions are in the list
3250 */
3251 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3253     sp_nodepath_deselect(nodepath);
3255     guint i = 0;
3256     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3257        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3258         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3259            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3260             i++;
3261             if (g_list_find(r, GINT_TO_POINTER(i))) {
3262                 sp_nodepath_node_select(node, TRUE, TRUE);
3263             }
3264         }
3265     }
3269 /**
3270 \brief Adjusts handle according to node type and line code.
3271 */
3272 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3274     g_assert(node);
3276     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3277     if (node->type == Inkscape::NodePath::NODE_AUTO)
3278         return;
3280    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3281    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3283    // nothing to do if we are an end node
3284     if (me->other == NULL) return;
3285     if (other->other == NULL) return;
3287     // nothing to do if we are a cusp node
3288     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3290     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3291     NRPathcode mecode;
3292     if (which_adjust == 1) {
3293         mecode = (NRPathcode)me->other->code;
3294     } else {
3295         mecode = (NRPathcode)node->code;
3296     }
3297     if (mecode == NR_LINETO) return;
3299     if (sp_node_side_is_line(node, other)) {
3300         // other is a line, and we are either smooth or symm
3301        Inkscape::NodePath::Node *othernode = other->other;
3302         double len = Geom::L2(me->pos - node->pos);
3303         Geom::Point delta = node->pos - othernode->pos;
3304         double linelen = Geom::L2(delta);
3305         if (linelen < 1e-18)
3306             return;
3307         me->pos = node->pos + (len / linelen)*delta;
3308         return;
3309     }
3311     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3312         // symmetrize 
3313         me->pos = 2 * node->pos - other->pos;
3314         return;
3315     } else {
3316         // smoothify
3317         double len = Geom::L2(me->pos - node->pos);
3318         Geom::Point delta = other->pos - node->pos;
3319         double otherlen = Geom::L2(delta);
3320         if (otherlen < 1e-18) return;
3321         me->pos = node->pos - (len / otherlen) * delta;
3322     }
3325 /**
3326  \brief Adjusts both handles according to node type and line code
3327  */
3328 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3330     g_assert(node);
3332     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3334     /* we are either smooth or symm */
3336     if (node->p.other == NULL) return;
3337     if (node->n.other == NULL) return;
3339     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3340         sp_node_adjust_handles_auto(node);
3341         return;
3342     }
3344     if (sp_node_side_is_line(node, &node->p)) {
3345         sp_node_adjust_handle(node, 1);
3346         return;
3347     }
3349     if (sp_node_side_is_line(node, &node->n)) {
3350         sp_node_adjust_handle(node, -1);
3351         return;
3352     }
3354     /* both are curves */
3355     Geom::Point const delta( node->n.pos - node->p.pos );
3357     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3358         node->p.pos = node->pos - delta / 2;
3359         node->n.pos = node->pos + delta / 2;
3360         return;
3361     }
3363     /* We are smooth */
3364     double plen = Geom::L2(node->p.pos - node->pos);
3365     if (plen < 1e-18) return;
3366     double nlen = Geom::L2(node->n.pos - node->pos);
3367     if (nlen < 1e-18) return;
3368     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3369     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3372 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3374     if (node->p.other == NULL || node->n.other == NULL) {
3375         node->p.pos = node->pos;
3376         node->n.pos = node->pos;
3377         return;
3378     }
3380     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3381     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3382  
3383     double norm_leg_prev = Geom::L2(leg_prev);
3384     double norm_leg_next = Geom::L2(leg_next);
3385  
3386     Geom::Point delta;
3387     if (norm_leg_next > 0.0) {
3388         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3389         delta.normalize();
3390     }
3391  
3392     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3393     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3396 /**
3397  * Node event callback.
3398  */
3399 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3401     gboolean ret = FALSE;
3402     switch (event->type) {
3403         case GDK_ENTER_NOTIFY:
3404             Inkscape::NodePath::Path::active_node = n;
3405             break;
3406         case GDK_LEAVE_NOTIFY:
3407             Inkscape::NodePath::Path::active_node = NULL;
3408             break;
3409         case GDK_SCROLL:
3410             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3411                 switch (event->scroll.direction) {
3412                     case GDK_SCROLL_UP:
3413                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3414                         break;
3415                     case GDK_SCROLL_DOWN:
3416                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3417                         break;
3418                     default:
3419                         break;
3420                 }
3421                 ret = TRUE;
3422             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3423                 switch (event->scroll.direction) {
3424                     case GDK_SCROLL_UP:
3425                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3426                         break;
3427                     case GDK_SCROLL_DOWN:
3428                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3429                         break;
3430                     default:
3431                         break;
3432                 }
3433                 ret = TRUE;
3434             }
3435             break;
3436         case GDK_KEY_PRESS:
3437             switch (get_group0_keyval (&event->key)) {
3438                 case GDK_space:
3439                     if (event->key.state & GDK_BUTTON1_MASK) {
3440                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3441                         stamp_repr(nodepath);
3442                         ret = TRUE;
3443                     }
3444                     break;
3445                 case GDK_Page_Up:
3446                     if (event->key.state & GDK_CONTROL_MASK) {
3447                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3448                     } else {
3449                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3450                     }
3451                     break;
3452                 case GDK_Page_Down:
3453                     if (event->key.state & GDK_CONTROL_MASK) {
3454                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3455                     } else {
3456                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3457                     }
3458                     break;
3459                 default:
3460                     break;
3461             }
3462             break;
3463         default:
3464             break;
3465     }
3467     return ret;
3470 /**
3471  * Handle keypress on node; directly called.
3472  */
3473 gboolean node_key(GdkEvent *event)
3475     Inkscape::NodePath::Path *np;
3477     // there is no way to verify nodes so set active_node to nil when deleting!!
3478     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3480     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3481         gint ret = FALSE;
3482         switch (get_group0_keyval (&event->key)) {
3483             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3484             case GDK_BackSpace:
3485                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3486                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3487                 sp_nodepath_update_repr(np, _("Delete node"));
3488                 Inkscape::NodePath::Path::active_node = NULL;
3489                 ret = TRUE;
3490                 break;
3491             case GDK_c:
3492                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3493                 ret = TRUE;
3494                 break;
3495             case GDK_s:
3496                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3497                 ret = TRUE;
3498                 break;
3499             case GDK_a:
3500                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3501                 ret = TRUE;
3502                 break;
3503             case GDK_y:
3504                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3505                 ret = TRUE;
3506                 break;
3507             case GDK_b:
3508                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3509                 ret = TRUE;
3510                 break;
3511         }
3512         return ret;
3513     }
3514     return FALSE;
3517 /**
3518  * Mouseclick on node callback.
3519  */
3520 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3522    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3524     if (state & GDK_CONTROL_MASK) {
3525         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3527         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3528             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3529                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3530             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3531                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3532             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3533                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3534             } else {
3535                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3536             }
3537             sp_nodepath_update_repr(nodepath, _("Change node type"));
3538             sp_nodepath_update_statusbar(nodepath);
3540         } else { //ctrl+alt+click: delete node
3541             GList *node_to_delete = NULL;
3542             node_to_delete = g_list_append(node_to_delete, n);
3543             sp_node_delete_preserve(node_to_delete);
3544         }
3546     } else {
3547         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3548     }
3551 /**
3552  * Mouse grabbed node callback.
3553  */
3554 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3556    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3558     if (!n->selected) {
3559         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3560     }
3562     n->is_dragging = true;
3563     // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping)
3564     n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin;  
3565     
3566     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3568     sp_nodepath_remember_origins (n->subpath->nodepath);
3571 /**
3572  * Mouse ungrabbed node callback.
3573  */
3574 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3576    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3578    n->dragging_out = NULL;
3579    n->is_dragging = false;
3580    n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
3581    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3583    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3586 /**
3587  * The point on a line, given by its angle, closest to the given point.
3588  * \param p  A point.
3589  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3590  * \param closest  Pointer to the point struct where the result is stored.
3591  * \todo FIXME: use dot product perhaps?
3592  */
3593 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3595     if (a == HUGE_VAL) { // vertical
3596         *closest = Geom::Point(0, (*p)[Geom::Y]);
3597     } else {
3598         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3599         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3600     }
3603 /**
3604  * Distance from the point to a line given by its angle.
3605  * \param p  A point.
3606  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3607  */
3608 static double point_line_distance(Geom::Point *p, double a)
3610     Geom::Point c;
3611     point_line_closest(p, a, &c);
3612     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]));
3615 /**
3616  * Callback for node "request" signal.
3617  * \todo fixme: This goes to "moved" event? (lauris)
3618  */
3619 static gboolean
3620 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3622     double yn, xn, yp, xp;
3623     double an, ap, na, pa;
3624     double d_an, d_ap, d_na, d_pa;
3625     gboolean collinear = FALSE;
3626     Geom::Point c;
3627     Geom::Point pr;
3629     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3631     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3633     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3634     if ( (!n->subpath->nodepath->straight_path) &&
3635          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3636            || n->dragging_out ) )
3637     {
3638        Geom::Point mouse = p;
3640        if (!n->dragging_out) {
3641            // This is the first drag-out event; find out which handle to drag out
3642            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3643            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3645            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3646                return FALSE;
3648            Inkscape::NodePath::NodeSide *opposite;
3649            if (appr_p > appr_n) { // closer to p
3650                n->dragging_out = &n->p;
3651                opposite = &n->n;
3652                n->code = NR_CURVETO;
3653            } else if (appr_p < appr_n) { // closer to n
3654                n->dragging_out = &n->n;
3655                opposite = &n->p;
3656                n->n.other->code = NR_CURVETO;
3657            } else { // p and n nodes are the same
3658                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3659                    n->dragging_out = &n->p;
3660                    opposite = &n->n;
3661                    n->code = NR_CURVETO;
3662                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3663                    n->dragging_out = &n->n;
3664                    opposite = &n->p;
3665                    n->n.other->code = NR_CURVETO;
3666                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3667                    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);
3668                    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);
3669                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3670                        n->dragging_out = &n->n;
3671                        opposite = &n->p;
3672                        n->n.other->code = NR_CURVETO;
3673                    } else { // closer to other's n handle
3674                        n->dragging_out = &n->p;
3675                        opposite = &n->n;
3676                        n->code = NR_CURVETO;
3677                    }
3678                }
3679            }
3681            // if there's another handle, make sure the one we drag out starts parallel to it
3682            if (opposite->pos != n->pos) {
3683                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3684            }
3686            // knots might not be created yet!
3687            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3688            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3689        }
3691        // pass this on to the handle-moved callback
3692        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3693        sp_node_update_handles(n);
3694        return TRUE;
3695    }
3697     if (state & GDK_CONTROL_MASK) { // constrained motion
3699         // calculate relative distances of handles
3700         // n handle:
3701         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3702         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3703         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3704         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3705             if (n->n.other) { // if there is the next point
3706                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3707                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3708                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3709             }
3710         }
3711         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3712         if (yn < 0) { xn = -xn; yn = -yn; }
3714         // p handle:
3715         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3716         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3717         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3718         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3719             if (n->p.other) {
3720                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3721                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3722                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3723             }
3724         }
3725         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3726         if (yp < 0) { xp = -xp; yp = -yp; }
3728         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3729             // sliding on handles, only if at least one of the handles is non-vertical
3730             // (otherwise it's the same as ctrl+drag anyway)
3732             // calculate angles of the handles
3733             if (xn == 0) {
3734                 if (yn == 0) { // no handle, consider it the continuation of the other one
3735                     an = 0;
3736                     collinear = TRUE;
3737                 }
3738                 else an = 0; // vertical; set the angle to horizontal
3739             } else an = yn/xn;
3741             if (xp == 0) {
3742                 if (yp == 0) { // no handle, consider it the continuation of the other one
3743                     ap = an;
3744                 }
3745                 else ap = 0; // vertical; set the angle to horizontal
3746             } else  ap = yp/xp;
3748             if (collinear) an = ap;
3750             // angles of the perpendiculars; HUGE_VAL means vertical
3751             if (an == 0) na = HUGE_VAL; else na = -1/an;
3752             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3754             // mouse point relative to the node's original pos
3755             pr = p - n->origin;
3757             // distances to the four lines (two handles and two perpendiculars)
3758             d_an = point_line_distance(&pr, an);
3759             d_na = point_line_distance(&pr, na);
3760             d_ap = point_line_distance(&pr, ap);
3761             d_pa = point_line_distance(&pr, pa);
3763             // find out which line is the closest, save its closest point in c
3764             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3765                 point_line_closest(&pr, an, &c);
3766             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3767                 point_line_closest(&pr, ap, &c);
3768             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3769                 point_line_closest(&pr, na, &c);
3770             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3771                 point_line_closest(&pr, pa, &c);
3772             }
3774             // move the node to the closest point
3775             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3776                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3777                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y], 
3778                                             true);
3780         } else {  // constraining to hor/vert
3782             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3783                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3784                                                 p[Geom::X] - n->pos[Geom::X], 
3785                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3786                                                 true, 
3787                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3788             } else { // snap to vert
3789                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3790                                                 n->origin[Geom::X] - n->pos[Geom::X],
3791                                                 p[Geom::Y] - n->pos[Geom::Y],
3792                                                 true,
3793                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3794             }
3795         }
3796     } else { // move freely
3797         if (n->is_dragging) {
3798             if (state & GDK_MOD1_MASK) { // sculpt
3799                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3800             } else {
3801                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3802                                             p[Geom::X] - n->pos[Geom::X],
3803                                             p[Geom::Y] - n->pos[Geom::Y],
3804                                             (state & GDK_SHIFT_MASK) == 0);
3805             }
3806         }
3807     }
3809     n->subpath->nodepath->desktop->scroll_to_point(p);
3811     return TRUE;
3814 /**
3815  * Node handle clicked callback.
3816  */
3817 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3819    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3821     if (state & GDK_CONTROL_MASK) { // "delete" handle
3822         if (n->p.knot == knot) {
3823             n->p.pos = n->pos;
3824         } else if (n->n.knot == knot) {
3825             n->n.pos = n->pos;
3826         }
3827         sp_node_update_handles(n);
3828         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3829         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3830         sp_nodepath_update_statusbar(nodepath);
3832     } else { // just select or add to selection, depending in Shift
3833         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3834     }
3837 /**
3838  * Node handle grabbed callback.
3839  */
3840 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3842    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3844     // convert auto -> smooth when dragging handle
3845    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3846         n->type = Inkscape::NodePath::NODE_SMOOTH;
3847         sp_nodepath_update_node_knot (n);
3848    }
3850     if (!n->selected) {
3851         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3852     }
3854     // remember the origin point of the handle
3855     if (n->p.knot == knot) {
3856         n->p.origin_radial = n->p.pos - n->pos;
3857     } else if (n->n.knot == knot) {
3858         n->n.origin_radial = n->n.pos - n->pos;
3859     } else {
3860         g_assert_not_reached();
3861     }
3863     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3866 /**
3867  * Node handle ungrabbed callback.
3868  */
3869 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3871    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3873     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3874     if (n->p.knot == knot) {
3875         n->p.origin_radial.a = 0;
3876         sp_knot_set_position(knot, n->p.pos, state);
3877     } else if (n->n.knot == knot) {
3878         n->n.origin_radial.a = 0;
3879         sp_knot_set_position(knot, n->n.pos, state);
3880     } else {
3881         g_assert_not_reached();
3882     }
3884     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3887 /**
3888  * Node handle "request" signal callback.
3889  */
3890 static gboolean node_handle_request(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3892     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3894     Inkscape::NodePath::NodeSide *me, *opposite;
3895     gint which;
3896     if (n->p.knot == knot) {
3897         me = &n->p;
3898         opposite = &n->n;
3899         which = -1;
3900     } else if (n->n.knot == knot) {
3901         me = &n->n;
3902         opposite = &n->p;
3903         which = 1;
3904     } else {
3905         me = opposite = NULL;
3906         which = 0;
3907         g_assert_not_reached();
3908     }
3910     SPDesktop *desktop = n->subpath->nodepath->desktop;
3911     SnapManager &m = desktop->namedview->snap_manager;
3912     m.setup(desktop, true, n->subpath->nodepath->item);
3913     Inkscape::SnappedPoint s;
3914     
3915     if ((state & GDK_SHIFT_MASK) != 0) {
3916         // We will not try to snap when the shift-key is pressed
3917         // so remove the old snap indicator and don't wait for it to time-out  
3918         desktop->snapindicator->remove_snappoint();
3919     }
3921     Inkscape::NodePath::Node *othernode = opposite->other;
3922     if (othernode) {
3923         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3924             /* We are smooth node adjacent with line */
3925             Geom::Point const delta = p - n->pos;
3926             Geom::Coord const len = Geom::L2(delta);
3927             Inkscape::NodePath::Node *othernode = opposite->other;
3928             Geom::Point const ndelta = n->pos - othernode->pos;
3929             Geom::Coord const linelen = Geom::L2(ndelta);
3930             Geom::Point ptemp = p;
3931             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3932                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3933                 ptemp = n->pos + (scal / linelen) * ndelta;
3934             }
3935             if ((state & GDK_SHIFT_MASK) == 0) {
3936                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, ptemp, Inkscape::Snapper::ConstraintLine(ptemp, ndelta));
3937             }
3938         } else {
3939             if ((state & GDK_SHIFT_MASK) == 0) {
3940                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p);
3941             }
3942         }
3943     } else {
3944         if ((state & GDK_SHIFT_MASK) == 0) {
3945             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p);
3946         }
3947     }
3948     
3949     sp_node_adjust_handle(n, -which);
3951     return FALSE;
3954 /**
3955  * Node handle moved callback.
3956  */
3957 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3959    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3960    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3962    Inkscape::NodePath::NodeSide *me;
3963    Inkscape::NodePath::NodeSide *other;
3964     if (n->p.knot == knot) {
3965         me = &n->p;
3966         other = &n->n;
3967     } else if (n->n.knot == knot) {
3968         me = &n->n;
3969         other = &n->p;
3970     } else {
3971         me = NULL;
3972         other = NULL;
3973         g_assert_not_reached();
3974     }
3976     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3977     Radial rme(me->pos - n->pos);
3978     Radial rother(other->pos - n->pos);
3979     Radial rnew(p - n->pos);
3981     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3982         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
3983         /* 0 interpreted as "no snapping". */
3985         // 1. Snap to the closest PI/snaps angle, starting from zero.
3986         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3988         // 2. Snap to the original angle, its opposite and perpendiculars
3989         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3990             /* The closest PI/2 angle, starting from original angle */
3991             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3993             // Snap to the closest.
3994             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3995                        ? a_snapped
3996                        : a_ortho );
3997         }
3999         // 3. Snap to the angle of the opposite line, if any
4000         Inkscape::NodePath::Node *othernode = other->other;
4001         if (othernode) {
4002             Geom::Point other_to_snap(0,0);
4003             if (sp_node_side_is_line(n, other)) {
4004                 other_to_snap = othernode->pos - n->pos;
4005             } else {
4006                 other_to_snap = other->pos - n->pos;
4007             }
4008             if (Geom::L2(other_to_snap) > 1e-3) {
4009                 Radial rother_to_snap(other_to_snap);
4010                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4011                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4013                 // Snap to the closest.
4014                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4015                        ? a_snapped
4016                        : a_oppo );
4017             }
4018         }
4020         rnew.a = a_snapped;
4021     }
4023     if (state & GDK_MOD1_MASK) {
4024         // lock handle length
4025         rnew.r = me->origin_radial.r;
4026     }
4028     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
4029         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
4030         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4031         rother.a += rnew.a - rme.a;
4032         other->pos = Geom::Point(rother) + n->pos;
4033         if (other->knot) {
4034             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4035             sp_knot_moveto(other->knot, other->pos);
4036         }
4037     }
4039     me->pos = Geom::Point(rnew) + n->pos;
4040     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4042     // move knot, but without emitting the signal:
4043     // we cannot emit a "moved" signal because we're now processing it
4044     sp_knot_moveto(me->knot, me->pos);
4046     update_object(n->subpath->nodepath);
4048     /* status text */
4049     SPDesktop *desktop = n->subpath->nodepath->desktop;
4050     if (!desktop) return;
4051     SPEventContext *ec = desktop->event_context;
4052     if (!ec) return;
4054     Inkscape::MessageContext *mc = get_message_context(ec);
4056     if (!mc) return;
4058     double degrees = 180 / M_PI * rnew.a;
4059     if (degrees > 180) degrees -= 360;
4060     if (degrees < -180) degrees += 360;
4061     if (prefs->getBool("/options/compassangledisplay/value"))
4062         degrees = angle_to_compass (degrees);
4064     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4066     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4067          _("<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);
4069     g_string_free(length, TRUE);
4072 /**
4073  * Node handle event callback.
4074  */
4075 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4077     gboolean ret = FALSE;
4078     switch (event->type) {
4079         case GDK_KEY_PRESS:
4080             switch (get_group0_keyval (&event->key)) {
4081                 case GDK_space:
4082                     if (event->key.state & GDK_BUTTON1_MASK) {
4083                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4084                         stamp_repr(nodepath);
4085                         ret = TRUE;
4086                     }
4087                     break;
4088                 default:
4089                     break;
4090             }
4091             break;
4092         case GDK_ENTER_NOTIFY:
4093             // we use an experimentally determined threshold that seems to work fine
4094             if (Geom::L2(n->pos - knot->pos) < 0.75)
4095                 Inkscape::NodePath::Path::active_node = n;
4096             break;
4097         case GDK_LEAVE_NOTIFY:
4098             // we use an experimentally determined threshold that seems to work fine
4099             if (Geom::L2(n->pos - knot->pos) < 0.75)
4100                 Inkscape::NodePath::Path::active_node = NULL;
4101             break;
4102         default:
4103             break;
4104     }
4106     return ret;
4109 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4110                                  Radial &rme, Radial &rother, gboolean const both)
4112     rme.a += angle;
4113     if ( both
4114          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4115          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4116     {
4117         rother.a += angle;
4118     }
4121 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4122                                         Radial &rme, Radial &rother, gboolean const both)
4124     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4126     gdouble r;
4127     if ( both
4128          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4129          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4130     {
4131         r = MAX(rme.r, rother.r);
4132     } else {
4133         r = rme.r;
4134     }
4136     gdouble const weird_angle = atan2(norm_angle, r);
4137 /* Bulia says norm_angle is just the visible distance that the
4138  * object's end must travel on the screen.  Left as 'angle' for want of
4139  * a better name.*/
4141     rme.a += weird_angle;
4142     if ( both
4143          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4144          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4145     {
4146         rother.a += weird_angle;
4147     }
4150 /**
4151  * Rotate one node.
4152  */
4153 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4155     Inkscape::NodePath::NodeSide *me, *other;
4156     bool both = false;
4158     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4159     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4161     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4162         me = &(n->p);
4163         other = &(n->n);
4164     } else if (!n->p.other) {
4165         me = &(n->n);
4166         other = &(n->p);
4167     } else {
4168         if (which > 0) { // right handle
4169             if (xn > xp) {
4170                 me = &(n->n);
4171                 other = &(n->p);
4172             } else {
4173                 me = &(n->p);
4174                 other = &(n->n);
4175             }
4176         } else if (which < 0){ // left handle
4177             if (xn <= xp) {
4178                 me = &(n->n);
4179                 other = &(n->p);
4180             } else {
4181                 me = &(n->p);
4182                 other = &(n->n);
4183             }
4184         } else { // both handles
4185             me = &(n->n);
4186             other = &(n->p);
4187             both = true;
4188         }
4189     }
4191     Radial rme(me->pos - n->pos);
4192     Radial rother(other->pos - n->pos);
4194     if (screen) {
4195         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4196     } else {
4197         node_rotate_one_internal (*n, angle, rme, rother, both);
4198     }
4200     me->pos = n->pos + Geom::Point(rme);
4202     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4203         other->pos =  n->pos + Geom::Point(rother);
4204     }
4206     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4207     // so here we just move all the knots without emitting move signals, for speed
4208     sp_node_update_handles(n, false);
4211 /**
4212  * Rotate selected nodes.
4213  */
4214 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4216     if (!nodepath || !nodepath->selected) return;
4218     if (g_list_length(nodepath->selected) == 1) {
4219        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4220         node_rotate_one (n, angle, which, screen);
4221     } else {
4222        // rotate as an object:
4224         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4225         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4226         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4227             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4228             box.expandTo (n->pos); // contain all selected nodes
4229         }
4231         gdouble rot;
4232         if (screen) {
4233             gdouble const zoom = nodepath->desktop->current_zoom();
4234             gdouble const zmove = angle / zoom;
4235             gdouble const r = Geom::L2(box.max() - box.midpoint());
4236             rot = atan2(zmove, r);
4237         } else {
4238             rot = angle;
4239         }
4241         Geom::Point rot_center;
4242         if (Inkscape::NodePath::Path::active_node == NULL)
4243             rot_center = box.midpoint();
4244         else
4245             rot_center = Inkscape::NodePath::Path::active_node->pos;
4247         Geom::Matrix t =
4248             Geom::Matrix (Geom::Translate(-rot_center)) *
4249             Geom::Matrix (Geom::Rotate(rot)) *
4250             Geom::Matrix (Geom::Translate(rot_center));
4252         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4253             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4254             n->pos *= t;
4255             n->n.pos *= t;
4256             n->p.pos *= t;
4257             sp_node_update_handles(n, false);
4258         }
4259     }
4261     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4264 /**
4265  * Scale one node.
4266  */
4267 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4269     bool both = false;
4270     Inkscape::NodePath::NodeSide *me, *other;
4272     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4273     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4275     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4276         me = &(n->p);
4277         other = &(n->n);
4278         n->code = NR_CURVETO;
4279     } else if (!n->p.other) {
4280         me = &(n->n);
4281         other = &(n->p);
4282         if (n->n.other)
4283             n->n.other->code = NR_CURVETO;
4284     } else {
4285         if (which > 0) { // right handle
4286             if (xn > xp) {
4287                 me = &(n->n);
4288                 other = &(n->p);
4289                 if (n->n.other)
4290                     n->n.other->code = NR_CURVETO;
4291             } else {
4292                 me = &(n->p);
4293                 other = &(n->n);
4294                 n->code = NR_CURVETO;
4295             }
4296         } else if (which < 0){ // left handle
4297             if (xn <= xp) {
4298                 me = &(n->n);
4299                 other = &(n->p);
4300                 if (n->n.other)
4301                     n->n.other->code = NR_CURVETO;
4302             } else {
4303                 me = &(n->p);
4304                 other = &(n->n);
4305                 n->code = NR_CURVETO;
4306             }
4307         } else { // both handles
4308             me = &(n->n);
4309             other = &(n->p);
4310             both = true;
4311             n->code = NR_CURVETO;
4312             if (n->n.other)
4313                 n->n.other->code = NR_CURVETO;
4314         }
4315     }
4317     Radial rme(me->pos - n->pos);
4318     Radial rother(other->pos - n->pos);
4320     rme.r += grow;
4321     if (rme.r < 0) rme.r = 0;
4322     if (rme.a == HUGE_VAL) {
4323         if (me->other) { // if direction is unknown, initialize it towards the next node
4324             Radial rme_next(me->other->pos - n->pos);
4325             rme.a = rme_next.a;
4326         } else { // if there's no next, initialize to 0
4327             rme.a = 0;
4328         }
4329     }
4330     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4331         rother.r += grow;
4332         if (rother.r < 0) rother.r = 0;
4333         if (rother.a == HUGE_VAL) {
4334             rother.a = rme.a + M_PI;
4335         }
4336     }
4338     me->pos = n->pos + Geom::Point(rme);
4340     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4341         other->pos = n->pos + Geom::Point(rother);
4342     }
4344     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4345     // so here we just move all the knots without emitting move signals, for speed
4346     sp_node_update_handles(n, false);
4349 /**
4350  * Scale selected nodes.
4351  */
4352 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4354     if (!nodepath || !nodepath->selected) return;
4356     if (g_list_length(nodepath->selected) == 1) {
4357         // scale handles of the single selected node
4358         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4359         node_scale_one (n, grow, which);
4360     } else {
4361         // scale nodes as an "object":
4363         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4364         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4365         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4366             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4367             box.expandTo (n->pos); // contain all selected nodes
4368         }
4370         if ( Geom::are_near(box.maxExtent(), 0) ) {
4371             SPEventContext *ec = nodepath->desktop->event_context;
4372             if (!ec) return;
4373             Inkscape::MessageContext *mc = get_message_context(ec);
4374             if (!mc) return;
4375             mc->setF(Inkscape::WARNING_MESSAGE,
4376                              _("Cannot scale nodes when all are at the same location."));
4377             return;
4378         }
4379         double scale = (box.maxExtent() + grow)/box.maxExtent();
4382         Geom::Point scale_center;
4383         if (Inkscape::NodePath::Path::active_node == NULL)
4384             scale_center = box.midpoint();
4385         else
4386             scale_center = Inkscape::NodePath::Path::active_node->pos;
4388         Geom::Matrix t =
4389             Geom::Translate(-scale_center) *
4390             Geom::Scale(scale, scale) *
4391             Geom::Translate(scale_center);
4393         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4394             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4395             n->pos *= t;
4396             n->n.pos *= t;
4397             n->p.pos *= t;
4398             sp_node_update_handles(n, false);
4399         }
4400     }
4402     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4405 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4407     if (!nodepath) return;
4408     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4411 /**
4412  * Flip selected nodes horizontally/vertically.
4413  */
4414 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4416     if (!nodepath || !nodepath->selected) return;
4418     if (g_list_length(nodepath->selected) == 1 && !center) {
4419         // flip handles of the single selected node
4420         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4421         double temp = n->p.pos[axis];
4422         n->p.pos[axis] = n->n.pos[axis];
4423         n->n.pos[axis] = temp;
4424         sp_node_update_handles(n, false);
4425     } else {
4426         // scale nodes as an "object":
4428         Geom::Rect box = sp_node_selected_bbox (nodepath);
4429         if (!center) {
4430             center = box.midpoint();
4431         }
4432         Geom::Matrix t =
4433             Geom::Matrix (Geom::Translate(- *center)) *
4434             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4435             Geom::Matrix (Geom::Translate(*center));
4437         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4438             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4439             n->pos *= t;
4440             n->n.pos *= t;
4441             n->p.pos *= t;
4442             sp_node_update_handles(n, false);
4443         }
4444     }
4446     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4449 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4451     g_assert (nodepath->selected);
4453     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4454     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4455     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4456         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4457         box.expandTo (n->pos); // contain all selected nodes
4458     }
4459     return box;
4462 //-----------------------------------------------
4463 /**
4464  * Return new subpath under given nodepath.
4465  */
4466 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4468     g_assert(nodepath);
4469     g_assert(nodepath->desktop);
4471    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4473     s->nodepath = nodepath;
4474     s->closed = FALSE;
4475     s->nodes = NULL;
4476     s->first = NULL;
4477     s->last = NULL;
4479     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4480     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4481     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4483     return s;
4486 /**
4487  * Destroy nodes in subpath, then subpath itself.
4488  */
4489 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4491     g_assert(subpath);
4492     g_assert(subpath->nodepath);
4493     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4495     while (subpath->nodes) {
4496         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4497     }
4499     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4501     g_free(subpath);
4504 /**
4505  * Link head to tail in subpath.
4506  */
4507 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4509     g_assert(!sp->closed);
4510     g_assert(sp->last != sp->first);
4511     g_assert(sp->first->code == NR_MOVETO);
4513     sp->closed = TRUE;
4515     //Link the head to the tail
4516     sp->first->p.other = sp->last;
4517     sp->last->n.other  = sp->first;
4518     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4519     sp->first          = sp->last;
4521     //Remove the extra end node
4522     sp_nodepath_node_destroy(sp->last->n.other);
4525 /**
4526  * Open closed (loopy) subpath at node.
4527  */
4528 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4530     g_assert(sp->closed);
4531     g_assert(n->subpath == sp);
4532     g_assert(sp->first == sp->last);
4534     /* We create new startpoint, current node will become last one */
4536    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4537                                                 &n->pos, &n->pos, &n->n.pos);
4540     sp->closed        = FALSE;
4542     //Unlink to make a head and tail
4543     sp->first         = new_path;
4544     sp->last          = n;
4545     n->n.other        = NULL;
4546     new_path->p.other = NULL;
4549 /**
4550  * Return new node in subpath with given properties.
4551  * \param pos Position of node.
4552  * \param ppos Handle position in previous direction
4553  * \param npos Handle position in previous direction
4554  */
4555 Inkscape::NodePath::Node *
4556 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)
4558     g_assert(sp);
4559     g_assert(sp->nodepath);
4560     g_assert(sp->nodepath->desktop);
4562     if (nodechunk == NULL)
4563         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4565     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4567     n->subpath  = sp;
4569     if (type != Inkscape::NodePath::NODE_NONE) {
4570         // use the type from sodipodi:nodetypes
4571         n->type = type;
4572     } else {
4573         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4574             // points are (almost) collinear
4575             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4576                 // endnode, or a node with a retracted handle
4577                 n->type = Inkscape::NodePath::NODE_CUSP;
4578             } else {
4579                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4580             }
4581         } else {
4582             n->type = Inkscape::NodePath::NODE_CUSP;
4583         }
4584     }
4586     n->code     = code;
4587     n->selected = FALSE;
4588     n->pos      = *pos;
4589     n->p.pos    = *ppos;
4590     n->n.pos    = *npos;
4591     
4592     n->dragging_out = NULL;
4594     Inkscape::NodePath::Node *prev;
4595     if (next) {
4596         //g_assert(g_list_find(sp->nodes, next));
4597         prev = next->p.other;
4598     } else {
4599         prev = sp->last;
4600     }
4602     if (prev)
4603         prev->n.other = n;
4604     else
4605         sp->first = n;
4607     if (next)
4608         next->p.other = n;
4609     else
4610         sp->last = n;
4612     n->p.other = prev;
4613     n->n.other = next;
4615     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"));
4616     sp_knot_set_position(n->knot, *pos, 0);
4618     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4619     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4620     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4622     sp_nodepath_update_node_knot(n);
4624     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4625     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4626     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4627     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4628     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4629     sp_knot_show(n->knot);
4631     // We only create handle knots and lines on demand
4632     n->p.knot = NULL;
4633     n->p.line = NULL;
4634     n->n.knot = NULL;
4635     n->n.line = NULL;
4637     sp->nodes = g_list_prepend(sp->nodes, n);
4639     return n;
4642 /**
4643  * Destroy node and its knots, link neighbors in subpath.
4644  */
4645 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4647     g_assert(node);
4648     g_assert(node->subpath);
4649     g_assert(SP_IS_KNOT(node->knot));
4651    Inkscape::NodePath::SubPath *sp = node->subpath;
4653     if (node->selected) { // first, deselect
4654         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4655         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4656     }
4658     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4660     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4661     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4662     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4663     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4664     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4665     g_object_unref(G_OBJECT(node->knot));
4667     if (node->p.knot) {
4668         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4669         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4670         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4671         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4672         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4673         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4674         g_object_unref(G_OBJECT(node->p.knot));
4675         node->p.knot = NULL;
4676     }
4678     if (node->n.knot) {
4679         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4680         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4681         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4682         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4683         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4684         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4685         g_object_unref(G_OBJECT(node->n.knot));
4686         node->n.knot = NULL;
4687     }
4689     if (node->p.line)
4690         gtk_object_destroy(GTK_OBJECT(node->p.line));
4691     if (node->n.line)
4692         gtk_object_destroy(GTK_OBJECT(node->n.line));
4694     if (sp->nodes) { // there are others nodes on the subpath
4695         if (sp->closed) {
4696             if (sp->first == node) {
4697                 g_assert(sp->last == node);
4698                 sp->first = node->n.other;
4699                 sp->last = sp->first;
4700             }
4701             node->p.other->n.other = node->n.other;
4702             node->n.other->p.other = node->p.other;
4703         } else {
4704             if (sp->first == node) {
4705                 sp->first = node->n.other;
4706                 sp->first->code = NR_MOVETO;
4707             }
4708             if (sp->last == node) sp->last = node->p.other;
4709             if (node->p.other) node->p.other->n.other = node->n.other;
4710             if (node->n.other) node->n.other->p.other = node->p.other;
4711         }
4712     } else { // this was the last node on subpath
4713         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4714     }
4716     g_mem_chunk_free(nodechunk, node);
4719 /**
4720  * Returns one of the node's two sides.
4721  * \param which Indicates which side.
4722  * \return Pointer to previous node side if which==-1, next if which==1.
4723  */
4724 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4726     g_assert(node);
4727     Inkscape::NodePath::NodeSide * result = 0;
4728     switch (which) {
4729         case -1:
4730             result = &node->p;
4731             break;
4732         case 1:
4733             result = &node->n;
4734             break;
4735         default:
4736             g_assert_not_reached();
4737     }
4739     return result;
4742 /**
4743  * Return the other side of the node, given one of its sides.
4744  */
4745 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4747     g_assert(node);
4748     Inkscape::NodePath::NodeSide *result = 0;
4750     if (me == &node->p) {
4751         result = &node->n;
4752     } else if (me == &node->n) {
4753         result = &node->p;
4754     } else {
4755         g_assert_not_reached();
4756     }
4758     return result;
4761 /**
4762  * Return NRPathcode on the given side of the node.
4763  */
4764 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4766     g_assert(node);
4768     NRPathcode result = NR_END;
4769     if (me == &node->p) {
4770         if (node->p.other) {
4771             result = (NRPathcode)node->code;
4772         } else {
4773             result = NR_MOVETO;
4774         }
4775     } else if (me == &node->n) {
4776         if (node->n.other) {
4777             result = (NRPathcode)node->n.other->code;
4778         } else {
4779             result = NR_MOVETO;
4780         }
4781     } else {
4782         g_assert_not_reached();
4783     }
4785     return result;
4788 /**
4789  * Return node with the given index
4790  */
4791 Inkscape::NodePath::Node *
4792 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4794     Inkscape::NodePath::Node *e = NULL;
4796     if (!nodepath) {
4797         return e;
4798     }
4800     //find segment
4801     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4803         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4804         int n = g_list_length(sp->nodes);
4805         if (sp->closed) {
4806             n++;
4807         }
4809         //if the piece belongs to this subpath grab it
4810         //otherwise move onto the next subpath
4811         if (index < n) {
4812             e = sp->first;
4813             for (int i = 0; i < index; ++i) {
4814                 e = e->n.other;
4815             }
4816             break;
4817         } else {
4818             if (sp->closed) {
4819                 index -= (n+1);
4820             } else {
4821                 index -= n;
4822             }
4823         }
4824     }
4826     return e;
4829 /**
4830  * Returns plain text meaning of node type.
4831  */
4832 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4834     unsigned retracted = 0;
4835     bool endnode = false;
4837     for (int which = -1; which <= 1; which += 2) {
4838         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4839         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4840             retracted ++;
4841         if (!side->other)
4842             endnode = true;
4843     }
4845     if (retracted == 0) {
4846         if (endnode) {
4847                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4848                 return _("end node");
4849         } else {
4850             switch (node->type) {
4851                 case Inkscape::NodePath::NODE_CUSP:
4852                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4853                     return _("cusp");
4854                 case Inkscape::NodePath::NODE_SMOOTH:
4855                     // TRANSLATORS: "smooth" is an adjective here
4856                     return _("smooth");
4857                 case Inkscape::NodePath::NODE_AUTO:
4858                     return _("auto");
4859                 case Inkscape::NodePath::NODE_SYMM:
4860                     return _("symmetric");
4861             }
4862         }
4863     } else if (retracted == 1) {
4864         if (endnode) {
4865             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4866             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4867         } else {
4868             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4869         }
4870     } else {
4871         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4872     }
4874     return NULL;
4877 /**
4878  * Handles content of statusbar as long as node tool is active.
4879  */
4880 void
4881 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4883     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");
4884     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4886     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4887     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4888     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4889     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4891     SPDesktop *desktop = NULL;
4892     if (nodepath) {
4893         desktop = nodepath->desktop;
4894     } else {
4895         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4896     }
4898     SPEventContext *ec = desktop->event_context;
4899     if (!ec) return;
4901     Inkscape::MessageContext *mc = get_message_context(ec);
4902     if (!mc) return;
4904     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4906     if (selected_nodes == 0) {
4907         Inkscape::Selection *sel = desktop->selection;
4908         if (!sel || sel->isEmpty()) {
4909             mc->setF(Inkscape::NORMAL_MESSAGE,
4910                      _("Select a single object to edit its nodes or handles."));
4911         } else {
4912             if (nodepath) {
4913             mc->setF(Inkscape::NORMAL_MESSAGE,
4914                      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.",
4915                               "<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.",
4916                               total_nodes),
4917                      total_nodes);
4918             } else {
4919                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4920                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4921                 } else {
4922                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4923                 }
4924             }
4925         }
4926     } else if (nodepath && selected_nodes == 1) {
4927         mc->setF(Inkscape::NORMAL_MESSAGE,
4928                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4929                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4930                           total_nodes),
4931                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4932     } else {
4933         if (selected_subpaths > 1) {
4934             mc->setF(Inkscape::NORMAL_MESSAGE,
4935                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4936                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4937                               total_nodes),
4938                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4939         } else {
4940             mc->setF(Inkscape::NORMAL_MESSAGE,
4941                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4942                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4943                               total_nodes),
4944                      selected_nodes, total_nodes, when_selected);
4945         }
4946     }
4949 /*
4950  * returns a *copy* of the curve of that object.
4951  */
4952 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4953     if (!object)
4954         return NULL;
4956     SPCurve *curve = NULL;
4957     if (SP_IS_PATH(object)) {
4958         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4959         curve = curve_new->copy();
4960     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4961         const gchar *svgd = object->repr->attribute(key);
4962         if (svgd) {
4963             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4964             SPCurve *curve_new = new SPCurve(pv);
4965             if (curve_new) {
4966                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4967             }
4968         }
4969     }
4971     return curve;
4974 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4975     if (!np || !np->object || !curve)
4976         return;
4978     if (SP_IS_PATH(np->object)) {
4979         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4980             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4981         } else {
4982             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4983         }
4984     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4985         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
4986         if (lpe) {
4987             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
4988             if (pathparam) {
4989                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4990                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4991             }
4992         }
4993     }
4996 /*
4997 SPCanvasItem *
4998 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4999     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
5001 */
5003 /*
5004 SPCanvasItem *
5005 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
5006     SPCurve *flash_curve = curve->copy();
5007     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
5008     flash_curve->transform(i2d);
5009     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5010     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5011     // unless we also flash the nodes...
5012     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5013     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5014     sp_canvas_item_show(canvasitem);
5015     flash_curve->unref();
5016     return canvasitem;
5019 SPCanvasItem *
5020 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
5021     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5022     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
5023                                            prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff));
5025 */
5027 SPCanvasItem *
5028 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
5029     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
5030     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
5031     flash_curve->transform(i2d);
5032     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5033     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5034     // unless we also flash the nodes...
5035     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5036     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
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 // TODO: Merge this with sp_nodepath_make_helper_item()!
5045 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5046     np->show_helperpath = show;
5048     if (show) {
5049         SPCurve *helper_curve = np->curve->copy();
5050         helper_curve->transform(np->i2d);
5051         if (!np->helper_path) {
5052             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5054             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5055             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);
5056             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5057             sp_canvas_item_move_to_z(np->helper_path, 0);
5058             sp_canvas_item_show(np->helper_path);
5059         } else {
5060             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5061         }
5062         helper_curve->unref();
5063     } else {
5064         if (np->helper_path) {
5065             GtkObject *temp = np->helper_path;
5066             np->helper_path = NULL;
5067             gtk_object_destroy(temp);
5068         }
5069     }
5072 /* sp_nodepath_make_straight_path:
5073  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5074  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5075  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5076  */
5077 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5078     np->straight_path = true;
5079     np->show_handles = false;
5080     g_message("add code to make the path straight.");
5081     // do sp_nodepath_convert_node_type on all nodes?
5082     // coding tip: search for this text : "Make selected segments lines"
5085 /*
5086   Local Variables:
5087   mode:c++
5088   c-file-style:"stroustrup"
5089   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5090   indent-tabs-mode:nil
5091   fill-column:99
5092   End:
5093 */
5094 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :