Code

Merge from fe-moved
[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     }
318     // we need to update item's transform from the repr here,
319     // because they may be out of sync when we respond
320     // to a change in repr by regenerating nodepath     --bb
321     sp_object_read_attr(SP_OBJECT(np->item), "transform");
323     np->i2d  = sp_item_i2d_affine(np->item);
324     np->d2i  = np->i2d.inverse();
326     np->repr = repr;
327     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)
328         np->repr_key = g_strdup(repr_key_in);
329         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
330         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe();
331         if (!lpe) {
332             g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!");
333             delete np;
334         }
335         Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in);
336         if (lpeparam) {
337             lpeparam->param_setup_nodepath(np);
338         }
339     } else {
340         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
341         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
342             np->repr_key = g_strdup("inkscape:original-d");
344             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
345             if (lpe) {
346                 lpe->setup_nodepath(np);
347             }
348         } else {
349             np->repr_key = g_strdup("d");
350         }
351     }
353     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
354      * So for example a closed rectangle has a nodetypestring of length 5.
355      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
356     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
357     np->curve->set_pathvector(pathv_sanitized);
358     guint length = np->curve->get_segment_count();
359     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
360         length += pit->empty() ? 0 : 1;
361     }
363     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
364     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
366     // create the subpath(s) from the bpath
367     subpaths_from_pathvector(np, pathv_sanitized, typestr);
369     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
370     np->subpaths = g_list_reverse(np->subpaths);
372     delete[] typestr;
373     curve->unref();
375     // Draw helper curve
376     if (np->show_helperpath) {
377         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
378     }
380     sp_nodepath_create_helperpaths(np);
382     return np;
385 /**
386  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
387  */
388 Inkscape::NodePath::Path::~Path() {
389     while (this->subpaths) {
390         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data);
391     }
393     //Inform the ShapeEditor that made me, if any, that I am gone.
394     if (this->shape_editor)
395         this->shape_editor->nodepath_destroyed();
397     g_assert(!this->selected);
399     if (this->helper_path) {
400         GtkObject *temp = this->helper_path;
401         this->helper_path = NULL;
402         gtk_object_destroy(temp);
403     }
404     if (this->curve) {
405         this->curve->unref();
406         this->curve = NULL;
407     }
409     if (this->repr_key) {
410         g_free(this->repr_key);
411         this->repr_key = NULL;
412     }
413     if (this->repr_nodetypes_key) {
414         g_free(this->repr_nodetypes_key);
415         this->repr_nodetypes_key = NULL;
416     }
418     sp_nodepath_destroy_helperpaths(this);
420     this->desktop = NULL;
423 /**
424  *  Return the node count of a given NodeSubPath.
425  */
426 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
428     int nodeCount = 0;
430     if (subpath) {
431         nodeCount = g_list_length(subpath->nodes);
432     }
434     return nodeCount;
437 /**
438  *  Return the node count of a given NodePath.
439  */
440 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
442     gint nodeCount = 0;
443     if (np) {
444         for (GList *item = np->subpaths ; item ; item=item->next) {
445             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
446             nodeCount += g_list_length(subpath->nodes);
447         }
448     }
449     return nodeCount;
452 /**
453  *  Return the subpath count of a given NodePath.
454  */
455 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
457     gint nodeCount = 0;
458     if (np) {
459         nodeCount = g_list_length(np->subpaths);
460     }
461     return nodeCount;
464 /**
465  *  Return the selected node count of a given NodePath.
466  */
467 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
469     gint nodeCount = 0;
470     if (np) {
471         nodeCount = g_list_length(np->selected);
472     }
473     return nodeCount;
476 /**
477  *  Return the number of subpaths where nodes are selected in a given NodePath.
478  */
479 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
481     gint nodeCount = 0;
482     if (np && np->selected) {
483         if (!np->selected->next) {
484             nodeCount = 1;
485         } else {
486             for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
487                 Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
488                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
489                     Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
490                     if (node->selected) {
491                         nodeCount++;
492                         break;
493                     }
494                 }
495             }
496         }
497     }
498     return nodeCount;
501 /**
502  * Clean up a nodepath after editing.
503  *
504  * Currently we are deleting trivial subpaths.
505  */
506 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
508     GList *badSubPaths = NULL;
510     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
511     for (GList *l = nodepath->subpaths; l ; l=l->next) {
512        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
513        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
514             badSubPaths = g_list_append(badSubPaths, sp);
515     }
517     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
518     //also removes the subpath from nodepath->subpaths
519     for (GList *l = badSubPaths; l ; l=l->next) {
520        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
521         sp_nodepath_subpath_destroy(sp);
522     }
524     g_list_free(badSubPaths);
527 /**
528  * Create new nodepaths from pathvector, make it subpaths of np.
529  * \param t The node type array.
530  */
531 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
533     guint i = 0;  // index into node type array
534     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
535         if (pit->empty())
536             continue;  // don't add single knot paths
538         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
540         Geom::Point ppos = pit->initialPoint() * np->i2d;
541         NRPathcode pcode = NR_MOVETO;
543         /* 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)*/
544         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
545             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
546                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
547                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
548             {
549                 Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
550                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
552                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
553                 pcode = NR_LINETO;
554             }
555             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
556                 std::vector<Geom::Point> points = cubic_bezier->points();
557                 Geom::Point pos = points[0] * (Geom::Matrix)np->i2d;
558                 Geom::Point npos = points[1] * (Geom::Matrix)np->i2d;
559                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
561                 ppos = points[2] * (Geom::Matrix)np->i2d;
562                 pcode = NR_CURVETO;
563             }
564         }
566         if (pit->closed()) {
567             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
568             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
569              * If the length is zero, don't add it to the nodepath. */
570             Geom::Curve const &closing_seg = pit->back_closed();
571             // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289)
572             if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) {
573                 Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
574                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
575             }
577             sp_nodepath_subpath_close(sp);
578         }
579     }
582 /**
583  * Convert from sodipodi:nodetypes to new style type array.
584  */
585 static
586 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
588     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
590     guint pos = 0;
592     if (types) {
593         for (guint i = 0; types[i] && ( i < length ); i++) {
594             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
595             if (types[i] != '\0') {
596                 switch (types[i]) {
597                     case 's':
598                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
599                         break;
600                     case 'a':
601                         typestr[pos++] =Inkscape::NodePath::NODE_AUTO;
602                         break;
603                     case 'z':
604                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
605                         break;
606                     case 'c':
607                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
608                         break;
609                     default:
610                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
611                         break;
612                 }
613             }
614         }
615     }
617     while (pos < length) {
618         typestr[pos++] = Inkscape::NodePath::NODE_NONE;
619     }
621     return typestr;
624 /**
625  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
626  * updated but repr is not (for speed). Used during curve and node drag.
627  */
628 static void update_object(Inkscape::NodePath::Path *np)
630     g_assert(np);
632     np->curve->unref();
633     np->curve = create_curve(np);
635     sp_nodepath_set_curve(np, np->curve);
637     if (np->show_helperpath) {
638         SPCurve * helper_curve = np->curve->copy();
639         helper_curve->transform(np->i2d);
640         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
641         helper_curve->unref();
642     }
644     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
645     //sp_nodepath_update_helperpaths(np);
647     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
648     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
649     np->shape_editor->update_knotholder();
652 /**
653  * Update XML path node with data from path object.
654  */
655 static void update_repr_internal(Inkscape::NodePath::Path *np)
657     g_assert(np);
659     Inkscape::XML::Node *repr = np->object->repr;
661     np->curve->unref();
662     np->curve = create_curve(np);
664     gchar *typestr = create_typestr(np);
665     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
667     // determine if path has an effect applied and write to correct "d" attribute.
668     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
669         np->local_change++;
670         repr->setAttribute(np->repr_key, svgpath);
671     }
673     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
674         np->local_change++;
675         repr->setAttribute(np->repr_nodetypes_key, typestr);
676     }
678     g_free(svgpath);
679     g_free(typestr);
681     if (np->show_helperpath) {
682         SPCurve * helper_curve = np->curve->copy();
683         helper_curve->transform(np->i2d);
684         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
685         helper_curve->unref();
686     }
688     // TODO: do we need this call here? after all, update_object() should have been called just before
689     //sp_nodepath_update_helperpaths(np);
692 /**
693  * Update XML path node with data from path object, commit changes forever.
694  */
695 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
697     //fixme: np can be NULL, so check before proceeding
698     g_return_if_fail(np != NULL);
700     update_repr_internal(np);
701     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
703     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
704                      annotation);
707 /**
708  * Update XML path node with data from path object, commit changes with undo.
709  */
710 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
712     update_repr_internal(np);
713     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
714                            annotation);
717 /**
718  * Make duplicate of path, replace corresponding XML node in tree, commit.
719  */
720 static void stamp_repr(Inkscape::NodePath::Path *np)
722     g_assert(np);
724     Inkscape::XML::Node *old_repr = np->object->repr;
725     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
727     // remember the position of the item
728     gint pos = old_repr->position();
729     // remember parent
730     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
732     SPCurve *curve = create_curve(np);
733     gchar *typestr = create_typestr(np);
735     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
737     new_repr->setAttribute(np->repr_key, svgpath);
738     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
740     // add the new repr to the parent
741     parent->appendChild(new_repr);
742     // move to the saved position
743     new_repr->setPosition(pos > 0 ? pos : 0);
745     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
746                      _("Stamp"));
748     Inkscape::GC::release(new_repr);
749     g_free(svgpath);
750     g_free(typestr);
751     curve->unref();
754 /**
755  * Create curve from path.
756  */
757 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
759     SPCurve *curve = new SPCurve();
761     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
762        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
763        curve->moveto(sp->first->pos * np->d2i);
764        Inkscape::NodePath::Node *n = sp->first->n.other;
765         while (n) {
766             Geom::Point const end_pt = n->pos * np->d2i;
767             if (!IS_FINITE(n->pos[0]) || !IS_FINITE(n->pos[1])){
768                 g_message("niet finite");
769             }
770             switch (n->code) {
771                 case NR_LINETO:
772                     curve->lineto(end_pt);
773                     break;
774                 case NR_CURVETO:
775                     curve->curveto(n->p.other->n.pos * np->d2i,
776                                      n->p.pos * np->d2i,
777                                      end_pt);
778                     break;
779                 default:
780                     g_assert_not_reached();
781                     break;
782             }
783             if (n != sp->last) {
784                 n = n->n.other;
785             } else {
786                 n = NULL;
787             }
788         }
789         if (sp->closed) {
790             curve->closepath();
791         }
792     }
794     return curve;
797 /**
798  * Convert path type string to sodipodi:nodetypes style.
799  */
800 static gchar *create_typestr(Inkscape::NodePath::Path *np)
802     gchar *typestr = g_new(gchar, 32);
803     gint len = 32;
804     gint pos = 0;
806     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
807        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
809         if (pos >= len) {
810             typestr = g_renew(gchar, typestr, len + 32);
811             len += 32;
812         }
814         typestr[pos++] = 'c';
816        Inkscape::NodePath::Node *n;
817         n = sp->first->n.other;
818         while (n) {
819             gchar code;
821             switch (n->type) {
822                 case Inkscape::NodePath::NODE_CUSP:
823                     code = 'c';
824                     break;
825                 case Inkscape::NodePath::NODE_SMOOTH:
826                     code = 's';
827                     break;
828                 case Inkscape::NodePath::NODE_AUTO:
829                     code = 'a';
830                     break;
831                 case Inkscape::NodePath::NODE_SYMM:
832                     code = 'z';
833                     break;
834                 default:
835                     g_assert_not_reached();
836                     code = '\0';
837                     break;
838             }
840             if (pos >= len) {
841                 typestr = g_renew(gchar, typestr, len + 32);
842                 len += 32;
843             }
845             typestr[pos++] = code;
847             if (n != sp->last) {
848                 n = n->n.other;
849             } else {
850                 n = NULL;
851             }
852         }
853     }
855     if (pos >= len) {
856         typestr = g_renew(gchar, typestr, len + 1);
857         len += 1;
858     }
860     typestr[pos++] = '\0';
862     return typestr;
865 // Returns different message contexts depending on the current context. This function should only
866 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
867 // other cases.
868 static Inkscape::MessageContext *
869 get_message_context(SPEventContext *ec)
871     Inkscape::MessageContext *mc = 0;
873     if (SP_IS_NODE_CONTEXT(ec)) {
874         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
875     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
876         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
877     } else {
878         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
879     }
881     return mc;
884 /**
885  \brief Fills node and handle positions for three nodes, splitting line
886   marked by end at distance t.
887  */
888 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
890     g_assert(new_path != NULL);
891     g_assert(end      != NULL);
893     g_assert(end->p.other == new_path);
894    Inkscape::NodePath::Node *start = new_path->p.other;
895     g_assert(start);
897     if (end->code == NR_LINETO) {
898         new_path->type =Inkscape::NodePath::NODE_CUSP;
899         new_path->code = NR_LINETO;
900         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
901     } else {
902         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
903         new_path->code = NR_CURVETO;
904         gdouble s      = 1 - t;
905         for (int dim = 0; dim < 2; dim++) {
906             Geom::Coord const f000 = start->pos[dim];
907             Geom::Coord const f001 = start->n.pos[dim];
908             Geom::Coord const f011 = end->p.pos[dim];
909             Geom::Coord const f111 = end->pos[dim];
910             Geom::Coord const f00t = s * f000 + t * f001;
911             Geom::Coord const f01t = s * f001 + t * f011;
912             Geom::Coord const f11t = s * f011 + t * f111;
913             Geom::Coord const f0tt = s * f00t + t * f01t;
914             Geom::Coord const f1tt = s * f01t + t * f11t;
915             Geom::Coord const fttt = s * f0tt + t * f1tt;
916             start->n.pos[dim]    = f00t;
917             new_path->p.pos[dim] = f0tt;
918             new_path->pos[dim]   = fttt;
919             new_path->n.pos[dim] = f1tt;
920             end->p.pos[dim]      = f11t;
921         }
922     }
925 /**
926  * Adds new node on direct line between two nodes, activates handles of all
927  * three nodes.
928  */
929 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
931     g_assert(end);
932     g_assert(end->subpath);
933     g_assert(g_list_find(end->subpath->nodes, end));
935    Inkscape::NodePath::Node *start = end->p.other;
936     g_assert( start->n.other == end );
937    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
938                                                end,
939                                                (NRPathcode)end->code == NR_LINETO?
940                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
941                                                (NRPathcode)end->code,
942                                                &start->pos, &start->pos, &start->n.pos);
943     sp_nodepath_line_midpoint(newnode, end, t);
945     sp_node_adjust_handles(start);
946     sp_node_update_handles(start);
947     sp_node_update_handles(newnode);
948     sp_node_adjust_handles(end);
949     sp_node_update_handles(end);
951     return newnode;
954 /**
955 \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
956 */
957 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
959     g_assert(node);
960     g_assert(node->subpath);
961     g_assert(g_list_find(node->subpath->nodes, node));
963     Inkscape::NodePath::Node* result = 0;
964     Inkscape::NodePath::SubPath *sp = node->subpath;
965     Inkscape::NodePath::Path *np    = sp->nodepath;
967     if (sp->closed) {
968         sp_nodepath_subpath_open(sp, node);
969         result = sp->first;
970     } else if ( (node == sp->first) || (node == sp->last ) ){
971         // no break for end nodes
972         result = 0;
973     } else {
974         // create a new subpath
975         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
977         // duplicate the break node as start of the new subpath
978         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
979                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
980                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
982         // attach rest of curve to new node
983         g_assert(node->n.other);
984         newnode->n.other = node->n.other; node->n.other = NULL;
985         newnode->n.other->p.other = newnode;
986         newsubpath->last = sp->last;
987         sp->last = node;
988         node = newnode;
989         while (node->n.other) {
990             node = node->n.other;
991             node->subpath = newsubpath;
992             sp->nodes = g_list_remove(sp->nodes, node);
993             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
994         }
997         result = newnode;
998     }
999     return result;
1002 /**
1003  * Duplicate node and connect to neighbours.
1004  */
1005 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
1007     g_assert(node);
1008     g_assert(node->subpath);
1009     g_assert(g_list_find(node->subpath->nodes, node));
1011    Inkscape::NodePath::SubPath *sp = node->subpath;
1013     NRPathcode code = (NRPathcode) node->code;
1014     if (code == NR_MOVETO) { // if node is the endnode,
1015         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1016     }
1018     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1020     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1021         return node;
1022     } else {
1023         return newnode; // otherwise select the newly created node
1024     }
1027 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1029     node->p.pos = (node->pos + (node->pos - node->n.pos));
1032 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1034     node->n.pos = (node->pos + (node->pos - node->p.pos));
1037 /**
1038  * Change line type at node, with side effects on neighbours.
1039  */
1040 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1042     g_assert(end);
1043     g_assert(end->subpath);
1044     g_assert(end->p.other);
1046     if (end->code != static_cast<guint>(code) ) {
1047         Inkscape::NodePath::Node *start = end->p.other;
1049         end->code = code;
1051         if (code == NR_LINETO) {
1052             if (start->code == NR_LINETO) {
1053                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1054             }
1055             if (end->n.other) {
1056                 if (end->n.other->code == NR_LINETO) {
1057                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1058                 }
1059             }
1061             if (start->type == Inkscape::NodePath::NODE_AUTO)
1062                 start->type = Inkscape::NodePath::NODE_SMOOTH;
1063             if (end->type == Inkscape::NodePath::NODE_AUTO)
1064                 end->type = Inkscape::NodePath::NODE_SMOOTH;
1065     
1066             sp_node_adjust_handle(start, -1);
1067             sp_node_adjust_handle(end, 1);
1069         } else {
1070             Geom::Point delta = end->pos - start->pos;
1071             start->n.pos = start->pos + delta / 3;
1072             end->p.pos = end->pos - delta / 3;
1073             sp_node_adjust_handle(start, 1);
1074             sp_node_adjust_handle(end, -1);
1075         }
1077         sp_node_update_handles(start);
1078         sp_node_update_handles(end);
1079     }
1082 static void
1083 sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
1085     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1086         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1087         node->knot->setSize (node->selected? 11 : 9);
1088         sp_knot_update_ctrl(node->knot);
1089     } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
1090         node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1091         node->knot->setSize (node->selected? 11 : 9);
1092         sp_knot_update_ctrl(node->knot);
1093     } else {
1094         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1095         node->knot->setSize (node->selected? 9 : 7);
1096         sp_knot_update_ctrl(node->knot);
1097     }
1101 /**
1102  * Change node type, and its handles accordingly.
1103  */
1104 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1106     g_assert(node);
1107     g_assert(node->subpath);
1109     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1110         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1111             type =Inkscape::NodePath::NODE_CUSP;
1112         }
1113     }
1115     node->type = type;
1117     sp_nodepath_update_node_knot(node);
1119     // if one of handles is mouseovered, preserve its position
1120     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1121         sp_node_adjust_handle(node, 1);
1122     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1123         sp_node_adjust_handle(node, -1);
1124     } else {
1125         sp_node_adjust_handles(node);
1126     }
1128     sp_node_update_handles(node);
1130     sp_nodepath_update_statusbar(node->subpath->nodepath);
1132     return node;
1135 bool
1136 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1138 // TODO clean up multiple returns
1139         Inkscape::NodePath::Node *othernode = side->other;
1140         if (!othernode)
1141             return false;
1142         NRPathcode const code = sp_node_path_code_from_side(node, side);
1143         if (code == NR_LINETO)
1144             return true;
1145         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1146         if (&node->p == side) {
1147             other_to_me = &othernode->n;
1148         } else if (&node->n == side) {
1149             other_to_me = &othernode->p;
1150         } 
1151         if (!other_to_me)
1152             return false;
1153         bool is_line = 
1154              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1155               Geom::L2(node->pos - side->pos) < 1e-6);
1156         return is_line;
1159 /**
1160  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1161  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1162  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1163  * If already cusp and set to cusp, retracts handles.
1164 */
1165 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1167     if (type == Inkscape::NodePath::NODE_AUTO) {
1168         if (node->p.other != NULL)
1169             node->code = NR_CURVETO;
1170         if (node->n.other != NULL)
1171             node->n.other->code = NR_CURVETO;
1172     }
1174     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1176 /* 
1177   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1178  
1179         if (two_handles) {
1180             // do nothing, adjust_handles called via set_node_type will line them up
1181         } else if (one_handle) {
1182             if (opposite_to_handle_is_line) {
1183                 if (lined_up) {
1184                     // already half-smooth; pull opposite handle too making it fully smooth
1185                 } else {
1186                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1187                 }
1188             } else {
1189                 // pull opposite handle in line with the existing one
1190             }
1191         } else if (no_handles) {
1192             if (both_segments_are_lines OR both_segments_are_curves) {
1193                 //pull both handles
1194             } else {
1195                 // pull the handle opposite to line segment, making node half-smooth
1196             }
1197         }
1198 */
1199         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1200         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1201         bool p_is_line = sp_node_side_is_line(node, &node->p);
1202         bool n_is_line = sp_node_side_is_line(node, &node->n);
1204         if (p_has_handle && n_has_handle) {
1205             // do nothing, adjust_handles will line them up
1206         } else if (p_has_handle || n_has_handle) {
1207             if (p_has_handle && n_is_line) {
1208                 Radial line (node->n.other->pos - node->pos);
1209                 Radial handle (node->pos - node->p.pos);
1210                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1211                     // already half-smooth; pull opposite handle too making it fully smooth
1212                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1213                 } else {
1214                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1215                 }
1216             } else if (n_has_handle && p_is_line) {
1217                 Radial line (node->p.other->pos - node->pos);
1218                 Radial handle (node->pos - node->n.pos);
1219                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1220                     // already half-smooth; pull opposite handle too making it fully smooth
1221                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1222                 } else {
1223                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1224                 }
1225             } else if (p_has_handle && node->n.other) {
1226                 // pull n handle
1227                 node->n.other->code = NR_CURVETO;
1228                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1229                     Geom::L2(node->p.pos - node->pos) :
1230                     Geom::L2(node->n.other->pos - node->pos) / 3;
1231                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1232             } else if (n_has_handle && node->p.other) {
1233                 // pull p handle
1234                 node->code = NR_CURVETO;
1235                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1236                     Geom::L2(node->n.pos - node->pos) :
1237                     Geom::L2(node->p.other->pos - node->pos) / 3;
1238                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1239             }
1240         } else if (!p_has_handle && !n_has_handle) {
1241             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1242                 // no handles, but both segments are either lnes or curves:
1243                 //pull both handles
1245                 // convert both to curves:
1246                 node->code = NR_CURVETO;
1247                 node->n.other->code = NR_CURVETO;
1249                 sp_node_adjust_handles_auto(node);
1250             } else {
1251                 // pull the handle opposite to line segment, making it half-smooth
1252                 if (p_is_line && node->n.other) {
1253                     if (type != Inkscape::NodePath::NODE_SYMM) {
1254                         // pull n handle
1255                         node->n.other->code = NR_CURVETO;
1256                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1257                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1258                     }
1259                 } else if (n_is_line && node->p.other) {
1260                     if (type != Inkscape::NodePath::NODE_SYMM) {
1261                         // pull p handle
1262                         node->code = NR_CURVETO;
1263                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1264                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1265                     }
1266                 }
1267             }
1268         }
1269     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1270         // cusping a cusp: retract nodes
1271         node->p.pos = node->pos;
1272         node->n.pos = node->pos;
1273     }
1275     sp_nodepath_set_node_type (node, type);
1278 /**
1279  * Move node to point, and adjust its and neighbouring handles.
1280  */
1281 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1283     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1284         node->pos = p;
1285         sp_node_adjust_handles_auto(node);
1286     } else {
1287         Geom::Point delta = p - node->pos;
1288         node->pos = p;
1290         node->p.pos += delta;
1291         node->n.pos += delta;
1292     }
1294     Inkscape::NodePath::Node *node_p = NULL;
1295     Inkscape::NodePath::Node *node_n = NULL;
1297     if (node->p.other) {
1298         if (node->code == NR_LINETO) {
1299             sp_node_adjust_handle(node, 1);
1300             sp_node_adjust_handle(node->p.other, -1);
1301             node_p = node->p.other;
1302         }
1303         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1304             sp_node_adjust_handles_auto(node->p.other);
1305             node_p = node->p.other;
1306         }
1307     }
1308     if (node->n.other) {
1309         if (node->n.other->code == NR_LINETO) {
1310             sp_node_adjust_handle(node, -1);
1311             sp_node_adjust_handle(node->n.other, 1);
1312             node_n = node->n.other;
1313         }
1314         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1315             sp_node_adjust_handles_auto(node->n.other);
1316             node_n = node->n.other;
1317         }
1318     }
1320     // this function is only called from batch movers that will update display at the end
1321     // themselves, so here we just move all the knots without emitting move signals, for speed
1322     sp_node_update_handles(node, false);
1323     if (node_n) {
1324         sp_node_update_handles(node_n, false);
1325     }
1326     if (node_p) {
1327         sp_node_update_handles(node_p, false);
1328     }
1331 /**
1332  * Call sp_node_moveto() for node selection and handle possible snapping.
1333  */
1334 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1335                                             bool const snap, bool constrained = false, 
1336                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1338     Geom::Coord best = NR_HUGE;
1339     Geom::Point delta(dx, dy);
1340     Geom::Point best_pt = delta;
1341     Inkscape::SnappedPoint best_abs;
1342     
1343     if (snap) {    
1344         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1345          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1346          * must provide that information. */
1347           
1348         // Build a list of the unselected nodes to which the snapper should snap 
1349         std::vector<Geom::Point> unselected_nodes;
1350         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1351             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1352             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1353                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1354                 if (!node->selected) {
1355                     unselected_nodes.push_back(to_2geom(node->pos));
1356                 }    
1357             }
1358         }        
1359         
1360         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1361         
1362         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1363             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1364             m.setup(nodepath->desktop, false, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1365             Inkscape::SnappedPoint s;
1366             if (constrained) {
1367                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1368                 dedicated_constraint.setPoint(n->pos);
1369                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
1370             } else {
1371                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta));
1372             }            
1373             if (s.getSnapped() && (s.getDistance() < best)) {
1374                 best = s.getDistance();
1375                 best_abs = s;
1376                 best_pt = from_2geom(s.getPoint()) - n->pos;
1377             }
1378         }
1379                         
1380         if (best_abs.getSnapped()) {
1381             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1382         } else {
1383             nodepath->desktop->snapindicator->remove_snappoint();    
1384         }
1385     }
1387     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1388         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1389         sp_node_moveto(n, n->pos + best_pt);
1390     }
1392     // do not update repr here so that node dragging is acceptably fast
1393     update_object(nodepath);
1396 /**
1397 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1398 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1399 near x = 0.
1400  */
1401 double
1402 sculpt_profile (double x, double alpha, guint profile)
1404     double result = 1;
1406     if (x >= 1) {
1407         result = 0;
1408     } else if (x <= 0) {
1409         result = 1;
1410     } else {
1411         switch (profile) {
1412             case SCULPT_PROFILE_LINEAR:
1413                 result = 1 - x;
1414                 break;
1415             case SCULPT_PROFILE_BELL:
1416                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1417                 break;
1418             case SCULPT_PROFILE_ELLIPTIC:
1419                 result = sqrt(1 - x*x);
1420                 break;
1421             default:
1422                 g_assert_not_reached();
1423         }
1424     }
1426     return result;
1429 double
1430 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1432     // extremely primitive for now, don't have time to look for the real one
1433     double lower = Geom::L2(b - a);
1434     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1435     return (lower + upper)/2;
1438 void
1439 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1441     n->pos = n->origin + delta;
1442     n->n.pos = n->n.origin + delta_n;
1443     n->p.pos = n->p.origin + delta_p;
1444     sp_node_adjust_handles(n);
1445     sp_node_update_handles(n, false);
1448 /**
1449  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1450  * on how far they are from the dragged node n.
1451  */
1452 static void
1453 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1455     g_assert (n);
1456     g_assert (nodepath);
1457     g_assert (n->subpath->nodepath == nodepath);
1459     double pressure = n->knot->pressure;
1460     if (pressure == 0)
1461         pressure = 0.5; // default
1462     pressure = CLAMP (pressure, 0.2, 0.8);
1464     // map pressure to alpha = 1/5 ... 5
1465     double alpha = 1 - 2 * fabs(pressure - 0.5);
1466     if (pressure > 0.5)
1467         alpha = 1/alpha;
1469     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1470     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1472     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1473         // Only one subpath has selected nodes:
1474         // use linear mode, where the distance from n to node being dragged is calculated along the path
1476         double n_sel_range = 0, p_sel_range = 0;
1477         guint n_nodes = 0, p_nodes = 0;
1478         guint n_sel_nodes = 0, p_sel_nodes = 0;
1480         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1481         {
1482             double n_range = 0, p_range = 0;
1483             bool n_going = true, p_going = true;
1484             Inkscape::NodePath::Node *n_node = n;
1485             Inkscape::NodePath::Node *p_node = n;
1486             do {
1487                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1488                 if (n_node && n_going)
1489                     n_node = n_node->n.other;
1490                 if (n_node == NULL) {
1491                     n_going = false;
1492                 } else {
1493                     n_nodes ++;
1494                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1495                     if (n_node->selected) {
1496                         n_sel_nodes ++;
1497                         n_sel_range = n_range;
1498                     }
1499                     if (n_node == p_node) {
1500                         n_going = false;
1501                         p_going = false;
1502                     }
1503                 }
1504                 if (p_node && p_going)
1505                     p_node = p_node->p.other;
1506                 if (p_node == NULL) {
1507                     p_going = false;
1508                 } else {
1509                     p_nodes ++;
1510                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1511                     if (p_node->selected) {
1512                         p_sel_nodes ++;
1513                         p_sel_range = p_range;
1514                     }
1515                     if (p_node == n_node) {
1516                         n_going = false;
1517                         p_going = false;
1518                     }
1519                 }
1520             } while (n_going || p_going);
1521         }
1523         // Second pass: actually move nodes in this subpath
1524         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1525         {
1526             double n_range = 0, p_range = 0;
1527             bool n_going = true, p_going = true;
1528             Inkscape::NodePath::Node *n_node = n;
1529             Inkscape::NodePath::Node *p_node = n;
1530             do {
1531                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1532                 if (n_node && n_going)
1533                     n_node = n_node->n.other;
1534                 if (n_node == NULL) {
1535                     n_going = false;
1536                 } else {
1537                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1538                     if (n_node->selected) {
1539                         sp_nodepath_move_node_and_handles (n_node,
1540                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1541                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1542                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1543                     }
1544                     if (n_node == p_node) {
1545                         n_going = false;
1546                         p_going = false;
1547                     }
1548                 }
1549                 if (p_node && p_going)
1550                     p_node = p_node->p.other;
1551                 if (p_node == NULL) {
1552                     p_going = false;
1553                 } else {
1554                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1555                     if (p_node->selected) {
1556                         sp_nodepath_move_node_and_handles (p_node,
1557                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1558                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1559                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1560                     }
1561                     if (p_node == n_node) {
1562                         n_going = false;
1563                         p_going = false;
1564                     }
1565                 }
1566             } while (n_going || p_going);
1567         }
1569     } else {
1570         // Multiple subpaths have selected nodes:
1571         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1572         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1573         // fix the pear-like shape when sculpting e.g. a ring
1575         // First pass: calculate range
1576         gdouble direct_range = 0;
1577         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1578             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1579             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1580                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1581                 if (node->selected) {
1582                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1583                 }
1584             }
1585         }
1587         // Second pass: actually move nodes
1588         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1589             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1590             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1591                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1592                 if (node->selected) {
1593                     if (direct_range > 1e-6) {
1594                         sp_nodepath_move_node_and_handles (node,
1595                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1596                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1597                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1598                     } else {
1599                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1600                     }
1602                 }
1603             }
1604         }
1605     }
1607     // do not update repr here so that node dragging is acceptably fast
1608     update_object(nodepath);
1612 /**
1613  * Move node selection to point, adjust its and neighbouring handles,
1614  * handle possible snapping, and commit the change with possible undo.
1615  */
1616 void
1617 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1619     if (!nodepath) return;
1621     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1623     if (dx == 0) {
1624         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1625     } else if (dy == 0) {
1626         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1627     } else {
1628         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1629     }
1632 /**
1633  * Move node selection off screen and commit the change.
1634  */
1635 void
1636 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1638     // borrowed from sp_selection_move_screen in selection-chemistry.c
1639     // we find out the current zoom factor and divide deltas by it
1641     gdouble zoom = desktop->current_zoom();
1642     gdouble zdx = dx / zoom;
1643     gdouble zdy = dy / zoom;
1645     if (!nodepath) return;
1647     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1649     if (dx == 0) {
1650         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1651     } else if (dy == 0) {
1652         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1653     } else {
1654         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1655     }
1658 /**
1659  * Move selected nodes to the absolute position given
1660  */
1661 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1663     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1664         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1665         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1666         sp_node_moveto(n, npos);
1667     }
1669     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1672 /**
1673  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1674  */
1675 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1677     boost::optional<Geom::Coord> no_coord;
1678     g_return_val_if_fail(nodepath->selected, no_coord);
1680     // determine coordinate of first selected node
1681     GList *nsel = nodepath->selected;
1682     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1683     Geom::Coord coord = n->pos[axis];
1684     bool coincide = true;
1686     // compare it to the coordinates of all the other selected nodes
1687     for (GList *l = nsel->next; l != NULL; l = l->next) {
1688         n = (Inkscape::NodePath::Node *) l->data;
1689         if (n->pos[axis] != coord) {
1690             coincide = false;
1691         }
1692     }
1693     if (coincide) {
1694         return coord;
1695     } else {
1696         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1697         // currently we return the coordinate of the bounding box midpoint because I don't know how
1698         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1699         return bbox.midpoint()[axis];
1700     }
1703 /** If they don't yet exist, creates knot and line for the given side of the node */
1704 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1706     if (!side->knot) {
1707         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"));
1709         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1710         side->knot->setSize (7);
1711         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1712         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1713         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1714         sp_knot_update_ctrl(side->knot);
1716         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1717         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1718         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1719         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1720         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1721         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1722     }
1724     if (!side->line) {
1725         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1726                                         SP_TYPE_CTRLLINE, NULL);
1727     }
1730 /**
1731  * Ensure the given handle of the node is visible/invisible, update its screen position
1732  */
1733 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1735     g_assert(node != NULL);
1737    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1738     NRPathcode code = sp_node_path_code_from_side(node, side);
1740     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1742     if (show_handle) {
1743         if (!side->knot) { // No handle knot at all
1744             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1745             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1746             side->knot->pos = side->pos;
1747             if (side->knot->item)
1748                 SP_CTRL(side->knot->item)->moveto(side->pos);
1749             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1750             sp_knot_show(side->knot);
1751         } else {
1752             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1753                 if (fire_move_signals) {
1754                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1755                 } else {
1756                     sp_knot_moveto(side->knot, side->pos);
1757                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1758                 }
1759             }
1760             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1761                 sp_knot_show(side->knot);
1762             }
1763         }
1764         sp_canvas_item_show(side->line);
1765     } else {
1766         if (side->knot) {
1767             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1768                 sp_knot_hide(side->knot);
1769             }
1770         }
1771         if (side->line) {
1772             sp_canvas_item_hide(side->line);
1773         }
1774     }
1777 /**
1778  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1779  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1780  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1781  * updated; otherwise, just move the knots silently (used in batch moves).
1782  */
1783 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1785     g_assert(node != NULL);
1787     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1788         sp_knot_show(node->knot);
1789     }
1791     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1792         if (fire_move_signals)
1793             sp_knot_set_position(node->knot, node->pos, 0);
1794         else
1795             sp_knot_moveto(node->knot, node->pos);
1796     }
1798     gboolean show_handles = node->selected;
1799     if (node->p.other != NULL) {
1800         if (node->p.other->selected) show_handles = TRUE;
1801     }
1802     if (node->n.other != NULL) {
1803         if (node->n.other->selected) show_handles = TRUE;
1804     }
1806     if (node->subpath->nodepath->show_handles == false)
1807         show_handles = FALSE;
1809     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1810     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1813 /**
1814  * Call sp_node_update_handles() for all nodes on subpath.
1815  */
1816 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1818     g_assert(subpath != NULL);
1820     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1821         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1822     }
1825 /**
1826  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1827  */
1828 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1830     g_assert(nodepath != NULL);
1832     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1833         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1834     }
1837 void
1838 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1840     if (nodepath) {
1841         nodepath->show_handles = show;
1842         sp_nodepath_update_handles(nodepath);
1843     }
1846 /**
1847  * Adds all selected nodes in nodepath to list.
1848  */
1849 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1851     StlConv<Node *>::list(l, selected);
1852 /// \todo this adds a copying, rework when the selection becomes a stl list
1855 /**
1856  * Align selected nodes on the specified axis.
1857  */
1858 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1860     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1861         return;
1862     }
1864     if ( !nodepath->selected->next ) { // only one node selected
1865         return;
1866     }
1867    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1868     Geom::Point dest(pNode->pos);
1869     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1870         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1871         if (pNode) {
1872             dest[axis] = pNode->pos[axis];
1873             sp_node_moveto(pNode, dest);
1874         }
1875     }
1877     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1880 /// Helper struct.
1881 struct NodeSort
1883    Inkscape::NodePath::Node *_node;
1884     Geom::Coord _coord;
1885     /// \todo use vectorof pointers instead of calling copy ctor
1886     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1887         _node(node), _coord(node->pos[axis])
1888     {}
1890 };
1892 static bool operator<(NodeSort const &a, NodeSort const &b)
1894     return (a._coord < b._coord);
1897 /**
1898  * Distribute selected nodes on the specified axis.
1899  */
1900 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1902     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1903         return;
1904     }
1906     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1907         return;
1908     }
1910    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1911     std::vector<NodeSort> sorted;
1912     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1913         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1914         if (pNode) {
1915             NodeSort n(pNode, axis);
1916             sorted.push_back(n);
1917             //dest[axis] = pNode->pos[axis];
1918             //sp_node_moveto(pNode, dest);
1919         }
1920     }
1921     std::sort(sorted.begin(), sorted.end());
1922     unsigned int len = sorted.size();
1923     //overall bboxes span
1924     float dist = (sorted.back()._coord -
1925                   sorted.front()._coord);
1926     //new distance between each bbox
1927     float step = (dist) / (len - 1);
1928     float pos = sorted.front()._coord;
1929     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1930           it < sorted.end();
1931           it ++ )
1932     {
1933         Geom::Point dest((*it)._node->pos);
1934         dest[axis] = pos;
1935         sp_node_moveto((*it)._node, dest);
1936         pos += step;
1937     }
1939     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1943 /**
1944  * Call sp_nodepath_line_add_node() for all selected segments.
1945  */
1946 void
1947 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1949     if (!nodepath) {
1950         return;
1951     }
1953     GList *nl = NULL;
1955     int n_added = 0;
1957     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1958        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1959         g_assert(t->selected);
1960         if (t->p.other && t->p.other->selected) {
1961             nl = g_list_prepend(nl, t);
1962         }
1963     }
1965     while (nl) {
1966        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1967        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1968        sp_nodepath_node_select(n, TRUE, FALSE);
1969        n_added ++;
1970        nl = g_list_remove(nl, t);
1971     }
1973     /** \todo fixme: adjust ? */
1974     sp_nodepath_update_handles(nodepath);
1976     if (n_added > 1) {
1977         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1978     } else if (n_added > 0) {
1979         sp_nodepath_update_repr(nodepath, _("Add node"));
1980     }
1982     sp_nodepath_update_statusbar(nodepath);
1985 /**
1986  * Select segment nearest to point
1987  */
1988 void
1989 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
1991     if (!nodepath) {
1992         return;
1993     }
1995     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1996     Geom::PathVector const &pathv = curve->get_pathvector();
1997     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
1998     if (!pvpos) {
1999         g_print ("Possible error?\n");
2000         return;
2001     }
2003     // calculate index for nodepath's representation.
2004     unsigned int segment_index = floor(pvpos->t) + 1;
2005     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2006         segment_index += pathv[i].size() + 1;
2007         if (pathv[i].closed()) {
2008             segment_index += 1;
2009         }
2010     }
2012     curve->unref();
2014     //find segment to segment
2015     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2017     //fixme: this can return NULL, so check before proceeding.
2018     g_return_if_fail(e != NULL);
2020     gboolean force = FALSE;
2021     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2022         force = TRUE;
2023     }
2024     sp_nodepath_node_select(e, (gboolean) toggle, force);
2025     if (e->p.other)
2026         sp_nodepath_node_select(e->p.other, TRUE, force);
2028     sp_nodepath_update_handles(nodepath);
2030     sp_nodepath_update_statusbar(nodepath);
2033 /**
2034  * Add a node nearest to point
2035  */
2036 void
2037 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2039     if (!nodepath) {
2040         return;
2041     }
2043     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2044     Geom::PathVector const &pathv = curve->get_pathvector();
2045     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2046     if (!pvpos) {
2047         g_print ("Possible error?\n");
2048         return;
2049     }
2051     // calculate index for nodepath's representation.
2052     double int_part;
2053     double t = std::modf(pvpos->t, &int_part);
2054     unsigned int segment_index = (unsigned int)int_part + 1;
2055     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2056         segment_index += pathv[i].size() + 1;
2057         if (pathv[i].closed()) {
2058             segment_index += 1;
2059         }
2060     }
2062     curve->unref();
2064     //find segment to split
2065     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2066     if (!e) {
2067         return;
2068     }
2070     //don't know why but t seems to flip for lines
2071     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2072         t = 1.0 - t;
2073     }
2075     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2076     sp_nodepath_node_select(n, FALSE, TRUE);
2078     /* fixme: adjust ? */
2079     sp_nodepath_update_handles(nodepath);
2081     sp_nodepath_update_repr(nodepath, _("Add node"));
2083     sp_nodepath_update_statusbar(nodepath);
2086 /*
2087  * Adjusts a segment so that t moves by a certain delta for dragging
2088  * converts lines to curves
2089  *
2090  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2091  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2092  */
2093 void
2094 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2096     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2098     //fixme: e and e->p can be NULL, so check for those before proceeding
2099     g_return_if_fail(e != NULL);
2100     g_return_if_fail(&e->p != NULL);
2102     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2103         e->type = Inkscape::NodePath::NODE_SMOOTH;
2104         sp_nodepath_update_node_knot (e);
2105     }
2106     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2107         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2108         sp_nodepath_update_node_knot (e->p.other);
2109     }
2111     /* feel good is an arbitrary parameter that distributes the delta between handles
2112      * if t of the drag point is less than 1/6 distance form the endpoint only
2113      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2114      */
2115     double feel_good;
2116     if (t <= 1.0 / 6.0)
2117         feel_good = 0;
2118     else if (t <= 0.5)
2119         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2120     else if (t <= 5.0 / 6.0)
2121         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2122     else
2123         feel_good = 1;
2125     //if we're dragging a line convert it to a curve
2126     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2127         sp_nodepath_set_line_type(e, NR_CURVETO);
2128     }
2130     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2131     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2132     e->p.other->n.pos += offsetcoord0;
2133     e->p.pos += offsetcoord1;
2135     // adjust handles of adjacent nodes where necessary
2136     sp_node_adjust_handle(e,1);
2137     sp_node_adjust_handle(e->p.other,-1);
2139     sp_nodepath_update_handles(e->subpath->nodepath);
2141     update_object(e->subpath->nodepath);
2143     sp_nodepath_update_statusbar(e->subpath->nodepath);
2147 /**
2148  * Call sp_nodepath_break() for all selected segments.
2149  */
2150 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2152     if (!nodepath) return;
2154     GList *tempin = g_list_copy(nodepath->selected);
2155     GList *temp = NULL;
2156     for (GList *l = tempin; l != NULL; l = l->next) {
2157        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2158        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2159         if (nn == NULL) continue; // no break, no new node
2160         temp = g_list_prepend(temp, nn);
2161     }
2162     g_list_free(tempin);
2164     if (temp) {
2165         sp_nodepath_deselect(nodepath);
2166     }
2167     for (GList *l = temp; l != NULL; l = l->next) {
2168         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2169     }
2171     sp_nodepath_update_handles(nodepath);
2173     sp_nodepath_update_repr(nodepath, _("Break path"));
2176 /**
2177  * Duplicate the selected node(s).
2178  */
2179 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2181     if (!nodepath) {
2182         return;
2183     }
2185     GList *temp = NULL;
2186     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2187        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2188        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2189         if (nn == NULL) continue; // could not duplicate
2190         temp = g_list_prepend(temp, nn);
2191     }
2193     if (temp) {
2194         sp_nodepath_deselect(nodepath);
2195     }
2196     for (GList *l = temp; l != NULL; l = l->next) {
2197         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2198     }
2200     sp_nodepath_update_handles(nodepath);
2202     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2205 /**
2206  *  Internal function to join two nodes by merging them into one.
2207  */
2208 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2210     /* a and b are endpoints */
2212     // if one of the two nodes is mouseovered, fix its position
2213     Geom::Point c;
2214     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2215         c = a->pos;
2216     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2217         c = b->pos;
2218     } else {
2219         // otherwise, move joined node to the midpoint
2220         c = (a->pos + b->pos) / 2;
2221     }
2223     if (a->subpath == b->subpath) {
2224        Inkscape::NodePath::SubPath *sp = a->subpath;
2225         sp_nodepath_subpath_close(sp);
2226         sp_node_moveto (sp->first, c);
2228         sp_nodepath_update_handles(sp->nodepath);
2229         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2230         return;
2231     }
2233     /* a and b are separate subpaths */
2234     Inkscape::NodePath::SubPath *sa = a->subpath;
2235     Inkscape::NodePath::SubPath *sb = b->subpath;
2236     Geom::Point p;
2237     Inkscape::NodePath::Node *n;
2238     NRPathcode code;
2239     if (a == sa->first) {
2240         // we will now reverse sa, so that a is its last node, not first, and drop that node
2241         p = sa->first->n.pos;
2242         code = (NRPathcode)sa->first->n.other->code;
2243         // create new subpath
2244        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2245        // create a first moveto node on it
2246         n = sa->last;
2247         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2248         n = n->p.other;
2249         if (n == sa->first) n = NULL;
2250         while (n) {
2251             // copy the rest of the nodes from sa to t, going backwards
2252             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2253             n = n->p.other;
2254             if (n == sa->first) n = NULL;
2255         }
2256         // replace sa with t
2257         sp_nodepath_subpath_destroy(sa);
2258         sa = t;
2259     } else if (a == sa->last) {
2260         // a is already last, just drop it
2261         p = sa->last->p.pos;
2262         code = (NRPathcode)sa->last->code;
2263         sp_nodepath_node_destroy(sa->last);
2264     } else {
2265         code = NR_END;
2266         g_assert_not_reached();
2267     }
2269     if (b == sb->first) {
2270         // copy all nodes from b to a, forward 
2271         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2272         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2273             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2274         }
2275     } else if (b == sb->last) {
2276         // copy all nodes from b to a, backward 
2277         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2278         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2279             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2280         }
2281     } else {
2282         g_assert_not_reached();
2283     }
2284     /* and now destroy sb */
2286     sp_nodepath_subpath_destroy(sb);
2288     sp_nodepath_update_handles(sa->nodepath);
2290     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2292     sp_nodepath_update_statusbar(nodepath);
2295 /**
2296  *  Internal function to join two nodes by adding a segment between them.
2297  */
2298 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2300     if (a->subpath == b->subpath) {
2301        Inkscape::NodePath::SubPath *sp = a->subpath;
2303         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2304         sp->closed = TRUE;
2306         sp->first->p.other = sp->last;
2307         sp->last->n.other  = sp->first;
2309         sp_node_handle_mirror_p_to_n(sp->last);
2310         sp_node_handle_mirror_n_to_p(sp->first);
2312         sp->first->code = sp->last->code;
2313         sp->first       = sp->last;
2315         sp_nodepath_update_handles(sp->nodepath);
2317         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2319         return;
2320     }
2322     /* a and b are separate subpaths */
2323     Inkscape::NodePath::SubPath *sa = a->subpath;
2324     Inkscape::NodePath::SubPath *sb = b->subpath;
2326     Inkscape::NodePath::Node *n;
2327     Geom::Point p;
2328     NRPathcode code;
2329     if (a == sa->first) {
2330         code = (NRPathcode) sa->first->n.other->code;
2331        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2332         n = sa->last;
2333         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2334         for (n = n->p.other; n != NULL; n = n->p.other) {
2335             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2336         }
2337         sp_nodepath_subpath_destroy(sa);
2338         sa = t;
2339     } else if (a == sa->last) {
2340         code = (NRPathcode)sa->last->code;
2341     } else {
2342         code = NR_END;
2343         g_assert_not_reached();
2344     }
2346     if (b == sb->first) {
2347         n = sb->first;
2348         sp_node_handle_mirror_p_to_n(sa->last);
2349         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2350         sp_node_handle_mirror_n_to_p(sa->last);
2351         for (n = n->n.other; n != NULL; n = n->n.other) {
2352             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2353         }
2354     } else if (b == sb->last) {
2355         n = sb->last;
2356         sp_node_handle_mirror_p_to_n(sa->last);
2357         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2358         sp_node_handle_mirror_n_to_p(sa->last);
2359         for (n = n->p.other; n != NULL; n = n->p.other) {
2360             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2361         }
2362     } else {
2363         g_assert_not_reached();
2364     }
2365     /* and now destroy sb */
2367     sp_nodepath_subpath_destroy(sb);
2369     sp_nodepath_update_handles(sa->nodepath);
2371     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2374 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2376 /**
2377  * Internal function to handle joining two nodes.
2378  */
2379 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2381     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2383     if (g_list_length(nodepath->selected) != 2) {
2384         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2385         return;
2386     }
2388     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2389     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2391     g_assert(a != b);
2392     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2393         // someone tried to join an orphan node (i.e. a single-node subpath).
2394         // this is not worth an error message, just fail silently.
2395         return;
2396     }
2398     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2399         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2400         return;
2401     }
2403     switch(mode) {
2404         case NODE_JOIN_ENDPOINTS:
2405             do_node_selected_join(nodepath, a, b);
2406             break;
2407         case NODE_JOIN_SEGMENT:
2408             do_node_selected_join_segment(nodepath, a, b);
2409             break;
2410     }
2413 /**
2414  *  Join two nodes by merging them into one.
2415  */
2416 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2418     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2421 /**
2422  *  Join two nodes by adding a segment between them.
2423  */
2424 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2426     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2429 /**
2430  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2431  */
2432 void sp_node_delete_preserve(GList *nodes_to_delete)
2434     GSList *nodepaths = NULL;
2436     while (nodes_to_delete) {
2437         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2438         Inkscape::NodePath::SubPath *sp = node->subpath;
2439         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2440         Inkscape::NodePath::Node *sample_cursor = NULL;
2441         Inkscape::NodePath::Node *sample_end = NULL;
2442         Inkscape::NodePath::Node *delete_cursor = node;
2443         bool just_delete = false;
2445         //find the start of this contiguous selection
2446         //move left to the first node that is not selected
2447         //or the start of the non-closed path
2448         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2449             delete_cursor = curr;
2450         }
2452         //just delete at the beginning of an open path
2453         if (!delete_cursor->p.other) {
2454             sample_cursor = delete_cursor;
2455             just_delete = true;
2456         } else {
2457             sample_cursor = delete_cursor->p.other;
2458         }
2460         //calculate points for each segment
2461         int rate = 5;
2462         float period = 1.0 / rate;
2463         std::vector<Geom::Point> data;
2464         if (!just_delete) {
2465             data.push_back(sample_cursor->pos);
2466             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2467                 //just delete at the end of an open path
2468                 if (!sp->closed && curr == sp->last) {
2469                     just_delete = true;
2470                     break;
2471                 }
2473                 //sample points on the contiguous selected segment
2474                 Geom::Point *bez;
2475                 bez = new Geom::Point [4];
2476                 bez[0] = curr->pos;
2477                 bez[1] = curr->n.pos;
2478                 bez[2] = curr->n.other->p.pos;
2479                 bez[3] = curr->n.other->pos;
2480                 for (int i=1; i<rate; i++) {
2481                     gdouble t = i * period;
2482                     Geom::Point p = bezier_pt(3, bez, t);
2483                     data.push_back(p);
2484                 }
2485                 data.push_back(curr->n.other->pos);
2487                 sample_end = curr->n.other;
2488                 //break if we've come full circle or hit the end of the selection
2489                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2490                     break;
2491                 }
2492             }
2493         }
2495         if (!just_delete) {
2496             //calculate the best fitting single segment and adjust the endpoints
2497             Geom::Point *adata;
2498             adata = new Geom::Point [data.size()];
2499             copy(data.begin(), data.end(), adata);
2501             Geom::Point *bez;
2502             bez = new Geom::Point [4];
2503             //would decreasing error create a better fitting approximation?
2504             gdouble error = 1.0;
2505             gint ret;
2506             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2508             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2509             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2510             //the resulting nodes behave as expected.
2511             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2512                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2513             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2514                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2516             //adjust endpoints
2517             sample_cursor->n.pos = bez[1];
2518             sample_end->p.pos = bez[2];
2519         }
2521         //destroy this contiguous selection
2522         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2523             Inkscape::NodePath::Node *temp = delete_cursor;
2524             if (delete_cursor->n.other == delete_cursor) {
2525                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2526                 delete_cursor = NULL;
2527             } else {
2528                 delete_cursor = delete_cursor->n.other;
2529             }
2530             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2531             sp_nodepath_node_destroy(temp);
2532         }
2534         sp_nodepath_update_handles(nodepath);
2536         if (!g_slist_find(nodepaths, nodepath))
2537             nodepaths = g_slist_prepend (nodepaths, nodepath);
2538     }
2540     for (GSList *i = nodepaths; i; i = i->next) {
2541         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2542         // different nodepaths will give us one undo event per nodepath
2543         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2545         // if the entire nodepath is removed, delete the selected object.
2546         if (nodepath->subpaths == NULL ||
2547             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2548             //at least 2
2549             sp_nodepath_get_node_count(nodepath) < 2) {
2550             SPDocument *document = sp_desktop_document (nodepath->desktop);
2551             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2552             //delete this nodepath's object, not the entire selection! (though at this time, this
2553             //does not matter)
2554             sp_selection_delete(nodepath->desktop);
2555             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2556                               _("Delete nodes"));
2557         } else {
2558             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2559             sp_nodepath_update_statusbar(nodepath);
2560         }
2561     }
2563     g_slist_free (nodepaths);
2566 /**
2567  * Delete one or more selected nodes.
2568  */
2569 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2571     if (!nodepath) return;
2572     if (!nodepath->selected) return;
2574     /** \todo fixme: do it the right way */
2575     while (nodepath->selected) {
2576        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2577         sp_nodepath_node_destroy(node);
2578     }
2581     //clean up the nodepath (such as for trivial subpaths)
2582     sp_nodepath_cleanup(nodepath);
2584     sp_nodepath_update_handles(nodepath);
2586     // if the entire nodepath is removed, delete the selected object.
2587     if (nodepath->subpaths == NULL ||
2588         sp_nodepath_get_node_count(nodepath) < 2) {
2589         SPDocument *document = sp_desktop_document (nodepath->desktop);
2590         sp_selection_delete(nodepath->desktop);
2591         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2592                           _("Delete nodes"));
2593         return;
2594     }
2596     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2598     sp_nodepath_update_statusbar(nodepath);
2601 /**
2602  * Delete one or more segments between two selected nodes.
2603  * This is the code for 'split'.
2604  */
2605 void
2606 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2608    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2609    Inkscape::NodePath::Node *curr, *next;     //Iterators
2611     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2613     if (g_list_length(nodepath->selected) != 2) {
2614         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2615                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2616         return;
2617     }
2619     //Selected nodes, not inclusive
2620    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2621    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2623     if ( ( a==b)                       ||  //same node
2624          (a->subpath  != b->subpath )  ||  //not the same path
2625          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2626          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2627     {
2628         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2629                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2630         return;
2631     }
2633     //###########################################
2634     //# BEGIN EDITS
2635     //###########################################
2636     //##################################
2637     //# CLOSED PATH
2638     //##################################
2639     if (a->subpath->closed) {
2642         gboolean reversed = FALSE;
2644         //Since we can go in a circle, we need to find the shorter distance.
2645         //  a->b or b->a
2646         start = end = NULL;
2647         int distance    = 0;
2648         int minDistance = 0;
2649         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2650             if (curr==b) {
2651                 //printf("a to b:%d\n", distance);
2652                 start = a;//go from a to b
2653                 end   = b;
2654                 minDistance = distance;
2655                 //printf("A to B :\n");
2656                 break;
2657             }
2658             distance++;
2659         }
2661         //try again, the other direction
2662         distance = 0;
2663         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2664             if (curr==a) {
2665                 //printf("b to a:%d\n", distance);
2666                 if (distance < minDistance) {
2667                     start    = b;  //we go from b to a
2668                     end      = a;
2669                     reversed = TRUE;
2670                     //printf("B to A\n");
2671                 }
2672                 break;
2673             }
2674             distance++;
2675         }
2678         //Copy everything from 'end' to 'start' to a new subpath
2679        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2680         for (curr=end ; curr ; curr=curr->n.other) {
2681             NRPathcode code = (NRPathcode) curr->code;
2682             if (curr == end)
2683                 code = NR_MOVETO;
2684             sp_nodepath_node_new(t, NULL,
2685                                  (Inkscape::NodePath::NodeType)curr->type, code,
2686                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2687             if (curr == start)
2688                 break;
2689         }
2690         sp_nodepath_subpath_destroy(a->subpath);
2693     }
2697     //##################################
2698     //# OPEN PATH
2699     //##################################
2700     else {
2702         //We need to get the direction of the list between A and B
2703         //Can we walk from a to b?
2704         start = end = NULL;
2705         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2706             if (curr==b) {
2707                 start = a;  //did it!  we go from a to b
2708                 end   = b;
2709                 //printf("A to B\n");
2710                 break;
2711             }
2712         }
2713         if (!start) {//didn't work?  let's try the other direction
2714             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2715                 if (curr==a) {
2716                     start = b;  //did it!  we go from b to a
2717                     end   = a;
2718                     //printf("B to A\n");
2719                     break;
2720                 }
2721             }
2722         }
2723         if (!start) {
2724             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2725                                                      _("Cannot find path between nodes."));
2726             return;
2727         }
2731         //Copy everything after 'end' to a new subpath
2732        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2733         for (curr=end ; curr ; curr=curr->n.other) {
2734             NRPathcode code = (NRPathcode) curr->code;
2735             if (curr == end)
2736                 code = NR_MOVETO;
2737             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2738                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2739         }
2741         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2742         for (curr = start->n.other ; curr  ; curr=next) {
2743             next = curr->n.other;
2744             sp_nodepath_node_destroy(curr);
2745         }
2747     }
2748     //###########################################
2749     //# END EDITS
2750     //###########################################
2752     //clean up the nodepath (such as for trivial subpaths)
2753     sp_nodepath_cleanup(nodepath);
2755     sp_nodepath_update_handles(nodepath);
2757     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2759     sp_nodepath_update_statusbar(nodepath);
2762 /**
2763  * Call sp_nodepath_set_line() for all selected segments.
2764  */
2765 void
2766 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2768     if (nodepath == NULL) return;
2770     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2771        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2772         g_assert(n->selected);
2773         if (n->p.other && n->p.other->selected) {
2774             sp_nodepath_set_line_type(n, code);
2775         }
2776     }
2778     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2781 /**
2782  * Call sp_nodepath_convert_node_type() for all selected nodes.
2783  */
2784 void
2785 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2787     if (nodepath == NULL) return;
2789     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2791     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2792         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2793     }
2795     sp_nodepath_update_repr(nodepath, _("Change node type"));
2798 /**
2799  * Change select status of node, update its own and neighbour handles.
2800  */
2801 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2803     node->selected = selected;
2805     if (selected) {
2806         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2807         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2808         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2809         sp_knot_update_ctrl(node->knot);
2810     } else {
2811         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2812         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2813         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2814         sp_knot_update_ctrl(node->knot);
2815     }
2817     sp_node_update_handles(node);
2818     if (node->n.other) sp_node_update_handles(node->n.other);
2819     if (node->p.other) sp_node_update_handles(node->p.other);
2822 /**
2823 \brief Select a node
2824 \param node     The node to select
2825 \param incremental   If true, add to selection, otherwise deselect others
2826 \param override   If true, always select this node, otherwise toggle selected status
2827 */
2828 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2830     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2832     if (incremental) {
2833         if (override) {
2834             if (!g_list_find(nodepath->selected, node)) {
2835                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2836             }
2837             sp_node_set_selected(node, TRUE);
2838         } else { // toggle
2839             if (node->selected) {
2840                 g_assert(g_list_find(nodepath->selected, node));
2841                 nodepath->selected = g_list_remove(nodepath->selected, node);
2842             } else {
2843                 g_assert(!g_list_find(nodepath->selected, node));
2844                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2845             }
2846             sp_node_set_selected(node, !node->selected);
2847         }
2848     } else {
2849         sp_nodepath_deselect(nodepath);
2850         nodepath->selected = g_list_prepend(nodepath->selected, node);
2851         sp_node_set_selected(node, TRUE);
2852     }
2854     sp_nodepath_update_statusbar(nodepath);
2858 /**
2859 \brief Deselect all nodes in the nodepath
2860 */
2861 void
2862 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2864     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2866     while (nodepath->selected) {
2867         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2868         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2869     }
2870     sp_nodepath_update_statusbar(nodepath);
2873 /**
2874 \brief Select or invert selection of all nodes in the nodepath
2875 */
2876 void
2877 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2879     if (!nodepath) return;
2881     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2882        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2883         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2884            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2885            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2886         }
2887     }
2890 /**
2891  * If nothing selected, does the same as sp_nodepath_select_all();
2892  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2893  * (i.e., similar to "select all in layer", with the "selected" subpaths
2894  * being treated as "layers" in the path).
2895  */
2896 void
2897 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2899     if (!nodepath) return;
2901     if (g_list_length (nodepath->selected) == 0) {
2902         sp_nodepath_select_all (nodepath, invert);
2903         return;
2904     }
2906     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2907     GSList *subpaths = NULL;
2909     for (GList *l = copy; l != NULL; l = l->next) {
2910         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2911         Inkscape::NodePath::SubPath *subpath = n->subpath;
2912         if (!g_slist_find (subpaths, subpath))
2913             subpaths = g_slist_prepend (subpaths, subpath);
2914     }
2916     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2917         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2918         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2919             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2920             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2921         }
2922     }
2924     g_slist_free (subpaths);
2925     g_list_free (copy);
2928 /**
2929  * \brief Select the node after the last selected; if none is selected,
2930  * select the first within path.
2931  */
2932 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2934     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2936    Inkscape::NodePath::Node *last = NULL;
2937     if (nodepath->selected) {
2938         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2939            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2940             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2941             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2942                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2943                 if (node->selected) {
2944                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2945                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2946                             if (spl->next) { // there's a next subpath
2947                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2948                                 last = subpath_next->first;
2949                             } else if (spl->prev) { // there's a previous subpath
2950                                 last = NULL; // to be set later to the first node of first subpath
2951                             } else {
2952                                 last = node->n.other;
2953                             }
2954                         } else {
2955                             last = node->n.other;
2956                         }
2957                     } else {
2958                         if (node->n.other) {
2959                             last = node->n.other;
2960                         } else {
2961                             if (spl->next) { // there's a next subpath
2962                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2963                                 last = subpath_next->first;
2964                             } else if (spl->prev) { // there's a previous subpath
2965                                 last = NULL; // to be set later to the first node of first subpath
2966                             } else {
2967                                 last = (Inkscape::NodePath::Node *) subpath->first;
2968                             }
2969                         }
2970                     }
2971                 }
2972             }
2973         }
2974         sp_nodepath_deselect(nodepath);
2975     }
2977     if (last) { // there's at least one more node after selected
2978         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2979     } else { // no more nodes, select the first one in first subpath
2980        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2981         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2982     }
2985 /**
2986  * \brief Select the node before the first selected; if none is selected,
2987  * select the last within path
2988  */
2989 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2991     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2993    Inkscape::NodePath::Node *last = NULL;
2994     if (nodepath->selected) {
2995         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2996            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2997             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2998                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2999                 if (node->selected) {
3000                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3001                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3002                             if (spl->prev) { // there's a prev subpath
3003                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3004                                 last = subpath_prev->last;
3005                             } else if (spl->next) { // there's a next subpath
3006                                 last = NULL; // to be set later to the last node of last subpath
3007                             } else {
3008                                 last = node->p.other;
3009                             }
3010                         } else {
3011                             last = node->p.other;
3012                         }
3013                     } else {
3014                         if (node->p.other) {
3015                             last = node->p.other;
3016                         } else {
3017                             if (spl->prev) { // there's a prev subpath
3018                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3019                                 last = subpath_prev->last;
3020                             } else if (spl->next) { // there's a next subpath
3021                                 last = NULL; // to be set later to the last node of last subpath
3022                             } else {
3023                                 last = (Inkscape::NodePath::Node *) subpath->last;
3024                             }
3025                         }
3026                     }
3027                 }
3028             }
3029         }
3030         sp_nodepath_deselect(nodepath);
3031     }
3033     if (last) { // there's at least one more node before selected
3034         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3035     } else { // no more nodes, select the last one in last subpath
3036         GList *spl = g_list_last(nodepath->subpaths);
3037        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3038         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3039     }
3042 /**
3043  * \brief Select all nodes that are within the rectangle.
3044  */
3045 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3047     if (!incremental) {
3048         sp_nodepath_deselect(nodepath);
3049     }
3051     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3052        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3053         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3054            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3056             if (b.contains(node->pos)) {
3057                 sp_nodepath_node_select(node, TRUE, TRUE);
3058             }
3059         }
3060     }
3064 void
3065 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3067     g_assert (n);
3068     g_assert (nodepath);
3069     g_assert (n->subpath->nodepath == nodepath);
3071     if (g_list_length (nodepath->selected) == 0) {
3072         if (grow > 0) {
3073             sp_nodepath_node_select(n, TRUE, TRUE);
3074         }
3075         return;
3076     }
3078     if (g_list_length (nodepath->selected) == 1) {
3079         if (grow < 0) {
3080             sp_nodepath_deselect (nodepath);
3081             return;
3082         }
3083     }
3085         double n_sel_range = 0, p_sel_range = 0;
3086             Inkscape::NodePath::Node *farthest_n_node = n;
3087             Inkscape::NodePath::Node *farthest_p_node = n;
3089         // Calculate ranges
3090         {
3091             double n_range = 0, p_range = 0;
3092             bool n_going = true, p_going = true;
3093             Inkscape::NodePath::Node *n_node = n;
3094             Inkscape::NodePath::Node *p_node = n;
3095             do {
3096                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3097                 if (n_node && n_going)
3098                     n_node = n_node->n.other;
3099                 if (n_node == NULL) {
3100                     n_going = false;
3101                 } else {
3102                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3103                     if (n_node->selected) {
3104                         n_sel_range = n_range;
3105                         farthest_n_node = n_node;
3106                     }
3107                     if (n_node == p_node) {
3108                         n_going = false;
3109                         p_going = false;
3110                     }
3111                 }
3112                 if (p_node && p_going)
3113                     p_node = p_node->p.other;
3114                 if (p_node == NULL) {
3115                     p_going = false;
3116                 } else {
3117                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3118                     if (p_node->selected) {
3119                         p_sel_range = p_range;
3120                         farthest_p_node = p_node;
3121                     }
3122                     if (p_node == n_node) {
3123                         n_going = false;
3124                         p_going = false;
3125                     }
3126                 }
3127             } while (n_going || p_going);
3128         }
3130     if (grow > 0) {
3131         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3132                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3133         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3134                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3135         }
3136     } else {
3137         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3138                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3139         } else if (farthest_p_node && farthest_p_node->selected) {
3140                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3141         }
3142     }
3145 void
3146 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3148     g_assert (n);
3149     g_assert (nodepath);
3150     g_assert (n->subpath->nodepath == nodepath);
3152     if (g_list_length (nodepath->selected) == 0) {
3153         if (grow > 0) {
3154             sp_nodepath_node_select(n, TRUE, TRUE);
3155         }
3156         return;
3157     }
3159     if (g_list_length (nodepath->selected) == 1) {
3160         if (grow < 0) {
3161             sp_nodepath_deselect (nodepath);
3162             return;
3163         }
3164     }
3166     Inkscape::NodePath::Node *farthest_selected = NULL;
3167     double farthest_dist = 0;
3169     Inkscape::NodePath::Node *closest_unselected = NULL;
3170     double closest_dist = NR_HUGE;
3172     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3173        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3174         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3175            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3176            if (node == n)
3177                continue;
3178            if (node->selected) {
3179                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3180                    farthest_dist = Geom::L2(node->pos - n->pos);
3181                    farthest_selected = node;
3182                }
3183            } else {
3184                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3185                    closest_dist = Geom::L2(node->pos - n->pos);
3186                    closest_unselected = node;
3187                }
3188            }
3189         }
3190     }
3192     if (grow > 0) {
3193         if (closest_unselected) {
3194             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3195         }
3196     } else {
3197         if (farthest_selected) {
3198             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3199         }
3200     }
3204 /**
3205 \brief  Saves all nodes' and handles' current positions in their origin members
3206 */
3207 void
3208 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3210     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3211        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3212         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3213            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3214            n->origin = n->pos;
3215            n->p.origin = n->p.pos;
3216            n->n.origin = n->n.pos;
3217         }
3218     }
3221 /**
3222 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3223 */
3224 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3226     GList *r = NULL;
3227     if (nodepath->selected) {
3228         guint i = 0;
3229         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3230             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3231             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3232                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3233                 i++;
3234                 if (node->selected) {
3235                     r = g_list_append(r, GINT_TO_POINTER(i));
3236                 }
3237             }
3238         }
3239     }
3240     return r;
3243 /**
3244 \brief  Restores selection by selecting nodes whose positions are in the list
3245 */
3246 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3248     sp_nodepath_deselect(nodepath);
3250     guint i = 0;
3251     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3252        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3253         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3254            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3255             i++;
3256             if (g_list_find(r, GINT_TO_POINTER(i))) {
3257                 sp_nodepath_node_select(node, TRUE, TRUE);
3258             }
3259         }
3260     }
3264 /**
3265 \brief Adjusts handle according to node type and line code.
3266 */
3267 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3269     g_assert(node);
3271     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3272     if (node->type == Inkscape::NodePath::NODE_AUTO)
3273         return;
3275    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3276    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3278    // nothing to do if we are an end node
3279     if (me->other == NULL) return;
3280     if (other->other == NULL) return;
3282     // nothing to do if we are a cusp node
3283     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3285     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3286     NRPathcode mecode;
3287     if (which_adjust == 1) {
3288         mecode = (NRPathcode)me->other->code;
3289     } else {
3290         mecode = (NRPathcode)node->code;
3291     }
3292     if (mecode == NR_LINETO) return;
3294     if (sp_node_side_is_line(node, other)) {
3295         // other is a line, and we are either smooth or symm
3296        Inkscape::NodePath::Node *othernode = other->other;
3297         double len = Geom::L2(me->pos - node->pos);
3298         Geom::Point delta = node->pos - othernode->pos;
3299         double linelen = Geom::L2(delta);
3300         if (linelen < 1e-18)
3301             return;
3302         me->pos = node->pos + (len / linelen)*delta;
3303         return;
3304     }
3306     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3307         // symmetrize 
3308         me->pos = 2 * node->pos - other->pos;
3309         return;
3310     } else {
3311         // smoothify
3312         double len = Geom::L2(me->pos - node->pos);
3313         Geom::Point delta = other->pos - node->pos;
3314         double otherlen = Geom::L2(delta);
3315         if (otherlen < 1e-18) return;
3316         me->pos = node->pos - (len / otherlen) * delta;
3317     }
3320 /**
3321  \brief Adjusts both handles according to node type and line code
3322  */
3323 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3325     g_assert(node);
3327     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3329     /* we are either smooth or symm */
3331     if (node->p.other == NULL) return;
3332     if (node->n.other == NULL) return;
3334     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3335         sp_node_adjust_handles_auto(node);
3336         return;
3337     }
3339     if (sp_node_side_is_line(node, &node->p)) {
3340         sp_node_adjust_handle(node, 1);
3341         return;
3342     }
3344     if (sp_node_side_is_line(node, &node->n)) {
3345         sp_node_adjust_handle(node, -1);
3346         return;
3347     }
3349     /* both are curves */
3350     Geom::Point const delta( node->n.pos - node->p.pos );
3352     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3353         node->p.pos = node->pos - delta / 2;
3354         node->n.pos = node->pos + delta / 2;
3355         return;
3356     }
3358     /* We are smooth */
3359     double plen = Geom::L2(node->p.pos - node->pos);
3360     if (plen < 1e-18) return;
3361     double nlen = Geom::L2(node->n.pos - node->pos);
3362     if (nlen < 1e-18) return;
3363     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3364     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3367 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3369     if (node->p.other == NULL || node->n.other == NULL) {
3370         node->p.pos = node->pos;
3371         node->n.pos = node->pos;
3372         return;
3373     }
3375     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3376     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3377  
3378     double norm_leg_prev = Geom::L2(leg_prev);
3379     double norm_leg_next = Geom::L2(leg_next);
3380  
3381     Geom::Point delta;
3382     if (norm_leg_next > 0.0) {
3383         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3384         delta.normalize();
3385     }
3386  
3387     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3388     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3391 /**
3392  * Node event callback.
3393  */
3394 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3396     gboolean ret = FALSE;
3397     switch (event->type) {
3398         case GDK_ENTER_NOTIFY:
3399             Inkscape::NodePath::Path::active_node = n;
3400             break;
3401         case GDK_LEAVE_NOTIFY:
3402             Inkscape::NodePath::Path::active_node = NULL;
3403             break;
3404         case GDK_SCROLL:
3405             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3406                 switch (event->scroll.direction) {
3407                     case GDK_SCROLL_UP:
3408                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3409                         break;
3410                     case GDK_SCROLL_DOWN:
3411                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3412                         break;
3413                     default:
3414                         break;
3415                 }
3416                 ret = TRUE;
3417             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3418                 switch (event->scroll.direction) {
3419                     case GDK_SCROLL_UP:
3420                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3421                         break;
3422                     case GDK_SCROLL_DOWN:
3423                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3424                         break;
3425                     default:
3426                         break;
3427                 }
3428                 ret = TRUE;
3429             }
3430             break;
3431         case GDK_KEY_PRESS:
3432             switch (get_group0_keyval (&event->key)) {
3433                 case GDK_space:
3434                     if (event->key.state & GDK_BUTTON1_MASK) {
3435                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3436                         stamp_repr(nodepath);
3437                         ret = TRUE;
3438                     }
3439                     break;
3440                 case GDK_Page_Up:
3441                     if (event->key.state & GDK_CONTROL_MASK) {
3442                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3443                     } else {
3444                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3445                     }
3446                     break;
3447                 case GDK_Page_Down:
3448                     if (event->key.state & GDK_CONTROL_MASK) {
3449                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3450                     } else {
3451                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3452                     }
3453                     break;
3454                 default:
3455                     break;
3456             }
3457             break;
3458         default:
3459             break;
3460     }
3462     return ret;
3465 /**
3466  * Handle keypress on node; directly called.
3467  */
3468 gboolean node_key(GdkEvent *event)
3470     Inkscape::NodePath::Path *np;
3472     // there is no way to verify nodes so set active_node to nil when deleting!!
3473     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3475     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3476         gint ret = FALSE;
3477         switch (get_group0_keyval (&event->key)) {
3478             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3479             case GDK_BackSpace:
3480                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3481                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3482                 sp_nodepath_update_repr(np, _("Delete node"));
3483                 Inkscape::NodePath::Path::active_node = NULL;
3484                 ret = TRUE;
3485                 break;
3486             case GDK_c:
3487                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3488                 ret = TRUE;
3489                 break;
3490             case GDK_s:
3491                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3492                 ret = TRUE;
3493                 break;
3494             case GDK_a:
3495                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3496                 ret = TRUE;
3497                 break;
3498             case GDK_y:
3499                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3500                 ret = TRUE;
3501                 break;
3502             case GDK_b:
3503                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3504                 ret = TRUE;
3505                 break;
3506         }
3507         return ret;
3508     }
3509     return FALSE;
3512 /**
3513  * Mouseclick on node callback.
3514  */
3515 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3517    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3519     if (state & GDK_CONTROL_MASK) {
3520         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3522         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3523             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3524                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3525             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3526                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3527             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3528                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3529             } else {
3530                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3531             }
3532             sp_nodepath_update_repr(nodepath, _("Change node type"));
3533             sp_nodepath_update_statusbar(nodepath);
3535         } else { //ctrl+alt+click: delete node
3536             GList *node_to_delete = NULL;
3537             node_to_delete = g_list_append(node_to_delete, n);
3538             sp_node_delete_preserve(node_to_delete);
3539         }
3541     } else {
3542         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3543     }
3546 /**
3547  * Mouse grabbed node callback.
3548  */
3549 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3551    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3553     if (!n->selected) {
3554         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3555     }
3557     n->is_dragging = true;
3558     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3560     sp_nodepath_remember_origins (n->subpath->nodepath);
3563 /**
3564  * Mouse ungrabbed node callback.
3565  */
3566 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3568    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3570    n->dragging_out = NULL;
3571    n->is_dragging = false;
3572    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3574    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3577 /**
3578  * The point on a line, given by its angle, closest to the given point.
3579  * \param p  A point.
3580  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3581  * \param closest  Pointer to the point struct where the result is stored.
3582  * \todo FIXME: use dot product perhaps?
3583  */
3584 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3586     if (a == HUGE_VAL) { // vertical
3587         *closest = Geom::Point(0, (*p)[Geom::Y]);
3588     } else {
3589         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3590         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3591     }
3594 /**
3595  * Distance from the point to a line given by its angle.
3596  * \param p  A point.
3597  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3598  */
3599 static double point_line_distance(Geom::Point *p, double a)
3601     Geom::Point c;
3602     point_line_closest(p, a, &c);
3603     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]));
3606 /**
3607  * Callback for node "request" signal.
3608  * \todo fixme: This goes to "moved" event? (lauris)
3609  */
3610 static gboolean
3611 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3613     double yn, xn, yp, xp;
3614     double an, ap, na, pa;
3615     double d_an, d_ap, d_na, d_pa;
3616     gboolean collinear = FALSE;
3617     Geom::Point c;
3618     Geom::Point pr;
3620     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3622     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3624     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3625     if ( (!n->subpath->nodepath->straight_path) &&
3626          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3627            || n->dragging_out ) )
3628     {
3629        Geom::Point mouse = p;
3631        if (!n->dragging_out) {
3632            // This is the first drag-out event; find out which handle to drag out
3633            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3634            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3636            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3637                return FALSE;
3639            Inkscape::NodePath::NodeSide *opposite;
3640            if (appr_p > appr_n) { // closer to p
3641                n->dragging_out = &n->p;
3642                opposite = &n->n;
3643                n->code = NR_CURVETO;
3644            } else if (appr_p < appr_n) { // closer to n
3645                n->dragging_out = &n->n;
3646                opposite = &n->p;
3647                n->n.other->code = NR_CURVETO;
3648            } else { // p and n nodes are the same
3649                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3650                    n->dragging_out = &n->p;
3651                    opposite = &n->n;
3652                    n->code = NR_CURVETO;
3653                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3654                    n->dragging_out = &n->n;
3655                    opposite = &n->p;
3656                    n->n.other->code = NR_CURVETO;
3657                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3658                    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);
3659                    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);
3660                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3661                        n->dragging_out = &n->n;
3662                        opposite = &n->p;
3663                        n->n.other->code = NR_CURVETO;
3664                    } else { // closer to other's n handle
3665                        n->dragging_out = &n->p;
3666                        opposite = &n->n;
3667                        n->code = NR_CURVETO;
3668                    }
3669                }
3670            }
3672            // if there's another handle, make sure the one we drag out starts parallel to it
3673            if (opposite->pos != n->pos) {
3674                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3675            }
3677            // knots might not be created yet!
3678            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3679            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3680        }
3682        // pass this on to the handle-moved callback
3683        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3684        sp_node_update_handles(n);
3685        return TRUE;
3686    }
3688     if (state & GDK_CONTROL_MASK) { // constrained motion
3690         // calculate relative distances of handles
3691         // n handle:
3692         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3693         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3694         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3695         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3696             if (n->n.other) { // if there is the next point
3697                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3698                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3699                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3700             }
3701         }
3702         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3703         if (yn < 0) { xn = -xn; yn = -yn; }
3705         // p handle:
3706         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3707         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3708         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3709         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3710             if (n->p.other) {
3711                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3712                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3713                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3714             }
3715         }
3716         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3717         if (yp < 0) { xp = -xp; yp = -yp; }
3719         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3720             // sliding on handles, only if at least one of the handles is non-vertical
3721             // (otherwise it's the same as ctrl+drag anyway)
3723             // calculate angles of the handles
3724             if (xn == 0) {
3725                 if (yn == 0) { // no handle, consider it the continuation of the other one
3726                     an = 0;
3727                     collinear = TRUE;
3728                 }
3729                 else an = 0; // vertical; set the angle to horizontal
3730             } else an = yn/xn;
3732             if (xp == 0) {
3733                 if (yp == 0) { // no handle, consider it the continuation of the other one
3734                     ap = an;
3735                 }
3736                 else ap = 0; // vertical; set the angle to horizontal
3737             } else  ap = yp/xp;
3739             if (collinear) an = ap;
3741             // angles of the perpendiculars; HUGE_VAL means vertical
3742             if (an == 0) na = HUGE_VAL; else na = -1/an;
3743             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3745             // mouse point relative to the node's original pos
3746             pr = p - n->origin;
3748             // distances to the four lines (two handles and two perpendiculars)
3749             d_an = point_line_distance(&pr, an);
3750             d_na = point_line_distance(&pr, na);
3751             d_ap = point_line_distance(&pr, ap);
3752             d_pa = point_line_distance(&pr, pa);
3754             // find out which line is the closest, save its closest point in c
3755             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3756                 point_line_closest(&pr, an, &c);
3757             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3758                 point_line_closest(&pr, ap, &c);
3759             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3760                 point_line_closest(&pr, na, &c);
3761             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3762                 point_line_closest(&pr, pa, &c);
3763             }
3765             // move the node to the closest point
3766             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3767                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3768                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y], 
3769                                             true);
3771         } else {  // constraining to hor/vert
3773             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3774                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3775                                                 p[Geom::X] - n->pos[Geom::X], 
3776                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3777                                                 true, 
3778                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3779             } else { // snap to vert
3780                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3781                                                 n->origin[Geom::X] - n->pos[Geom::X],
3782                                                 p[Geom::Y] - n->pos[Geom::Y],
3783                                                 true,
3784                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3785             }
3786         }
3787     } else { // move freely
3788         if (n->is_dragging) {
3789             if (state & GDK_MOD1_MASK) { // sculpt
3790                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3791             } else {
3792                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3793                                             p[Geom::X] - n->pos[Geom::X],
3794                                             p[Geom::Y] - n->pos[Geom::Y],
3795                                             (state & GDK_SHIFT_MASK) == 0);
3796             }
3797         }
3798     }
3800     n->subpath->nodepath->desktop->scroll_to_point(p);
3802     return TRUE;
3805 /**
3806  * Node handle clicked callback.
3807  */
3808 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3810    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3812     if (state & GDK_CONTROL_MASK) { // "delete" handle
3813         if (n->p.knot == knot) {
3814             n->p.pos = n->pos;
3815         } else if (n->n.knot == knot) {
3816             n->n.pos = n->pos;
3817         }
3818         sp_node_update_handles(n);
3819         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3820         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3821         sp_nodepath_update_statusbar(nodepath);
3823     } else { // just select or add to selection, depending in Shift
3824         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3825     }
3828 /**
3829  * Node handle grabbed callback.
3830  */
3831 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3833    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3835     // convert auto -> smooth when dragging handle
3836    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3837         n->type = Inkscape::NodePath::NODE_SMOOTH;
3838         sp_nodepath_update_node_knot (n);
3839    }
3841     if (!n->selected) {
3842         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3843     }
3845     // remember the origin point of the handle
3846     if (n->p.knot == knot) {
3847         n->p.origin_radial = n->p.pos - n->pos;
3848     } else if (n->n.knot == knot) {
3849         n->n.origin_radial = n->n.pos - n->pos;
3850     } else {
3851         g_assert_not_reached();
3852     }
3854     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3857 /**
3858  * Node handle ungrabbed callback.
3859  */
3860 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3862    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3864     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3865     if (n->p.knot == knot) {
3866         n->p.origin_radial.a = 0;
3867         sp_knot_set_position(knot, n->p.pos, state);
3868     } else if (n->n.knot == knot) {
3869         n->n.origin_radial.a = 0;
3870         sp_knot_set_position(knot, n->n.pos, state);
3871     } else {
3872         g_assert_not_reached();
3873     }
3875     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3878 /**
3879  * Node handle "request" signal callback.
3880  */
3881 static gboolean node_handle_request(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3883     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3885     Inkscape::NodePath::NodeSide *me, *opposite;
3886     gint which;
3887     if (n->p.knot == knot) {
3888         me = &n->p;
3889         opposite = &n->n;
3890         which = -1;
3891     } else if (n->n.knot == knot) {
3892         me = &n->n;
3893         opposite = &n->p;
3894         which = 1;
3895     } else {
3896         me = opposite = NULL;
3897         which = 0;
3898         g_assert_not_reached();
3899     }
3901     SPDesktop *desktop = n->subpath->nodepath->desktop;
3902     SnapManager &m = desktop->namedview->snap_manager;
3903     m.setup(desktop, true, n->subpath->nodepath->item);
3904     Inkscape::SnappedPoint s;
3905     
3906     if ((state & GDK_SHIFT_MASK) != 0) {
3907         // We will not try to snap when the shift-key is pressed
3908         // so remove the old snap indicator and don't wait for it to time-out  
3909         desktop->snapindicator->remove_snappoint();
3910     }
3912     Inkscape::NodePath::Node *othernode = opposite->other;
3913     if (othernode) {
3914         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3915             /* We are smooth node adjacent with line */
3916             Geom::Point const delta = p - n->pos;
3917             Geom::Coord const len = Geom::L2(delta);
3918             Inkscape::NodePath::Node *othernode = opposite->other;
3919             Geom::Point const ndelta = n->pos - othernode->pos;
3920             Geom::Coord const linelen = Geom::L2(ndelta);
3921             Geom::Point ptemp = p;
3922             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3923                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3924                 ptemp = n->pos + (scal / linelen) * ndelta;
3925             }
3926             if ((state & GDK_SHIFT_MASK) == 0) {
3927                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, ptemp, Inkscape::Snapper::ConstraintLine(ptemp, ndelta));
3928             }
3929         } else {
3930             if ((state & GDK_SHIFT_MASK) == 0) {
3931                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p);
3932             }
3933         }
3934     } else {
3935         if ((state & GDK_SHIFT_MASK) == 0) {
3936             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p);
3937         }
3938     }
3939     
3940     sp_node_adjust_handle(n, -which);
3942     return FALSE;
3945 /**
3946  * Node handle moved callback.
3947  */
3948 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3950    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3951    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3953    Inkscape::NodePath::NodeSide *me;
3954    Inkscape::NodePath::NodeSide *other;
3955     if (n->p.knot == knot) {
3956         me = &n->p;
3957         other = &n->n;
3958     } else if (n->n.knot == knot) {
3959         me = &n->n;
3960         other = &n->p;
3961     } else {
3962         me = NULL;
3963         other = NULL;
3964         g_assert_not_reached();
3965     }
3967     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3968     Radial rme(me->pos - n->pos);
3969     Radial rother(other->pos - n->pos);
3970     Radial rnew(p - n->pos);
3972     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3973         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
3974         /* 0 interpreted as "no snapping". */
3976         // 1. Snap to the closest PI/snaps angle, starting from zero.
3977         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3979         // 2. Snap to the original angle, its opposite and perpendiculars
3980         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3981             /* The closest PI/2 angle, starting from original angle */
3982             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3984             // Snap to the closest.
3985             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3986                        ? a_snapped
3987                        : a_ortho );
3988         }
3990         // 3. Snap to the angle of the opposite line, if any
3991         Inkscape::NodePath::Node *othernode = other->other;
3992         if (othernode) {
3993             Geom::Point other_to_snap(0,0);
3994             if (sp_node_side_is_line(n, other)) {
3995                 other_to_snap = othernode->pos - n->pos;
3996             } else {
3997                 other_to_snap = other->pos - n->pos;
3998             }
3999             if (Geom::L2(other_to_snap) > 1e-3) {
4000                 Radial rother_to_snap(other_to_snap);
4001                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4002                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4004                 // Snap to the closest.
4005                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4006                        ? a_snapped
4007                        : a_oppo );
4008             }
4009         }
4011         rnew.a = a_snapped;
4012     }
4014     if (state & GDK_MOD1_MASK) {
4015         // lock handle length
4016         rnew.r = me->origin_radial.r;
4017     }
4019     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
4020         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
4021         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4022         rother.a += rnew.a - rme.a;
4023         other->pos = Geom::Point(rother) + n->pos;
4024         if (other->knot) {
4025             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4026             sp_knot_moveto(other->knot, other->pos);
4027         }
4028     }
4030     me->pos = Geom::Point(rnew) + n->pos;
4031     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4033     // move knot, but without emitting the signal:
4034     // we cannot emit a "moved" signal because we're now processing it
4035     sp_knot_moveto(me->knot, me->pos);
4037     update_object(n->subpath->nodepath);
4039     /* status text */
4040     SPDesktop *desktop = n->subpath->nodepath->desktop;
4041     if (!desktop) return;
4042     SPEventContext *ec = desktop->event_context;
4043     if (!ec) return;
4045     Inkscape::MessageContext *mc = get_message_context(ec);
4047     if (!mc) return;
4049     double degrees = 180 / M_PI * rnew.a;
4050     if (degrees > 180) degrees -= 360;
4051     if (degrees < -180) degrees += 360;
4052     if (prefs->getBool("/options/compassangledisplay/value"))
4053         degrees = angle_to_compass (degrees);
4055     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4057     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4058          _("<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);
4060     g_string_free(length, TRUE);
4063 /**
4064  * Node handle event callback.
4065  */
4066 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4068     gboolean ret = FALSE;
4069     switch (event->type) {
4070         case GDK_KEY_PRESS:
4071             switch (get_group0_keyval (&event->key)) {
4072                 case GDK_space:
4073                     if (event->key.state & GDK_BUTTON1_MASK) {
4074                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4075                         stamp_repr(nodepath);
4076                         ret = TRUE;
4077                     }
4078                     break;
4079                 default:
4080                     break;
4081             }
4082             break;
4083         case GDK_ENTER_NOTIFY:
4084             // we use an experimentally determined threshold that seems to work fine
4085             if (Geom::L2(n->pos - knot->pos) < 0.75)
4086                 Inkscape::NodePath::Path::active_node = n;
4087             break;
4088         case GDK_LEAVE_NOTIFY:
4089             // we use an experimentally determined threshold that seems to work fine
4090             if (Geom::L2(n->pos - knot->pos) < 0.75)
4091                 Inkscape::NodePath::Path::active_node = NULL;
4092             break;
4093         default:
4094             break;
4095     }
4097     return ret;
4100 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4101                                  Radial &rme, Radial &rother, gboolean const both)
4103     rme.a += angle;
4104     if ( both
4105          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4106          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4107     {
4108         rother.a += angle;
4109     }
4112 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4113                                         Radial &rme, Radial &rother, gboolean const both)
4115     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4117     gdouble r;
4118     if ( both
4119          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4120          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4121     {
4122         r = MAX(rme.r, rother.r);
4123     } else {
4124         r = rme.r;
4125     }
4127     gdouble const weird_angle = atan2(norm_angle, r);
4128 /* Bulia says norm_angle is just the visible distance that the
4129  * object's end must travel on the screen.  Left as 'angle' for want of
4130  * a better name.*/
4132     rme.a += weird_angle;
4133     if ( both
4134          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4135          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4136     {
4137         rother.a += weird_angle;
4138     }
4141 /**
4142  * Rotate one node.
4143  */
4144 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4146     Inkscape::NodePath::NodeSide *me, *other;
4147     bool both = false;
4149     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4150     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4152     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4153         me = &(n->p);
4154         other = &(n->n);
4155     } else if (!n->p.other) {
4156         me = &(n->n);
4157         other = &(n->p);
4158     } else {
4159         if (which > 0) { // right handle
4160             if (xn > xp) {
4161                 me = &(n->n);
4162                 other = &(n->p);
4163             } else {
4164                 me = &(n->p);
4165                 other = &(n->n);
4166             }
4167         } else if (which < 0){ // left handle
4168             if (xn <= xp) {
4169                 me = &(n->n);
4170                 other = &(n->p);
4171             } else {
4172                 me = &(n->p);
4173                 other = &(n->n);
4174             }
4175         } else { // both handles
4176             me = &(n->n);
4177             other = &(n->p);
4178             both = true;
4179         }
4180     }
4182     Radial rme(me->pos - n->pos);
4183     Radial rother(other->pos - n->pos);
4185     if (screen) {
4186         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4187     } else {
4188         node_rotate_one_internal (*n, angle, rme, rother, both);
4189     }
4191     me->pos = n->pos + Geom::Point(rme);
4193     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4194         other->pos =  n->pos + Geom::Point(rother);
4195     }
4197     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4198     // so here we just move all the knots without emitting move signals, for speed
4199     sp_node_update_handles(n, false);
4202 /**
4203  * Rotate selected nodes.
4204  */
4205 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4207     if (!nodepath || !nodepath->selected) return;
4209     if (g_list_length(nodepath->selected) == 1) {
4210        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4211         node_rotate_one (n, angle, which, screen);
4212     } else {
4213        // rotate as an object:
4215         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4216         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4217         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4218             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4219             box.expandTo (n->pos); // contain all selected nodes
4220         }
4222         gdouble rot;
4223         if (screen) {
4224             gdouble const zoom = nodepath->desktop->current_zoom();
4225             gdouble const zmove = angle / zoom;
4226             gdouble const r = Geom::L2(box.max() - box.midpoint());
4227             rot = atan2(zmove, r);
4228         } else {
4229             rot = angle;
4230         }
4232         Geom::Point rot_center;
4233         if (Inkscape::NodePath::Path::active_node == NULL)
4234             rot_center = box.midpoint();
4235         else
4236             rot_center = Inkscape::NodePath::Path::active_node->pos;
4238         Geom::Matrix t =
4239             Geom::Matrix (Geom::Translate(-rot_center)) *
4240             Geom::Matrix (Geom::Rotate(rot)) *
4241             Geom::Matrix (Geom::Translate(rot_center));
4243         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4244             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4245             n->pos *= t;
4246             n->n.pos *= t;
4247             n->p.pos *= t;
4248             sp_node_update_handles(n, false);
4249         }
4250     }
4252     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4255 /**
4256  * Scale one node.
4257  */
4258 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4260     bool both = false;
4261     Inkscape::NodePath::NodeSide *me, *other;
4263     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4264     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4266     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4267         me = &(n->p);
4268         other = &(n->n);
4269         n->code = NR_CURVETO;
4270     } else if (!n->p.other) {
4271         me = &(n->n);
4272         other = &(n->p);
4273         if (n->n.other)
4274             n->n.other->code = NR_CURVETO;
4275     } else {
4276         if (which > 0) { // right handle
4277             if (xn > xp) {
4278                 me = &(n->n);
4279                 other = &(n->p);
4280                 if (n->n.other)
4281                     n->n.other->code = NR_CURVETO;
4282             } else {
4283                 me = &(n->p);
4284                 other = &(n->n);
4285                 n->code = NR_CURVETO;
4286             }
4287         } else if (which < 0){ // left handle
4288             if (xn <= xp) {
4289                 me = &(n->n);
4290                 other = &(n->p);
4291                 if (n->n.other)
4292                     n->n.other->code = NR_CURVETO;
4293             } else {
4294                 me = &(n->p);
4295                 other = &(n->n);
4296                 n->code = NR_CURVETO;
4297             }
4298         } else { // both handles
4299             me = &(n->n);
4300             other = &(n->p);
4301             both = true;
4302             n->code = NR_CURVETO;
4303             if (n->n.other)
4304                 n->n.other->code = NR_CURVETO;
4305         }
4306     }
4308     Radial rme(me->pos - n->pos);
4309     Radial rother(other->pos - n->pos);
4311     rme.r += grow;
4312     if (rme.r < 0) rme.r = 0;
4313     if (rme.a == HUGE_VAL) {
4314         if (me->other) { // if direction is unknown, initialize it towards the next node
4315             Radial rme_next(me->other->pos - n->pos);
4316             rme.a = rme_next.a;
4317         } else { // if there's no next, initialize to 0
4318             rme.a = 0;
4319         }
4320     }
4321     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4322         rother.r += grow;
4323         if (rother.r < 0) rother.r = 0;
4324         if (rother.a == HUGE_VAL) {
4325             rother.a = rme.a + M_PI;
4326         }
4327     }
4329     me->pos = n->pos + Geom::Point(rme);
4331     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4332         other->pos = n->pos + Geom::Point(rother);
4333     }
4335     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4336     // so here we just move all the knots without emitting move signals, for speed
4337     sp_node_update_handles(n, false);
4340 /**
4341  * Scale selected nodes.
4342  */
4343 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4345     if (!nodepath || !nodepath->selected) return;
4347     if (g_list_length(nodepath->selected) == 1) {
4348         // scale handles of the single selected node
4349         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4350         node_scale_one (n, grow, which);
4351     } else {
4352         // scale nodes as an "object":
4354         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4355         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4356         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4357             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4358             box.expandTo (n->pos); // contain all selected nodes
4359         }
4361         if ( Geom::are_near(box.maxExtent(), 0) ) {
4362             SPEventContext *ec = nodepath->desktop->event_context;
4363             if (!ec) return;
4364             Inkscape::MessageContext *mc = get_message_context(ec);
4365             if (!mc) return;
4366             mc->setF(Inkscape::WARNING_MESSAGE,
4367                              _("Cannot scale nodes when all are at the same location."));
4368             return;
4369         }
4370         double scale = (box.maxExtent() + grow)/box.maxExtent();
4373         Geom::Point scale_center;
4374         if (Inkscape::NodePath::Path::active_node == NULL)
4375             scale_center = box.midpoint();
4376         else
4377             scale_center = Inkscape::NodePath::Path::active_node->pos;
4379         Geom::Matrix t =
4380             Geom::Translate(-scale_center) *
4381             Geom::Scale(scale, scale) *
4382             Geom::Translate(scale_center);
4384         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4385             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4386             n->pos *= t;
4387             n->n.pos *= t;
4388             n->p.pos *= t;
4389             sp_node_update_handles(n, false);
4390         }
4391     }
4393     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4396 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4398     if (!nodepath) return;
4399     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4402 /**
4403  * Flip selected nodes horizontally/vertically.
4404  */
4405 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4407     if (!nodepath || !nodepath->selected) return;
4409     if (g_list_length(nodepath->selected) == 1 && !center) {
4410         // flip handles of the single selected node
4411         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4412         double temp = n->p.pos[axis];
4413         n->p.pos[axis] = n->n.pos[axis];
4414         n->n.pos[axis] = temp;
4415         sp_node_update_handles(n, false);
4416     } else {
4417         // scale nodes as an "object":
4419         Geom::Rect box = sp_node_selected_bbox (nodepath);
4420         if (!center) {
4421             center = box.midpoint();
4422         }
4423         Geom::Matrix t =
4424             Geom::Matrix (Geom::Translate(- *center)) *
4425             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4426             Geom::Matrix (Geom::Translate(*center));
4428         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4429             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4430             n->pos *= t;
4431             n->n.pos *= t;
4432             n->p.pos *= t;
4433             sp_node_update_handles(n, false);
4434         }
4435     }
4437     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4440 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4442     g_assert (nodepath->selected);
4444     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4445     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4446     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4447         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4448         box.expandTo (n->pos); // contain all selected nodes
4449     }
4450     return box;
4453 //-----------------------------------------------
4454 /**
4455  * Return new subpath under given nodepath.
4456  */
4457 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4459     g_assert(nodepath);
4460     g_assert(nodepath->desktop);
4462    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4464     s->nodepath = nodepath;
4465     s->closed = FALSE;
4466     s->nodes = NULL;
4467     s->first = NULL;
4468     s->last = NULL;
4470     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4471     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4472     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4474     return s;
4477 /**
4478  * Destroy nodes in subpath, then subpath itself.
4479  */
4480 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4482     g_assert(subpath);
4483     g_assert(subpath->nodepath);
4484     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4486     while (subpath->nodes) {
4487         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4488     }
4490     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4492     g_free(subpath);
4495 /**
4496  * Link head to tail in subpath.
4497  */
4498 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4500     g_assert(!sp->closed);
4501     g_assert(sp->last != sp->first);
4502     g_assert(sp->first->code == NR_MOVETO);
4504     sp->closed = TRUE;
4506     //Link the head to the tail
4507     sp->first->p.other = sp->last;
4508     sp->last->n.other  = sp->first;
4509     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4510     sp->first          = sp->last;
4512     //Remove the extra end node
4513     sp_nodepath_node_destroy(sp->last->n.other);
4516 /**
4517  * Open closed (loopy) subpath at node.
4518  */
4519 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4521     g_assert(sp->closed);
4522     g_assert(n->subpath == sp);
4523     g_assert(sp->first == sp->last);
4525     /* We create new startpoint, current node will become last one */
4527    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4528                                                 &n->pos, &n->pos, &n->n.pos);
4531     sp->closed        = FALSE;
4533     //Unlink to make a head and tail
4534     sp->first         = new_path;
4535     sp->last          = n;
4536     n->n.other        = NULL;
4537     new_path->p.other = NULL;
4540 /**
4541  * Return new node in subpath with given properties.
4542  * \param pos Position of node.
4543  * \param ppos Handle position in previous direction
4544  * \param npos Handle position in previous direction
4545  */
4546 Inkscape::NodePath::Node *
4547 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)
4549     g_assert(sp);
4550     g_assert(sp->nodepath);
4551     g_assert(sp->nodepath->desktop);
4553     if (nodechunk == NULL)
4554         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4556     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4558     n->subpath  = sp;
4560     if (type != Inkscape::NodePath::NODE_NONE) {
4561         // use the type from sodipodi:nodetypes
4562         n->type = type;
4563     } else {
4564         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4565             // points are (almost) collinear
4566             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4567                 // endnode, or a node with a retracted handle
4568                 n->type = Inkscape::NodePath::NODE_CUSP;
4569             } else {
4570                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4571             }
4572         } else {
4573             n->type = Inkscape::NodePath::NODE_CUSP;
4574         }
4575     }
4577     n->code     = code;
4578     n->selected = FALSE;
4579     n->pos      = *pos;
4580     n->p.pos    = *ppos;
4581     n->n.pos    = *npos;
4583     n->dragging_out = NULL;
4585     Inkscape::NodePath::Node *prev;
4586     if (next) {
4587         //g_assert(g_list_find(sp->nodes, next));
4588         prev = next->p.other;
4589     } else {
4590         prev = sp->last;
4591     }
4593     if (prev)
4594         prev->n.other = n;
4595     else
4596         sp->first = n;
4598     if (next)
4599         next->p.other = n;
4600     else
4601         sp->last = n;
4603     n->p.other = prev;
4604     n->n.other = next;
4606     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"));
4607     sp_knot_set_position(n->knot, *pos, 0);
4609     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4610     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4611     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4613     sp_nodepath_update_node_knot(n);
4615     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4616     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4617     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4618     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4619     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4620     sp_knot_show(n->knot);
4622     // We only create handle knots and lines on demand
4623     n->p.knot = NULL;
4624     n->p.line = NULL;
4625     n->n.knot = NULL;
4626     n->n.line = NULL;
4628     sp->nodes = g_list_prepend(sp->nodes, n);
4630     return n;
4633 /**
4634  * Destroy node and its knots, link neighbors in subpath.
4635  */
4636 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4638     g_assert(node);
4639     g_assert(node->subpath);
4640     g_assert(SP_IS_KNOT(node->knot));
4642    Inkscape::NodePath::SubPath *sp = node->subpath;
4644     if (node->selected) { // first, deselect
4645         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4646         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4647     }
4649     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4651     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4652     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4653     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4654     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4655     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4656     g_object_unref(G_OBJECT(node->knot));
4658     if (node->p.knot) {
4659         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4660         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4661         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4662         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4663         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4664         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4665         g_object_unref(G_OBJECT(node->p.knot));
4666         node->p.knot = NULL;
4667     }
4669     if (node->n.knot) {
4670         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4671         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4672         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4673         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4674         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4675         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4676         g_object_unref(G_OBJECT(node->n.knot));
4677         node->n.knot = NULL;
4678     }
4680     if (node->p.line)
4681         gtk_object_destroy(GTK_OBJECT(node->p.line));
4682     if (node->n.line)
4683         gtk_object_destroy(GTK_OBJECT(node->n.line));
4685     if (sp->nodes) { // there are others nodes on the subpath
4686         if (sp->closed) {
4687             if (sp->first == node) {
4688                 g_assert(sp->last == node);
4689                 sp->first = node->n.other;
4690                 sp->last = sp->first;
4691             }
4692             node->p.other->n.other = node->n.other;
4693             node->n.other->p.other = node->p.other;
4694         } else {
4695             if (sp->first == node) {
4696                 sp->first = node->n.other;
4697                 sp->first->code = NR_MOVETO;
4698             }
4699             if (sp->last == node) sp->last = node->p.other;
4700             if (node->p.other) node->p.other->n.other = node->n.other;
4701             if (node->n.other) node->n.other->p.other = node->p.other;
4702         }
4703     } else { // this was the last node on subpath
4704         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4705     }
4707     g_mem_chunk_free(nodechunk, node);
4710 /**
4711  * Returns one of the node's two sides.
4712  * \param which Indicates which side.
4713  * \return Pointer to previous node side if which==-1, next if which==1.
4714  */
4715 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4717     g_assert(node);
4718     Inkscape::NodePath::NodeSide * result = 0;
4719     switch (which) {
4720         case -1:
4721             result = &node->p;
4722             break;
4723         case 1:
4724             result = &node->n;
4725             break;
4726         default:
4727             g_assert_not_reached();
4728     }
4730     return result;
4733 /**
4734  * Return the other side of the node, given one of its sides.
4735  */
4736 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4738     g_assert(node);
4739     Inkscape::NodePath::NodeSide *result = 0;
4741     if (me == &node->p) {
4742         result = &node->n;
4743     } else if (me == &node->n) {
4744         result = &node->p;
4745     } else {
4746         g_assert_not_reached();
4747     }
4749     return result;
4752 /**
4753  * Return NRPathcode on the given side of the node.
4754  */
4755 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4757     g_assert(node);
4759     NRPathcode result = NR_END;
4760     if (me == &node->p) {
4761         if (node->p.other) {
4762             result = (NRPathcode)node->code;
4763         } else {
4764             result = NR_MOVETO;
4765         }
4766     } else if (me == &node->n) {
4767         if (node->n.other) {
4768             result = (NRPathcode)node->n.other->code;
4769         } else {
4770             result = NR_MOVETO;
4771         }
4772     } else {
4773         g_assert_not_reached();
4774     }
4776     return result;
4779 /**
4780  * Return node with the given index
4781  */
4782 Inkscape::NodePath::Node *
4783 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4785     Inkscape::NodePath::Node *e = NULL;
4787     if (!nodepath) {
4788         return e;
4789     }
4791     //find segment
4792     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4794         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4795         int n = g_list_length(sp->nodes);
4796         if (sp->closed) {
4797             n++;
4798         }
4800         //if the piece belongs to this subpath grab it
4801         //otherwise move onto the next subpath
4802         if (index < n) {
4803             e = sp->first;
4804             for (int i = 0; i < index; ++i) {
4805                 e = e->n.other;
4806             }
4807             break;
4808         } else {
4809             if (sp->closed) {
4810                 index -= (n+1);
4811             } else {
4812                 index -= n;
4813             }
4814         }
4815     }
4817     return e;
4820 /**
4821  * Returns plain text meaning of node type.
4822  */
4823 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4825     unsigned retracted = 0;
4826     bool endnode = false;
4828     for (int which = -1; which <= 1; which += 2) {
4829         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4830         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4831             retracted ++;
4832         if (!side->other)
4833             endnode = true;
4834     }
4836     if (retracted == 0) {
4837         if (endnode) {
4838                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4839                 return _("end node");
4840         } else {
4841             switch (node->type) {
4842                 case Inkscape::NodePath::NODE_CUSP:
4843                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4844                     return _("cusp");
4845                 case Inkscape::NodePath::NODE_SMOOTH:
4846                     // TRANSLATORS: "smooth" is an adjective here
4847                     return _("smooth");
4848                 case Inkscape::NodePath::NODE_AUTO:
4849                     return _("auto");
4850                 case Inkscape::NodePath::NODE_SYMM:
4851                     return _("symmetric");
4852             }
4853         }
4854     } else if (retracted == 1) {
4855         if (endnode) {
4856             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4857             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4858         } else {
4859             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4860         }
4861     } else {
4862         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4863     }
4865     return NULL;
4868 /**
4869  * Handles content of statusbar as long as node tool is active.
4870  */
4871 void
4872 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4874     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");
4875     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4877     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4878     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4879     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4880     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4882     SPDesktop *desktop = NULL;
4883     if (nodepath) {
4884         desktop = nodepath->desktop;
4885     } else {
4886         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4887     }
4889     SPEventContext *ec = desktop->event_context;
4890     if (!ec) return;
4892     Inkscape::MessageContext *mc = get_message_context(ec);
4893     if (!mc) return;
4895     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4897     if (selected_nodes == 0) {
4898         Inkscape::Selection *sel = desktop->selection;
4899         if (!sel || sel->isEmpty()) {
4900             mc->setF(Inkscape::NORMAL_MESSAGE,
4901                      _("Select a single object to edit its nodes or handles."));
4902         } else {
4903             if (nodepath) {
4904             mc->setF(Inkscape::NORMAL_MESSAGE,
4905                      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.",
4906                               "<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.",
4907                               total_nodes),
4908                      total_nodes);
4909             } else {
4910                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4911                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4912                 } else {
4913                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4914                 }
4915             }
4916         }
4917     } else if (nodepath && selected_nodes == 1) {
4918         mc->setF(Inkscape::NORMAL_MESSAGE,
4919                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4920                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4921                           total_nodes),
4922                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4923     } else {
4924         if (selected_subpaths > 1) {
4925             mc->setF(Inkscape::NORMAL_MESSAGE,
4926                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4927                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4928                               total_nodes),
4929                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4930         } else {
4931             mc->setF(Inkscape::NORMAL_MESSAGE,
4932                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4933                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4934                               total_nodes),
4935                      selected_nodes, total_nodes, when_selected);
4936         }
4937     }
4940 /*
4941  * returns a *copy* of the curve of that object.
4942  */
4943 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4944     if (!object)
4945         return NULL;
4947     SPCurve *curve = NULL;
4948     if (SP_IS_PATH(object)) {
4949         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4950         curve = curve_new->copy();
4951     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4952         const gchar *svgd = object->repr->attribute(key);
4953         if (svgd) {
4954             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4955             SPCurve *curve_new = new SPCurve(pv);
4956             if (curve_new) {
4957                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4958             }
4959         }
4960     }
4962     return curve;
4965 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4966     if (!np || !np->object || !curve)
4967         return;
4969     if (SP_IS_PATH(np->object)) {
4970         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4971             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4972         } else {
4973             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4974         }
4975     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4976         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
4977         if (lpe) {
4978             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
4979             if (pathparam) {
4980                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4981                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4982             }
4983         }
4984     }
4987 /*
4988 SPCanvasItem *
4989 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4990     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
4992 */
4994 /*
4995 SPCanvasItem *
4996 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4997     SPCurve *flash_curve = curve->copy();
4998     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4999     flash_curve->transform(i2d);
5000     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5001     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5002     // unless we also flash the nodes...
5003     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5004     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5005     sp_canvas_item_show(canvasitem);
5006     flash_curve->unref();
5007     return canvasitem;
5010 SPCanvasItem *
5011 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
5012     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5013     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
5014                                            prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff));
5016 */
5018 SPCanvasItem *
5019 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
5020     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
5021     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
5022     flash_curve->transform(i2d);
5023     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5024     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5025     // unless we also flash the nodes...
5026     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5027     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5028     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5029     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5030     sp_canvas_item_show(canvasitem);
5031     flash_curve->unref();
5032     return canvasitem;
5035 // TODO: Merge this with sp_nodepath_make_helper_item()!
5036 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5037     np->show_helperpath = show;
5039     if (show) {
5040         SPCurve *helper_curve = np->curve->copy();
5041         helper_curve->transform(np->i2d);
5042         if (!np->helper_path) {
5043             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5045             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5046             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);
5047             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5048             sp_canvas_item_move_to_z(np->helper_path, 0);
5049             sp_canvas_item_show(np->helper_path);
5050         } else {
5051             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5052         }
5053         helper_curve->unref();
5054     } else {
5055         if (np->helper_path) {
5056             GtkObject *temp = np->helper_path;
5057             np->helper_path = NULL;
5058             gtk_object_destroy(temp);
5059         }
5060     }
5063 /* sp_nodepath_make_straight_path:
5064  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5065  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5066  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5067  */
5068 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5069     np->straight_path = true;
5070     np->show_handles = false;
5071     g_message("add code to make the path straight.");
5072     // do sp_nodepath_convert_node_type on all nodes?
5073     // coding tip: search for this text : "Make selected segments lines"
5076 /*
5077   Local Variables:
5078   mode:c++
5079   c-file-style:"stroustrup"
5080   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5081   indent-tabs-mode:nil
5082   fill-column:99
5083   End:
5084 */
5085 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :