Code

Merge from trunk
[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 *p, guint state, gpointer data);
131 /* Handle event callbacks */
132 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
134 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
135 static gboolean node_handle_request(SPKnot *knot, Geom::Point *p, guint state, gpointer data);
136 static void node_handle_moved(SPKnot *knot, Geom::Point *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     }
245 /**
246  * \brief Creates new nodepath from item
247  */
248 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
250     Inkscape::XML::Node *repr = object->repr;
252     /** \todo
253      * FIXME: remove this. We don't want to edit paths inside flowtext.
254      * Instead we will build our flowtext with cloned paths, so that the
255      * real paths are outside the flowtext and thus editable as usual.
256      */
257     if (SP_IS_FLOWTEXT(object)) {
258         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
259             if SP_IS_FLOWREGION(child) {
260                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
261                 if (grandchild && SP_IS_PATH(grandchild)) {
262                     object = SP_ITEM(grandchild);
263                     break;
264                 }
265             }
266         }
267     }
269     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
271     if (curve == NULL) {
272         return NULL;
273     }
275     if (curve->get_segment_count() < 1) {
276         curve->unref();
277         return NULL; // prevent crash for one-node paths
278     }
280     //Create new nodepath
281     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
282     if (!np) {
283         curve->unref();
284         return NULL;
285     }
287     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
289     // Set defaults
290     np->desktop     = desktop;
291     np->object      = object;
292     np->subpaths    = NULL;
293     np->selected    = NULL;
294     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
295     np->local_change = 0;
296     np->show_handles = show_handles;
297     np->helper_path = NULL;
298     np->helper_path_vec = new HelperPathList;
299     np->helperpath_rgba = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
300     np->helperpath_width = 1.0;
301     np->curve = curve->copy();
302     np->show_helperpath = prefs->getBool("/tools/nodes/show_helperpath");
303     if (SP_IS_LPE_ITEM(object)) {
304         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
305         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
306             np->show_helperpath = true;
307         }            
308     }
309     np->straight_path = false;
310     if (IS_LIVEPATHEFFECT(object) && item) {
311         np->item = item;
312     } else {
313         np->item = SP_ITEM(object);
314     }
316     // we need to update item's transform from the repr here,
317     // because they may be out of sync when we respond
318     // to a change in repr by regenerating nodepath     --bb
319     sp_object_read_attr(SP_OBJECT(np->item), "transform");
321     np->i2d  = sp_item_i2d_affine(np->item);
322     np->d2i  = np->i2d.inverse();
324     np->repr = repr;
325     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)
326         np->repr_key = g_strdup(repr_key_in);
327         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
328         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe();
329         if (!lpe) {
330             g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!");
331             sp_nodepath_destroy(np);
332         }
333         Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in);
334         if (lpeparam) {
335             lpeparam->param_setup_nodepath(np);
336         }
337     } else {
338         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
339         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
340             np->repr_key = g_strdup("inkscape:original-d");
342             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
343             if (lpe) {
344                 lpe->setup_nodepath(np);
345             }
346         } else {
347             np->repr_key = g_strdup("d");
348         }
349     }
351     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
352      * So for example a closed rectangle has a nodetypestring of length 5.
353      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
354     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
355     np->curve->set_pathvector(pathv_sanitized);
356     guint length = np->curve->get_segment_count();
357     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
358         length += pit->empty() ? 0 : 1;
359     }
361     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
362     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
364     // create the subpath(s) from the bpath
365     subpaths_from_pathvector(np, pathv_sanitized, typestr);
367     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
368     np->subpaths = g_list_reverse(np->subpaths);
370     delete[] typestr;
371     curve->unref();
373     // Draw helper curve
374     if (np->show_helperpath) {
375         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
376     }
378     sp_nodepath_create_helperpaths(np);
380     return np;
383 /**
384  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
385  */
386 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
388     if (!np) {  //soft fail, like delete
389         return;
390     }
392     while (np->subpaths) {
393         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
394     }
396     //Inform the ShapeEditor that made me, if any, that I am gone.
397     if (np->shape_editor)
398         np->shape_editor->nodepath_destroyed();
400     g_assert(!np->selected);
402     if (np->helper_path) {
403         GtkObject *temp = np->helper_path;
404         np->helper_path = NULL;
405         gtk_object_destroy(temp);
406     }
407     if (np->curve) {
408         np->curve->unref();
409         np->curve = NULL;
410     }
412     if (np->repr_key) {
413         g_free(np->repr_key);
414         np->repr_key = NULL;
415     }
416     if (np->repr_nodetypes_key) {
417         g_free(np->repr_nodetypes_key);
418         np->repr_nodetypes_key = NULL;
419     }
421     sp_nodepath_destroy_helperpaths(np);
422     delete np->helper_path_vec;
423     np->helper_path_vec = NULL;
425     np->desktop = NULL;
427     g_free(np);
430 /**
431  *  Return the node count of a given NodeSubPath.
432  */
433 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
435     int nodeCount = 0;
437     if (subpath) {
438         nodeCount = g_list_length(subpath->nodes);
439     }
441     return nodeCount;
444 /**
445  *  Return the node count of a given NodePath.
446  */
447 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
449     gint nodeCount = 0;
450     if (np) {
451         for (GList *item = np->subpaths ; item ; item=item->next) {
452             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
453             nodeCount += g_list_length(subpath->nodes);
454         }
455     }
456     return nodeCount;
459 /**
460  *  Return the subpath count of a given NodePath.
461  */
462 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
464     gint nodeCount = 0;
465     if (np) {
466         nodeCount = g_list_length(np->subpaths);
467     }
468     return nodeCount;
471 /**
472  *  Return the selected node count of a given NodePath.
473  */
474 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
476     gint nodeCount = 0;
477     if (np) {
478         nodeCount = g_list_length(np->selected);
479     }
480     return nodeCount;
483 /**
484  *  Return the number of subpaths where nodes are selected in a given NodePath.
485  */
486 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
488     gint nodeCount = 0;
489     if (np && np->selected) {
490         if (!np->selected->next) {
491             nodeCount = 1;
492         } else {
493             for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
494                 Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
495                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
496                     Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
497                     if (node->selected) {
498                         nodeCount++;
499                         break;
500                     }
501                 }
502             }
503         }
504     }
505     return nodeCount;
508 /**
509  * Clean up a nodepath after editing.
510  *
511  * Currently we are deleting trivial subpaths.
512  */
513 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
515     GList *badSubPaths = NULL;
517     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
518     for (GList *l = nodepath->subpaths; l ; l=l->next) {
519        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
520        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
521             badSubPaths = g_list_append(badSubPaths, sp);
522     }
524     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
525     //also removes the subpath from nodepath->subpaths
526     for (GList *l = badSubPaths; l ; l=l->next) {
527        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
528         sp_nodepath_subpath_destroy(sp);
529     }
531     g_list_free(badSubPaths);
534 /**
535  * Create new nodepaths from pathvector, make it subpaths of np.
536  * \param t The node type array.
537  */
538 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
540     guint i = 0;  // index into node type array
541     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
542         if (pit->empty())
543             continue;  // don't add single knot paths
545         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
547         Geom::Point ppos = pit->initialPoint() * np->i2d;
548         NRPathcode pcode = NR_MOVETO;
550         /* 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)*/
551         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
552             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
553                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
554                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
555             {
556                 Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
557                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
559                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
560                 pcode = NR_LINETO;
561             }
562             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
563                 std::vector<Geom::Point> points = cubic_bezier->points();
564                 Geom::Point pos = points[0] * (Geom::Matrix)np->i2d;
565                 Geom::Point npos = points[1] * (Geom::Matrix)np->i2d;
566                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
568                 ppos = points[2] * (Geom::Matrix)np->i2d;
569                 pcode = NR_CURVETO;
570             }
571         }
573         if (pit->closed()) {
574             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
575             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
576              * If the length is zero, don't add it to the nodepath. */
577             Geom::Curve const &closing_seg = pit->back_closed();
578             // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289)
579             if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) {
580                 Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
581                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
582             }
584             sp_nodepath_subpath_close(sp);
585         }
586     }
589 /**
590  * Convert from sodipodi:nodetypes to new style type array.
591  */
592 static
593 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
595     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
597     guint pos = 0;
599     if (types) {
600         for (guint i = 0; types[i] && ( i < length ); i++) {
601             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
602             if (types[i] != '\0') {
603                 switch (types[i]) {
604                     case 's':
605                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
606                         break;
607                     case 'a':
608                         typestr[pos++] =Inkscape::NodePath::NODE_AUTO;
609                         break;
610                     case 'z':
611                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
612                         break;
613                     case 'c':
614                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
615                         break;
616                     default:
617                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
618                         break;
619                 }
620             }
621         }
622     }
624     while (pos < length) {
625         typestr[pos++] = Inkscape::NodePath::NODE_NONE;
626     }
628     return typestr;
631 /**
632  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
633  * updated but repr is not (for speed). Used during curve and node drag.
634  */
635 static void update_object(Inkscape::NodePath::Path *np)
637     g_assert(np);
639     np->curve->unref();
640     np->curve = create_curve(np);
642     sp_nodepath_set_curve(np, np->curve);
644     if (np->show_helperpath) {
645         SPCurve * helper_curve = np->curve->copy();
646         helper_curve->transform(np->i2d);
647         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
648         helper_curve->unref();
649     }
651     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
652     //sp_nodepath_update_helperpaths(np);
654     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
655     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
656     np->shape_editor->update_knotholder();
659 /**
660  * Update XML path node with data from path object.
661  */
662 static void update_repr_internal(Inkscape::NodePath::Path *np)
664     g_assert(np);
666     Inkscape::XML::Node *repr = np->object->repr;
668     np->curve->unref();
669     np->curve = create_curve(np);
671     gchar *typestr = create_typestr(np);
672     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
674     // determine if path has an effect applied and write to correct "d" attribute.
675     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
676         np->local_change++;
677         repr->setAttribute(np->repr_key, svgpath);
678     }
680     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
681         np->local_change++;
682         repr->setAttribute(np->repr_nodetypes_key, typestr);
683     }
685     g_free(svgpath);
686     g_free(typestr);
688     if (np->show_helperpath) {
689         SPCurve * helper_curve = np->curve->copy();
690         helper_curve->transform(np->i2d);
691         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
692         helper_curve->unref();
693     }
695     // TODO: do we need this call here? after all, update_object() should have been called just before
696     //sp_nodepath_update_helperpaths(np);
699 /**
700  * Update XML path node with data from path object, commit changes forever.
701  */
702 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
704     //fixme: np can be NULL, so check before proceeding
705     g_return_if_fail(np != NULL);
707     update_repr_internal(np);
708     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
710     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
711                      annotation);
714 /**
715  * Update XML path node with data from path object, commit changes with undo.
716  */
717 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
719     update_repr_internal(np);
720     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
721                            annotation);
724 /**
725  * Make duplicate of path, replace corresponding XML node in tree, commit.
726  */
727 static void stamp_repr(Inkscape::NodePath::Path *np)
729     g_assert(np);
731     Inkscape::XML::Node *old_repr = np->object->repr;
732     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
734     // remember the position of the item
735     gint pos = old_repr->position();
736     // remember parent
737     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
739     SPCurve *curve = create_curve(np);
740     gchar *typestr = create_typestr(np);
742     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
744     new_repr->setAttribute(np->repr_key, svgpath);
745     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
747     // add the new repr to the parent
748     parent->appendChild(new_repr);
749     // move to the saved position
750     new_repr->setPosition(pos > 0 ? pos : 0);
752     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
753                      _("Stamp"));
755     Inkscape::GC::release(new_repr);
756     g_free(svgpath);
757     g_free(typestr);
758     curve->unref();
761 /**
762  * Create curve from path.
763  */
764 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
766     SPCurve *curve = new SPCurve();
768     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
769        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
770        curve->moveto(sp->first->pos * np->d2i);
771        Inkscape::NodePath::Node *n = sp->first->n.other;
772         while (n) {
773             Geom::Point const end_pt = n->pos * np->d2i;
774             switch (n->code) {
775                 case NR_LINETO:
776                     curve->lineto(end_pt);
777                     break;
778                 case NR_CURVETO:
779                     curve->curveto(n->p.other->n.pos * np->d2i,
780                                      n->p.pos * np->d2i,
781                                      end_pt);
782                     break;
783                 default:
784                     g_assert_not_reached();
785                     break;
786             }
787             if (n != sp->last) {
788                 n = n->n.other;
789             } else {
790                 n = NULL;
791             }
792         }
793         if (sp->closed) {
794             curve->closepath();
795         }
796     }
798     return curve;
801 /**
802  * Convert path type string to sodipodi:nodetypes style.
803  */
804 static gchar *create_typestr(Inkscape::NodePath::Path *np)
806     gchar *typestr = g_new(gchar, 32);
807     gint len = 32;
808     gint pos = 0;
810     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
811        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
813         if (pos >= len) {
814             typestr = g_renew(gchar, typestr, len + 32);
815             len += 32;
816         }
818         typestr[pos++] = 'c';
820        Inkscape::NodePath::Node *n;
821         n = sp->first->n.other;
822         while (n) {
823             gchar code;
825             switch (n->type) {
826                 case Inkscape::NodePath::NODE_CUSP:
827                     code = 'c';
828                     break;
829                 case Inkscape::NodePath::NODE_SMOOTH:
830                     code = 's';
831                     break;
832                 case Inkscape::NodePath::NODE_AUTO:
833                     code = 'a';
834                     break;
835                 case Inkscape::NodePath::NODE_SYMM:
836                     code = 'z';
837                     break;
838                 default:
839                     g_assert_not_reached();
840                     code = '\0';
841                     break;
842             }
844             if (pos >= len) {
845                 typestr = g_renew(gchar, typestr, len + 32);
846                 len += 32;
847             }
849             typestr[pos++] = code;
851             if (n != sp->last) {
852                 n = n->n.other;
853             } else {
854                 n = NULL;
855             }
856         }
857     }
859     if (pos >= len) {
860         typestr = g_renew(gchar, typestr, len + 1);
861         len += 1;
862     }
864     typestr[pos++] = '\0';
866     return typestr;
869 // Returns different message contexts depending on the current context. This function should only
870 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
871 // other cases.
872 static Inkscape::MessageContext *
873 get_message_context(SPEventContext *ec)
875     Inkscape::MessageContext *mc = 0;
877     if (SP_IS_NODE_CONTEXT(ec)) {
878         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
879     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
880         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
881     } else {
882         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
883     }
885     return mc;
888 /**
889  \brief Fills node and handle positions for three nodes, splitting line
890   marked by end at distance t.
891  */
892 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
894     g_assert(new_path != NULL);
895     g_assert(end      != NULL);
897     g_assert(end->p.other == new_path);
898    Inkscape::NodePath::Node *start = new_path->p.other;
899     g_assert(start);
901     if (end->code == NR_LINETO) {
902         new_path->type =Inkscape::NodePath::NODE_CUSP;
903         new_path->code = NR_LINETO;
904         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
905     } else {
906         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
907         new_path->code = NR_CURVETO;
908         gdouble s      = 1 - t;
909         for (int dim = 0; dim < 2; dim++) {
910             Geom::Coord const f000 = start->pos[dim];
911             Geom::Coord const f001 = start->n.pos[dim];
912             Geom::Coord const f011 = end->p.pos[dim];
913             Geom::Coord const f111 = end->pos[dim];
914             Geom::Coord const f00t = s * f000 + t * f001;
915             Geom::Coord const f01t = s * f001 + t * f011;
916             Geom::Coord const f11t = s * f011 + t * f111;
917             Geom::Coord const f0tt = s * f00t + t * f01t;
918             Geom::Coord const f1tt = s * f01t + t * f11t;
919             Geom::Coord const fttt = s * f0tt + t * f1tt;
920             start->n.pos[dim]    = f00t;
921             new_path->p.pos[dim] = f0tt;
922             new_path->pos[dim]   = fttt;
923             new_path->n.pos[dim] = f1tt;
924             end->p.pos[dim]      = f11t;
925         }
926     }
929 /**
930  * Adds new node on direct line between two nodes, activates handles of all
931  * three nodes.
932  */
933 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
935     g_assert(end);
936     g_assert(end->subpath);
937     g_assert(g_list_find(end->subpath->nodes, end));
939    Inkscape::NodePath::Node *start = end->p.other;
940     g_assert( start->n.other == end );
941    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
942                                                end,
943                                                (NRPathcode)end->code == NR_LINETO?
944                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
945                                                (NRPathcode)end->code,
946                                                &start->pos, &start->pos, &start->n.pos);
947     sp_nodepath_line_midpoint(newnode, end, t);
949     sp_node_adjust_handles(start);
950     sp_node_update_handles(start);
951     sp_node_update_handles(newnode);
952     sp_node_adjust_handles(end);
953     sp_node_update_handles(end);
955     return newnode;
958 /**
959 \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
960 */
961 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
963     g_assert(node);
964     g_assert(node->subpath);
965     g_assert(g_list_find(node->subpath->nodes, node));
967     Inkscape::NodePath::Node* result = 0;
968     Inkscape::NodePath::SubPath *sp = node->subpath;
969     Inkscape::NodePath::Path *np    = sp->nodepath;
971     if (sp->closed) {
972         sp_nodepath_subpath_open(sp, node);
973         result = sp->first;
974     } else if ( (node == sp->first) || (node == sp->last ) ){
975         // no break for end nodes
976         result = 0;
977     } else {
978         // create a new subpath
979         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
981         // duplicate the break node as start of the new subpath
982         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
983                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
984                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
986         // attach rest of curve to new node
987         g_assert(node->n.other);
988         newnode->n.other = node->n.other; node->n.other = NULL;
989         newnode->n.other->p.other = newnode;
990         newsubpath->last = sp->last;
991         sp->last = node;
992         node = newnode;
993         while (node->n.other) {
994             node = node->n.other;
995             node->subpath = newsubpath;
996             sp->nodes = g_list_remove(sp->nodes, node);
997             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
998         }
1001         result = newnode;
1002     }
1003     return result;
1006 /**
1007  * Duplicate node and connect to neighbours.
1008  */
1009 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
1011     g_assert(node);
1012     g_assert(node->subpath);
1013     g_assert(g_list_find(node->subpath->nodes, node));
1015    Inkscape::NodePath::SubPath *sp = node->subpath;
1017     NRPathcode code = (NRPathcode) node->code;
1018     if (code == NR_MOVETO) { // if node is the endnode,
1019         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1020     }
1022     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1024     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1025         return node;
1026     } else {
1027         return newnode; // otherwise select the newly created node
1028     }
1031 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1033     node->p.pos = (node->pos + (node->pos - node->n.pos));
1036 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1038     node->n.pos = (node->pos + (node->pos - node->p.pos));
1041 /**
1042  * Change line type at node, with side effects on neighbours.
1043  */
1044 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1046     g_assert(end);
1047     g_assert(end->subpath);
1048     g_assert(end->p.other);
1050     if (end->code != static_cast<guint>(code) ) {
1051         Inkscape::NodePath::Node *start = end->p.other;
1053         end->code = code;
1055         if (code == NR_LINETO) {
1056             if (start->code == NR_LINETO) {
1057                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1058             }
1059             if (end->n.other) {
1060                 if (end->n.other->code == NR_LINETO) {
1061                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1062                 }
1063             }
1065             if (start->type == Inkscape::NodePath::NODE_AUTO)
1066                 start->type = Inkscape::NodePath::NODE_SMOOTH;
1067             if (end->type == Inkscape::NodePath::NODE_AUTO)
1068                 end->type = Inkscape::NodePath::NODE_SMOOTH;
1069     
1070             sp_node_adjust_handle(start, -1);
1071             sp_node_adjust_handle(end, 1);
1073         } else {
1074             Geom::Point delta = end->pos - start->pos;
1075             start->n.pos = start->pos + delta / 3;
1076             end->p.pos = end->pos - delta / 3;
1077             sp_node_adjust_handle(start, 1);
1078             sp_node_adjust_handle(end, -1);
1079         }
1081         sp_node_update_handles(start);
1082         sp_node_update_handles(end);
1083     }
1086 static void
1087 sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
1089     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1090         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1091         node->knot->setSize (node->selected? 11 : 9);
1092         sp_knot_update_ctrl(node->knot);
1093     } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
1094         node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1095         node->knot->setSize (node->selected? 11 : 9);
1096         sp_knot_update_ctrl(node->knot);
1097     } else {
1098         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1099         node->knot->setSize (node->selected? 9 : 7);
1100         sp_knot_update_ctrl(node->knot);
1101     }
1105 /**
1106  * Change node type, and its handles accordingly.
1107  */
1108 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1110     g_assert(node);
1111     g_assert(node->subpath);
1113     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1114         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1115             type =Inkscape::NodePath::NODE_CUSP;
1116         }
1117     }
1119     node->type = type;
1121     sp_nodepath_update_node_knot(node);
1123     // if one of handles is mouseovered, preserve its position
1124     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1125         sp_node_adjust_handle(node, 1);
1126     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1127         sp_node_adjust_handle(node, -1);
1128     } else {
1129         sp_node_adjust_handles(node);
1130     }
1132     sp_node_update_handles(node);
1134     sp_nodepath_update_statusbar(node->subpath->nodepath);
1136     return node;
1139 bool
1140 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1142 // TODO clean up multiple returns
1143         Inkscape::NodePath::Node *othernode = side->other;
1144         if (!othernode)
1145             return false;
1146         NRPathcode const code = sp_node_path_code_from_side(node, side);
1147         if (code == NR_LINETO)
1148             return true;
1149         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1150         if (&node->p == side) {
1151             other_to_me = &othernode->n;
1152         } else if (&node->n == side) {
1153             other_to_me = &othernode->p;
1154         } 
1155         if (!other_to_me)
1156             return false;
1157         bool is_line = 
1158              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1159               Geom::L2(node->pos - side->pos) < 1e-6);
1160         return is_line;
1163 /**
1164  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1165  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1166  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1167  * If already cusp and set to cusp, retracts handles.
1168 */
1169 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1171     if (type == Inkscape::NodePath::NODE_AUTO) {
1172         if (node->p.other != NULL)
1173             node->code = NR_CURVETO;
1174         if (node->n.other != NULL)
1175             node->n.other->code = NR_CURVETO;
1176     }
1178     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1180 /* 
1181   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1182  
1183         if (two_handles) {
1184             // do nothing, adjust_handles called via set_node_type will line them up
1185         } else if (one_handle) {
1186             if (opposite_to_handle_is_line) {
1187                 if (lined_up) {
1188                     // already half-smooth; pull opposite handle too making it fully smooth
1189                 } else {
1190                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1191                 }
1192             } else {
1193                 // pull opposite handle in line with the existing one
1194             }
1195         } else if (no_handles) {
1196             if (both_segments_are_lines OR both_segments_are_curves) {
1197                 //pull both handles
1198             } else {
1199                 // pull the handle opposite to line segment, making node half-smooth
1200             }
1201         }
1202 */
1203         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1204         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1205         bool p_is_line = sp_node_side_is_line(node, &node->p);
1206         bool n_is_line = sp_node_side_is_line(node, &node->n);
1208         if (p_has_handle && n_has_handle) {
1209             // do nothing, adjust_handles will line them up
1210         } else if (p_has_handle || n_has_handle) {
1211             if (p_has_handle && n_is_line) {
1212                 Radial line (node->n.other->pos - node->pos);
1213                 Radial handle (node->pos - node->p.pos);
1214                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1215                     // already half-smooth; pull opposite handle too making it fully smooth
1216                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1217                 } else {
1218                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1219                 }
1220             } else if (n_has_handle && p_is_line) {
1221                 Radial line (node->p.other->pos - node->pos);
1222                 Radial handle (node->pos - node->n.pos);
1223                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1224                     // already half-smooth; pull opposite handle too making it fully smooth
1225                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1226                 } else {
1227                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1228                 }
1229             } else if (p_has_handle && node->n.other) {
1230                 // pull n handle
1231                 node->n.other->code = NR_CURVETO;
1232                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1233                     Geom::L2(node->p.pos - node->pos) :
1234                     Geom::L2(node->n.other->pos - node->pos) / 3;
1235                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1236             } else if (n_has_handle && node->p.other) {
1237                 // pull p handle
1238                 node->code = NR_CURVETO;
1239                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1240                     Geom::L2(node->n.pos - node->pos) :
1241                     Geom::L2(node->p.other->pos - node->pos) / 3;
1242                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1243             }
1244         } else if (!p_has_handle && !n_has_handle) {
1245             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1246                 // no handles, but both segments are either lnes or curves:
1247                 //pull both handles
1249                 // convert both to curves:
1250                 node->code = NR_CURVETO;
1251                 node->n.other->code = NR_CURVETO;
1253                 sp_node_adjust_handles_auto(node);
1254             } else {
1255                 // pull the handle opposite to line segment, making it half-smooth
1256                 if (p_is_line && node->n.other) {
1257                     if (type != Inkscape::NodePath::NODE_SYMM) {
1258                         // pull n handle
1259                         node->n.other->code = NR_CURVETO;
1260                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1261                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1262                     }
1263                 } else if (n_is_line && node->p.other) {
1264                     if (type != Inkscape::NodePath::NODE_SYMM) {
1265                         // pull p handle
1266                         node->code = NR_CURVETO;
1267                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1268                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1269                     }
1270                 }
1271             }
1272         }
1273     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1274         // cusping a cusp: retract nodes
1275         node->p.pos = node->pos;
1276         node->n.pos = node->pos;
1277     }
1279     sp_nodepath_set_node_type (node, type);
1282 /**
1283  * Move node to point, and adjust its and neighbouring handles.
1284  */
1285 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1287     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1288         node->pos = p;
1289         sp_node_adjust_handles_auto(node);
1290     } else {
1291         Geom::Point delta = p - node->pos;
1292         node->pos = p;
1294         node->p.pos += delta;
1295         node->n.pos += delta;
1296     }
1298     Inkscape::NodePath::Node *node_p = NULL;
1299     Inkscape::NodePath::Node *node_n = NULL;
1301     if (node->p.other) {
1302         if (node->code == NR_LINETO) {
1303             sp_node_adjust_handle(node, 1);
1304             sp_node_adjust_handle(node->p.other, -1);
1305             node_p = node->p.other;
1306         }
1307         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1308             sp_node_adjust_handles_auto(node->p.other);
1309             node_p = node->p.other;
1310         }
1311     }
1312     if (node->n.other) {
1313         if (node->n.other->code == NR_LINETO) {
1314             sp_node_adjust_handle(node, -1);
1315             sp_node_adjust_handle(node->n.other, 1);
1316             node_n = node->n.other;
1317         }
1318         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1319             sp_node_adjust_handles_auto(node->n.other);
1320             node_n = node->n.other;
1321         }
1322     }
1324     // this function is only called from batch movers that will update display at the end
1325     // themselves, so here we just move all the knots without emitting move signals, for speed
1326     sp_node_update_handles(node, false);
1327     if (node_n) {
1328         sp_node_update_handles(node_n, false);
1329     }
1330     if (node_p) {
1331         sp_node_update_handles(node_p, false);
1332     }
1335 /**
1336  * Call sp_node_moveto() for node selection and handle possible snapping.
1337  */
1338 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1339                                             bool const snap, bool constrained = false, 
1340                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1342     Geom::Coord best = NR_HUGE;
1343     Geom::Point delta(dx, dy);
1344     Geom::Point best_pt = delta;
1345     Inkscape::SnappedPoint best_abs;
1346     
1347     if (snap) {    
1348         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1349          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1350          * must provide that information. */
1351           
1352         // Build a list of the unselected nodes to which the snapper should snap 
1353         std::vector<Geom::Point> unselected_nodes;
1354         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1355             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1356             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1357                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1358                 if (!node->selected) {
1359                     unselected_nodes.push_back(to_2geom(node->pos));
1360                 }    
1361             }
1362         }        
1363         
1364         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1365         
1366         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1367             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1368             m.setup(nodepath->desktop, false, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1369             Inkscape::SnappedPoint s;
1370             if (constrained) {
1371                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1372                 dedicated_constraint.setPoint(n->pos);
1373                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
1374             } else {
1375                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta));
1376             }            
1377             if (s.getSnapped() && (s.getDistance() < best)) {
1378                 best = s.getDistance();
1379                 best_abs = s;
1380                 best_pt = from_2geom(s.getPoint()) - n->pos;
1381             }
1382         }
1383                         
1384         if (best_abs.getSnapped()) {
1385             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1386         } else {
1387             nodepath->desktop->snapindicator->remove_snappoint();    
1388         }
1389     }
1391     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1392         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1393         sp_node_moveto(n, n->pos + best_pt);
1394     }
1396     // do not update repr here so that node dragging is acceptably fast
1397     update_object(nodepath);
1400 /**
1401 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1402 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1403 near x = 0.
1404  */
1405 double
1406 sculpt_profile (double x, double alpha, guint profile)
1408     double result = 1;
1410     if (x >= 1) {
1411         result = 0;
1412     } else if (x <= 0) {
1413         result = 1;
1414     } else {
1415         switch (profile) {
1416             case SCULPT_PROFILE_LINEAR:
1417                 result = 1 - x;
1418                 break;
1419             case SCULPT_PROFILE_BELL:
1420                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1421                 break;
1422             case SCULPT_PROFILE_ELLIPTIC:
1423                 result = sqrt(1 - x*x);
1424                 break;
1425             default:
1426                 g_assert_not_reached();
1427         }
1428     }
1430     return result;
1433 double
1434 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1436     // extremely primitive for now, don't have time to look for the real one
1437     double lower = Geom::L2(b - a);
1438     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1439     return (lower + upper)/2;
1442 void
1443 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1445     n->pos = n->origin + delta;
1446     n->n.pos = n->n.origin + delta_n;
1447     n->p.pos = n->p.origin + delta_p;
1448     sp_node_adjust_handles(n);
1449     sp_node_update_handles(n, false);
1452 /**
1453  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1454  * on how far they are from the dragged node n.
1455  */
1456 static void
1457 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1459     g_assert (n);
1460     g_assert (nodepath);
1461     g_assert (n->subpath->nodepath == nodepath);
1463     double pressure = n->knot->pressure;
1464     if (pressure == 0)
1465         pressure = 0.5; // default
1466     pressure = CLAMP (pressure, 0.2, 0.8);
1468     // map pressure to alpha = 1/5 ... 5
1469     double alpha = 1 - 2 * fabs(pressure - 0.5);
1470     if (pressure > 0.5)
1471         alpha = 1/alpha;
1473     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1474     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1476     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1477         // Only one subpath has selected nodes:
1478         // use linear mode, where the distance from n to node being dragged is calculated along the path
1480         double n_sel_range = 0, p_sel_range = 0;
1481         guint n_nodes = 0, p_nodes = 0;
1482         guint n_sel_nodes = 0, p_sel_nodes = 0;
1484         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1485         {
1486             double n_range = 0, p_range = 0;
1487             bool n_going = true, p_going = true;
1488             Inkscape::NodePath::Node *n_node = n;
1489             Inkscape::NodePath::Node *p_node = n;
1490             do {
1491                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1492                 if (n_node && n_going)
1493                     n_node = n_node->n.other;
1494                 if (n_node == NULL) {
1495                     n_going = false;
1496                 } else {
1497                     n_nodes ++;
1498                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1499                     if (n_node->selected) {
1500                         n_sel_nodes ++;
1501                         n_sel_range = n_range;
1502                     }
1503                     if (n_node == p_node) {
1504                         n_going = false;
1505                         p_going = false;
1506                     }
1507                 }
1508                 if (p_node && p_going)
1509                     p_node = p_node->p.other;
1510                 if (p_node == NULL) {
1511                     p_going = false;
1512                 } else {
1513                     p_nodes ++;
1514                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1515                     if (p_node->selected) {
1516                         p_sel_nodes ++;
1517                         p_sel_range = p_range;
1518                     }
1519                     if (p_node == n_node) {
1520                         n_going = false;
1521                         p_going = false;
1522                     }
1523                 }
1524             } while (n_going || p_going);
1525         }
1527         // Second pass: actually move nodes in this subpath
1528         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1529         {
1530             double n_range = 0, p_range = 0;
1531             bool n_going = true, p_going = true;
1532             Inkscape::NodePath::Node *n_node = n;
1533             Inkscape::NodePath::Node *p_node = n;
1534             do {
1535                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1536                 if (n_node && n_going)
1537                     n_node = n_node->n.other;
1538                 if (n_node == NULL) {
1539                     n_going = false;
1540                 } else {
1541                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1542                     if (n_node->selected) {
1543                         sp_nodepath_move_node_and_handles (n_node,
1544                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1545                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1546                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1547                     }
1548                     if (n_node == p_node) {
1549                         n_going = false;
1550                         p_going = false;
1551                     }
1552                 }
1553                 if (p_node && p_going)
1554                     p_node = p_node->p.other;
1555                 if (p_node == NULL) {
1556                     p_going = false;
1557                 } else {
1558                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1559                     if (p_node->selected) {
1560                         sp_nodepath_move_node_and_handles (p_node,
1561                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1562                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1563                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1564                     }
1565                     if (p_node == n_node) {
1566                         n_going = false;
1567                         p_going = false;
1568                     }
1569                 }
1570             } while (n_going || p_going);
1571         }
1573     } else {
1574         // Multiple subpaths have selected nodes:
1575         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1576         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1577         // fix the pear-like shape when sculpting e.g. a ring
1579         // First pass: calculate range
1580         gdouble direct_range = 0;
1581         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1582             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1583             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1584                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1585                 if (node->selected) {
1586                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1587                 }
1588             }
1589         }
1591         // Second pass: actually move nodes
1592         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1593             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1594             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1595                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1596                 if (node->selected) {
1597                     if (direct_range > 1e-6) {
1598                         sp_nodepath_move_node_and_handles (node,
1599                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1600                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1601                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1602                     } else {
1603                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1604                     }
1606                 }
1607             }
1608         }
1609     }
1611     // do not update repr here so that node dragging is acceptably fast
1612     update_object(nodepath);
1616 /**
1617  * Move node selection to point, adjust its and neighbouring handles,
1618  * handle possible snapping, and commit the change with possible undo.
1619  */
1620 void
1621 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1623     if (!nodepath) return;
1625     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1627     if (dx == 0) {
1628         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1629     } else if (dy == 0) {
1630         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1631     } else {
1632         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1633     }
1636 /**
1637  * Move node selection off screen and commit the change.
1638  */
1639 void
1640 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1642     // borrowed from sp_selection_move_screen in selection-chemistry.c
1643     // we find out the current zoom factor and divide deltas by it
1645     gdouble zoom = desktop->current_zoom();
1646     gdouble zdx = dx / zoom;
1647     gdouble zdy = dy / zoom;
1649     if (!nodepath) return;
1651     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1653     if (dx == 0) {
1654         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1655     } else if (dy == 0) {
1656         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1657     } else {
1658         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1659     }
1662 /**
1663  * Move selected nodes to the absolute position given
1664  */
1665 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1667     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1668         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1669         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1670         sp_node_moveto(n, npos);
1671     }
1673     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1676 /**
1677  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1678  */
1679 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1681     boost::optional<Geom::Coord> no_coord;
1682     g_return_val_if_fail(nodepath->selected, no_coord);
1684     // determine coordinate of first selected node
1685     GList *nsel = nodepath->selected;
1686     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1687     Geom::Coord coord = n->pos[axis];
1688     bool coincide = true;
1690     // compare it to the coordinates of all the other selected nodes
1691     for (GList *l = nsel->next; l != NULL; l = l->next) {
1692         n = (Inkscape::NodePath::Node *) l->data;
1693         if (n->pos[axis] != coord) {
1694             coincide = false;
1695         }
1696     }
1697     if (coincide) {
1698         return coord;
1699     } else {
1700         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1701         // currently we return the coordinate of the bounding box midpoint because I don't know how
1702         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1703         return bbox.midpoint()[axis];
1704     }
1707 /** If they don't yet exist, creates knot and line for the given side of the node */
1708 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1710     if (!side->knot) {
1711         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"));
1713         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1714         side->knot->setSize (7);
1715         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1716         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1717         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1718         sp_knot_update_ctrl(side->knot);
1720         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1721         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1722         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1723         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1724         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1725         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1726     }
1728     if (!side->line) {
1729         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1730                                         SP_TYPE_CTRLLINE, NULL);
1731     }
1734 /**
1735  * Ensure the given handle of the node is visible/invisible, update its screen position
1736  */
1737 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1739     g_assert(node != NULL);
1741    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1742     NRPathcode code = sp_node_path_code_from_side(node, side);
1744     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1746     if (show_handle) {
1747         if (!side->knot) { // No handle knot at all
1748             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1749             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1750             side->knot->pos = side->pos;
1751             if (side->knot->item)
1752                 SP_CTRL(side->knot->item)->moveto(side->pos);
1753             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1754             sp_knot_show(side->knot);
1755         } else {
1756             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1757                 if (fire_move_signals) {
1758                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1759                 } else {
1760                     sp_knot_moveto(side->knot, side->pos);
1761                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1762                 }
1763             }
1764             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1765                 sp_knot_show(side->knot);
1766             }
1767         }
1768         sp_canvas_item_show(side->line);
1769     } else {
1770         if (side->knot) {
1771             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1772                 sp_knot_hide(side->knot);
1773             }
1774         }
1775         if (side->line) {
1776             sp_canvas_item_hide(side->line);
1777         }
1778     }
1781 /**
1782  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1783  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1784  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1785  * updated; otherwise, just move the knots silently (used in batch moves).
1786  */
1787 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1789     g_assert(node != NULL);
1791     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1792         sp_knot_show(node->knot);
1793     }
1795     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1796         if (fire_move_signals)
1797             sp_knot_set_position(node->knot, node->pos, 0);
1798         else
1799             sp_knot_moveto(node->knot, node->pos);
1800     }
1802     gboolean show_handles = node->selected;
1803     if (node->p.other != NULL) {
1804         if (node->p.other->selected) show_handles = TRUE;
1805     }
1806     if (node->n.other != NULL) {
1807         if (node->n.other->selected) show_handles = TRUE;
1808     }
1810     if (node->subpath->nodepath->show_handles == false)
1811         show_handles = FALSE;
1813     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1814     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1817 /**
1818  * Call sp_node_update_handles() for all nodes on subpath.
1819  */
1820 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1822     g_assert(subpath != NULL);
1824     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1825         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1826     }
1829 /**
1830  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1831  */
1832 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1834     g_assert(nodepath != NULL);
1836     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1837         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1838     }
1841 void
1842 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1844     if (nodepath) {
1845         nodepath->show_handles = show;
1846         sp_nodepath_update_handles(nodepath);
1847     }
1850 /**
1851  * Adds all selected nodes in nodepath to list.
1852  */
1853 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1855     StlConv<Node *>::list(l, selected);
1856 /// \todo this adds a copying, rework when the selection becomes a stl list
1859 /**
1860  * Align selected nodes on the specified axis.
1861  */
1862 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1864     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1865         return;
1866     }
1868     if ( !nodepath->selected->next ) { // only one node selected
1869         return;
1870     }
1871    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1872     Geom::Point dest(pNode->pos);
1873     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1874         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1875         if (pNode) {
1876             dest[axis] = pNode->pos[axis];
1877             sp_node_moveto(pNode, dest);
1878         }
1879     }
1881     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1884 /// Helper struct.
1885 struct NodeSort
1887    Inkscape::NodePath::Node *_node;
1888     Geom::Coord _coord;
1889     /// \todo use vectorof pointers instead of calling copy ctor
1890     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1891         _node(node), _coord(node->pos[axis])
1892     {}
1894 };
1896 static bool operator<(NodeSort const &a, NodeSort const &b)
1898     return (a._coord < b._coord);
1901 /**
1902  * Distribute selected nodes on the specified axis.
1903  */
1904 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1906     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1907         return;
1908     }
1910     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1911         return;
1912     }
1914    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1915     std::vector<NodeSort> sorted;
1916     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1917         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1918         if (pNode) {
1919             NodeSort n(pNode, axis);
1920             sorted.push_back(n);
1921             //dest[axis] = pNode->pos[axis];
1922             //sp_node_moveto(pNode, dest);
1923         }
1924     }
1925     std::sort(sorted.begin(), sorted.end());
1926     unsigned int len = sorted.size();
1927     //overall bboxes span
1928     float dist = (sorted.back()._coord -
1929                   sorted.front()._coord);
1930     //new distance between each bbox
1931     float step = (dist) / (len - 1);
1932     float pos = sorted.front()._coord;
1933     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1934           it < sorted.end();
1935           it ++ )
1936     {
1937         Geom::Point dest((*it)._node->pos);
1938         dest[axis] = pos;
1939         sp_node_moveto((*it)._node, dest);
1940         pos += step;
1941     }
1943     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1947 /**
1948  * Call sp_nodepath_line_add_node() for all selected segments.
1949  */
1950 void
1951 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1953     if (!nodepath) {
1954         return;
1955     }
1957     GList *nl = NULL;
1959     int n_added = 0;
1961     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1962        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1963         g_assert(t->selected);
1964         if (t->p.other && t->p.other->selected) {
1965             nl = g_list_prepend(nl, t);
1966         }
1967     }
1969     while (nl) {
1970        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1971        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1972        sp_nodepath_node_select(n, TRUE, FALSE);
1973        n_added ++;
1974        nl = g_list_remove(nl, t);
1975     }
1977     /** \todo fixme: adjust ? */
1978     sp_nodepath_update_handles(nodepath);
1980     if (n_added > 1) {
1981         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1982     } else if (n_added > 0) {
1983         sp_nodepath_update_repr(nodepath, _("Add node"));
1984     }
1986     sp_nodepath_update_statusbar(nodepath);
1989 /**
1990  * Select segment nearest to point
1991  */
1992 void
1993 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
1995     if (!nodepath) {
1996         return;
1997     }
1999     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2000     Geom::PathVector const &pathv = curve->get_pathvector();
2001     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2002     if (!pvpos) {
2003         g_print ("Possible error?\n");
2004         return;
2005     }
2007     // calculate index for nodepath's representation.
2008     unsigned int segment_index = floor(pvpos->t) + 1;
2009     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2010         segment_index += pathv[i].size() + 1;
2011         if (pathv[i].closed()) {
2012             segment_index += 1;
2013         }
2014     }
2016     curve->unref();
2018     //find segment to segment
2019     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2021     //fixme: this can return NULL, so check before proceeding.
2022     g_return_if_fail(e != NULL);
2024     gboolean force = FALSE;
2025     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2026         force = TRUE;
2027     }
2028     sp_nodepath_node_select(e, (gboolean) toggle, force);
2029     if (e->p.other)
2030         sp_nodepath_node_select(e->p.other, TRUE, force);
2032     sp_nodepath_update_handles(nodepath);
2034     sp_nodepath_update_statusbar(nodepath);
2037 /**
2038  * Add a node nearest to point
2039  */
2040 void
2041 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2043     if (!nodepath) {
2044         return;
2045     }
2047     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2048     Geom::PathVector const &pathv = curve->get_pathvector();
2049     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2050     if (!pvpos) {
2051         g_print ("Possible error?\n");
2052         return;
2053     }
2055     // calculate index for nodepath's representation.
2056     double int_part;
2057     double t = std::modf(pvpos->t, &int_part);
2058     unsigned int segment_index = (unsigned int)int_part + 1;
2059     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2060         segment_index += pathv[i].size() + 1;
2061         if (pathv[i].closed()) {
2062             segment_index += 1;
2063         }
2064     }
2066     curve->unref();
2068     //find segment to split
2069     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2071     //don't know why but t seems to flip for lines
2072     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2073         t = 1.0 - t;
2074     }
2076     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2077     sp_nodepath_node_select(n, FALSE, TRUE);
2079     /* fixme: adjust ? */
2080     sp_nodepath_update_handles(nodepath);
2082     sp_nodepath_update_repr(nodepath, _("Add node"));
2084     sp_nodepath_update_statusbar(nodepath);
2087 /*
2088  * Adjusts a segment so that t moves by a certain delta for dragging
2089  * converts lines to curves
2090  *
2091  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2092  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2093  */
2094 void
2095 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2097     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2099     //fixme: e and e->p can be NULL, so check for those before proceeding
2100     g_return_if_fail(e != NULL);
2101     g_return_if_fail(&e->p != NULL);
2103     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2104         e->type = Inkscape::NodePath::NODE_SMOOTH;
2105         sp_nodepath_update_node_knot (e);
2106     }
2107     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2108         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2109         sp_nodepath_update_node_knot (e->p.other);
2110     }
2112     /* feel good is an arbitrary parameter that distributes the delta between handles
2113      * if t of the drag point is less than 1/6 distance form the endpoint only
2114      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2115      */
2116     double feel_good;
2117     if (t <= 1.0 / 6.0)
2118         feel_good = 0;
2119     else if (t <= 0.5)
2120         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2121     else if (t <= 5.0 / 6.0)
2122         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2123     else
2124         feel_good = 1;
2126     //if we're dragging a line convert it to a curve
2127     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2128         sp_nodepath_set_line_type(e, NR_CURVETO);
2129     }
2131     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2132     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2133     e->p.other->n.pos += offsetcoord0;
2134     e->p.pos += offsetcoord1;
2136     // adjust handles of adjacent nodes where necessary
2137     sp_node_adjust_handle(e,1);
2138     sp_node_adjust_handle(e->p.other,-1);
2140     sp_nodepath_update_handles(e->subpath->nodepath);
2142     update_object(e->subpath->nodepath);
2144     sp_nodepath_update_statusbar(e->subpath->nodepath);
2148 /**
2149  * Call sp_nodepath_break() for all selected segments.
2150  */
2151 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2153     if (!nodepath) return;
2155     GList *tempin = g_list_copy(nodepath->selected);
2156     GList *temp = NULL;
2157     for (GList *l = tempin; l != NULL; l = l->next) {
2158        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2159        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2160         if (nn == NULL) continue; // no break, no new node
2161         temp = g_list_prepend(temp, nn);
2162     }
2163     g_list_free(tempin);
2165     if (temp) {
2166         sp_nodepath_deselect(nodepath);
2167     }
2168     for (GList *l = temp; l != NULL; l = l->next) {
2169         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2170     }
2172     sp_nodepath_update_handles(nodepath);
2174     sp_nodepath_update_repr(nodepath, _("Break path"));
2177 /**
2178  * Duplicate the selected node(s).
2179  */
2180 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2182     if (!nodepath) {
2183         return;
2184     }
2186     GList *temp = NULL;
2187     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2188        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2189        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2190         if (nn == NULL) continue; // could not duplicate
2191         temp = g_list_prepend(temp, nn);
2192     }
2194     if (temp) {
2195         sp_nodepath_deselect(nodepath);
2196     }
2197     for (GList *l = temp; l != NULL; l = l->next) {
2198         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2199     }
2201     sp_nodepath_update_handles(nodepath);
2203     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2206 /**
2207  *  Internal function to join two nodes by merging them into one.
2208  */
2209 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2211     /* a and b are endpoints */
2213     // if one of the two nodes is mouseovered, fix its position
2214     Geom::Point c;
2215     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2216         c = a->pos;
2217     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2218         c = b->pos;
2219     } else {
2220         // otherwise, move joined node to the midpoint
2221         c = (a->pos + b->pos) / 2;
2222     }
2224     if (a->subpath == b->subpath) {
2225        Inkscape::NodePath::SubPath *sp = a->subpath;
2226         sp_nodepath_subpath_close(sp);
2227         sp_node_moveto (sp->first, c);
2229         sp_nodepath_update_handles(sp->nodepath);
2230         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2231         return;
2232     }
2234     /* a and b are separate subpaths */
2235     Inkscape::NodePath::SubPath *sa = a->subpath;
2236     Inkscape::NodePath::SubPath *sb = b->subpath;
2237     Geom::Point p;
2238     Inkscape::NodePath::Node *n;
2239     NRPathcode code;
2240     if (a == sa->first) {
2241         // we will now reverse sa, so that a is its last node, not first, and drop that node
2242         p = sa->first->n.pos;
2243         code = (NRPathcode)sa->first->n.other->code;
2244         // create new subpath
2245        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2246        // create a first moveto node on it
2247         n = sa->last;
2248         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2249         n = n->p.other;
2250         if (n == sa->first) n = NULL;
2251         while (n) {
2252             // copy the rest of the nodes from sa to t, going backwards
2253             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2254             n = n->p.other;
2255             if (n == sa->first) n = NULL;
2256         }
2257         // replace sa with t
2258         sp_nodepath_subpath_destroy(sa);
2259         sa = t;
2260     } else if (a == sa->last) {
2261         // a is already last, just drop it
2262         p = sa->last->p.pos;
2263         code = (NRPathcode)sa->last->code;
2264         sp_nodepath_node_destroy(sa->last);
2265     } else {
2266         code = NR_END;
2267         g_assert_not_reached();
2268     }
2270     if (b == sb->first) {
2271         // copy all nodes from b to a, forward 
2272         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2273         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2274             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2275         }
2276     } else if (b == sb->last) {
2277         // copy all nodes from b to a, backward 
2278         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2279         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2280             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2281         }
2282     } else {
2283         g_assert_not_reached();
2284     }
2285     /* and now destroy sb */
2287     sp_nodepath_subpath_destroy(sb);
2289     sp_nodepath_update_handles(sa->nodepath);
2291     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2293     sp_nodepath_update_statusbar(nodepath);
2296 /**
2297  *  Internal function to join two nodes by adding a segment between them.
2298  */
2299 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2301     if (a->subpath == b->subpath) {
2302        Inkscape::NodePath::SubPath *sp = a->subpath;
2304         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2305         sp->closed = TRUE;
2307         sp->first->p.other = sp->last;
2308         sp->last->n.other  = sp->first;
2310         sp_node_handle_mirror_p_to_n(sp->last);
2311         sp_node_handle_mirror_n_to_p(sp->first);
2313         sp->first->code = sp->last->code;
2314         sp->first       = sp->last;
2316         sp_nodepath_update_handles(sp->nodepath);
2318         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2320         return;
2321     }
2323     /* a and b are separate subpaths */
2324     Inkscape::NodePath::SubPath *sa = a->subpath;
2325     Inkscape::NodePath::SubPath *sb = b->subpath;
2327     Inkscape::NodePath::Node *n;
2328     Geom::Point p;
2329     NRPathcode code;
2330     if (a == sa->first) {
2331         code = (NRPathcode) sa->first->n.other->code;
2332        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2333         n = sa->last;
2334         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2335         for (n = n->p.other; n != NULL; n = n->p.other) {
2336             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2337         }
2338         sp_nodepath_subpath_destroy(sa);
2339         sa = t;
2340     } else if (a == sa->last) {
2341         code = (NRPathcode)sa->last->code;
2342     } else {
2343         code = NR_END;
2344         g_assert_not_reached();
2345     }
2347     if (b == sb->first) {
2348         n = sb->first;
2349         sp_node_handle_mirror_p_to_n(sa->last);
2350         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2351         sp_node_handle_mirror_n_to_p(sa->last);
2352         for (n = n->n.other; n != NULL; n = n->n.other) {
2353             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2354         }
2355     } else if (b == sb->last) {
2356         n = sb->last;
2357         sp_node_handle_mirror_p_to_n(sa->last);
2358         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2359         sp_node_handle_mirror_n_to_p(sa->last);
2360         for (n = n->p.other; n != NULL; n = n->p.other) {
2361             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2362         }
2363     } else {
2364         g_assert_not_reached();
2365     }
2366     /* and now destroy sb */
2368     sp_nodepath_subpath_destroy(sb);
2370     sp_nodepath_update_handles(sa->nodepath);
2372     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2375 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2377 /**
2378  * Internal function to handle joining two nodes.
2379  */
2380 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2382     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2384     if (g_list_length(nodepath->selected) != 2) {
2385         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2386         return;
2387     }
2389     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2390     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2392     g_assert(a != b);
2393     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2394         // someone tried to join an orphan node (i.e. a single-node subpath).
2395         // this is not worth an error message, just fail silently.
2396         return;
2397     }
2399     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2400         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2401         return;
2402     }
2404     switch(mode) {
2405         case NODE_JOIN_ENDPOINTS:
2406             do_node_selected_join(nodepath, a, b);
2407             break;
2408         case NODE_JOIN_SEGMENT:
2409             do_node_selected_join_segment(nodepath, a, b);
2410             break;
2411     }
2414 /**
2415  *  Join two nodes by merging them into one.
2416  */
2417 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2419     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2422 /**
2423  *  Join two nodes by adding a segment between them.
2424  */
2425 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2427     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2430 /**
2431  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2432  */
2433 void sp_node_delete_preserve(GList *nodes_to_delete)
2435     GSList *nodepaths = NULL;
2437     while (nodes_to_delete) {
2438         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2439         Inkscape::NodePath::SubPath *sp = node->subpath;
2440         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2441         Inkscape::NodePath::Node *sample_cursor = NULL;
2442         Inkscape::NodePath::Node *sample_end = NULL;
2443         Inkscape::NodePath::Node *delete_cursor = node;
2444         bool just_delete = false;
2446         //find the start of this contiguous selection
2447         //move left to the first node that is not selected
2448         //or the start of the non-closed path
2449         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2450             delete_cursor = curr;
2451         }
2453         //just delete at the beginning of an open path
2454         if (!delete_cursor->p.other) {
2455             sample_cursor = delete_cursor;
2456             just_delete = true;
2457         } else {
2458             sample_cursor = delete_cursor->p.other;
2459         }
2461         //calculate points for each segment
2462         int rate = 5;
2463         float period = 1.0 / rate;
2464         std::vector<Geom::Point> data;
2465         if (!just_delete) {
2466             data.push_back(sample_cursor->pos);
2467             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2468                 //just delete at the end of an open path
2469                 if (!sp->closed && curr == sp->last) {
2470                     just_delete = true;
2471                     break;
2472                 }
2474                 //sample points on the contiguous selected segment
2475                 Geom::Point *bez;
2476                 bez = new Geom::Point [4];
2477                 bez[0] = curr->pos;
2478                 bez[1] = curr->n.pos;
2479                 bez[2] = curr->n.other->p.pos;
2480                 bez[3] = curr->n.other->pos;
2481                 for (int i=1; i<rate; i++) {
2482                     gdouble t = i * period;
2483                     Geom::Point p = bezier_pt(3, bez, t);
2484                     data.push_back(p);
2485                 }
2486                 data.push_back(curr->n.other->pos);
2488                 sample_end = curr->n.other;
2489                 //break if we've come full circle or hit the end of the selection
2490                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2491                     break;
2492                 }
2493             }
2494         }
2496         if (!just_delete) {
2497             //calculate the best fitting single segment and adjust the endpoints
2498             Geom::Point *adata;
2499             adata = new Geom::Point [data.size()];
2500             copy(data.begin(), data.end(), adata);
2502             Geom::Point *bez;
2503             bez = new Geom::Point [4];
2504             //would decreasing error create a better fitting approximation?
2505             gdouble error = 1.0;
2506             gint ret;
2507             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2509             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2510             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2511             //the resulting nodes behave as expected.
2512             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2513                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2514             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2515                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2517             //adjust endpoints
2518             sample_cursor->n.pos = bez[1];
2519             sample_end->p.pos = bez[2];
2520         }
2522         //destroy this contiguous selection
2523         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2524             Inkscape::NodePath::Node *temp = delete_cursor;
2525             if (delete_cursor->n.other == delete_cursor) {
2526                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2527                 delete_cursor = NULL;
2528             } else {
2529                 delete_cursor = delete_cursor->n.other;
2530             }
2531             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2532             sp_nodepath_node_destroy(temp);
2533         }
2535         sp_nodepath_update_handles(nodepath);
2537         if (!g_slist_find(nodepaths, nodepath))
2538             nodepaths = g_slist_prepend (nodepaths, nodepath);
2539     }
2541     for (GSList *i = nodepaths; i; i = i->next) {
2542         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2543         // different nodepaths will give us one undo event per nodepath
2544         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2546         // if the entire nodepath is removed, delete the selected object.
2547         if (nodepath->subpaths == NULL ||
2548             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2549             //at least 2
2550             sp_nodepath_get_node_count(nodepath) < 2) {
2551             SPDocument *document = sp_desktop_document (nodepath->desktop);
2552             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2553             //delete this nodepath's object, not the entire selection! (though at this time, this
2554             //does not matter)
2555             sp_selection_delete(nodepath->desktop);
2556             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2557                               _("Delete nodes"));
2558         } else {
2559             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2560             sp_nodepath_update_statusbar(nodepath);
2561         }
2562     }
2564     g_slist_free (nodepaths);
2567 /**
2568  * Delete one or more selected nodes.
2569  */
2570 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2572     if (!nodepath) return;
2573     if (!nodepath->selected) return;
2575     /** \todo fixme: do it the right way */
2576     while (nodepath->selected) {
2577        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2578         sp_nodepath_node_destroy(node);
2579     }
2582     //clean up the nodepath (such as for trivial subpaths)
2583     sp_nodepath_cleanup(nodepath);
2585     sp_nodepath_update_handles(nodepath);
2587     // if the entire nodepath is removed, delete the selected object.
2588     if (nodepath->subpaths == NULL ||
2589         sp_nodepath_get_node_count(nodepath) < 2) {
2590         SPDocument *document = sp_desktop_document (nodepath->desktop);
2591         sp_selection_delete(nodepath->desktop);
2592         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2593                           _("Delete nodes"));
2594         return;
2595     }
2597     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2599     sp_nodepath_update_statusbar(nodepath);
2602 /**
2603  * Delete one or more segments between two selected nodes.
2604  * This is the code for 'split'.
2605  */
2606 void
2607 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2609    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2610    Inkscape::NodePath::Node *curr, *next;     //Iterators
2612     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2614     if (g_list_length(nodepath->selected) != 2) {
2615         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2616                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2617         return;
2618     }
2620     //Selected nodes, not inclusive
2621    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2622    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2624     if ( ( a==b)                       ||  //same node
2625          (a->subpath  != b->subpath )  ||  //not the same path
2626          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2627          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2628     {
2629         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2630                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2631         return;
2632     }
2634     //###########################################
2635     //# BEGIN EDITS
2636     //###########################################
2637     //##################################
2638     //# CLOSED PATH
2639     //##################################
2640     if (a->subpath->closed) {
2643         gboolean reversed = FALSE;
2645         //Since we can go in a circle, we need to find the shorter distance.
2646         //  a->b or b->a
2647         start = end = NULL;
2648         int distance    = 0;
2649         int minDistance = 0;
2650         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2651             if (curr==b) {
2652                 //printf("a to b:%d\n", distance);
2653                 start = a;//go from a to b
2654                 end   = b;
2655                 minDistance = distance;
2656                 //printf("A to B :\n");
2657                 break;
2658             }
2659             distance++;
2660         }
2662         //try again, the other direction
2663         distance = 0;
2664         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2665             if (curr==a) {
2666                 //printf("b to a:%d\n", distance);
2667                 if (distance < minDistance) {
2668                     start    = b;  //we go from b to a
2669                     end      = a;
2670                     reversed = TRUE;
2671                     //printf("B to A\n");
2672                 }
2673                 break;
2674             }
2675             distance++;
2676         }
2679         //Copy everything from 'end' to 'start' to a new subpath
2680        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2681         for (curr=end ; curr ; curr=curr->n.other) {
2682             NRPathcode code = (NRPathcode) curr->code;
2683             if (curr == end)
2684                 code = NR_MOVETO;
2685             sp_nodepath_node_new(t, NULL,
2686                                  (Inkscape::NodePath::NodeType)curr->type, code,
2687                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2688             if (curr == start)
2689                 break;
2690         }
2691         sp_nodepath_subpath_destroy(a->subpath);
2694     }
2698     //##################################
2699     //# OPEN PATH
2700     //##################################
2701     else {
2703         //We need to get the direction of the list between A and B
2704         //Can we walk from a to b?
2705         start = end = NULL;
2706         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2707             if (curr==b) {
2708                 start = a;  //did it!  we go from a to b
2709                 end   = b;
2710                 //printf("A to B\n");
2711                 break;
2712             }
2713         }
2714         if (!start) {//didn't work?  let's try the other direction
2715             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2716                 if (curr==a) {
2717                     start = b;  //did it!  we go from b to a
2718                     end   = a;
2719                     //printf("B to A\n");
2720                     break;
2721                 }
2722             }
2723         }
2724         if (!start) {
2725             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2726                                                      _("Cannot find path between nodes."));
2727             return;
2728         }
2732         //Copy everything after 'end' to a new subpath
2733        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2734         for (curr=end ; curr ; curr=curr->n.other) {
2735             NRPathcode code = (NRPathcode) curr->code;
2736             if (curr == end)
2737                 code = NR_MOVETO;
2738             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2739                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2740         }
2742         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2743         for (curr = start->n.other ; curr  ; curr=next) {
2744             next = curr->n.other;
2745             sp_nodepath_node_destroy(curr);
2746         }
2748     }
2749     //###########################################
2750     //# END EDITS
2751     //###########################################
2753     //clean up the nodepath (such as for trivial subpaths)
2754     sp_nodepath_cleanup(nodepath);
2756     sp_nodepath_update_handles(nodepath);
2758     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2760     sp_nodepath_update_statusbar(nodepath);
2763 /**
2764  * Call sp_nodepath_set_line() for all selected segments.
2765  */
2766 void
2767 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2769     if (nodepath == NULL) return;
2771     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2772        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2773         g_assert(n->selected);
2774         if (n->p.other && n->p.other->selected) {
2775             sp_nodepath_set_line_type(n, code);
2776         }
2777     }
2779     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2782 /**
2783  * Call sp_nodepath_convert_node_type() for all selected nodes.
2784  */
2785 void
2786 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2788     if (nodepath == NULL) return;
2790     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2792     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2793         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2794     }
2796     sp_nodepath_update_repr(nodepath, _("Change node type"));
2799 /**
2800  * Change select status of node, update its own and neighbour handles.
2801  */
2802 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2804     node->selected = selected;
2806     if (selected) {
2807         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2808         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2809         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2810         sp_knot_update_ctrl(node->knot);
2811     } else {
2812         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2813         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2814         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2815         sp_knot_update_ctrl(node->knot);
2816     }
2818     sp_node_update_handles(node);
2819     if (node->n.other) sp_node_update_handles(node->n.other);
2820     if (node->p.other) sp_node_update_handles(node->p.other);
2823 /**
2824 \brief Select a node
2825 \param node     The node to select
2826 \param incremental   If true, add to selection, otherwise deselect others
2827 \param override   If true, always select this node, otherwise toggle selected status
2828 */
2829 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2831     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2833     if (incremental) {
2834         if (override) {
2835             if (!g_list_find(nodepath->selected, node)) {
2836                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2837             }
2838             sp_node_set_selected(node, TRUE);
2839         } else { // toggle
2840             if (node->selected) {
2841                 g_assert(g_list_find(nodepath->selected, node));
2842                 nodepath->selected = g_list_remove(nodepath->selected, node);
2843             } else {
2844                 g_assert(!g_list_find(nodepath->selected, node));
2845                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2846             }
2847             sp_node_set_selected(node, !node->selected);
2848         }
2849     } else {
2850         sp_nodepath_deselect(nodepath);
2851         nodepath->selected = g_list_prepend(nodepath->selected, node);
2852         sp_node_set_selected(node, TRUE);
2853     }
2855     sp_nodepath_update_statusbar(nodepath);
2859 /**
2860 \brief Deselect all nodes in the nodepath
2861 */
2862 void
2863 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2865     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2867     while (nodepath->selected) {
2868         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2869         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2870     }
2871     sp_nodepath_update_statusbar(nodepath);
2874 /**
2875 \brief Select or invert selection of all nodes in the nodepath
2876 */
2877 void
2878 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2880     if (!nodepath) return;
2882     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2883        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2884         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2885            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2886            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2887         }
2888     }
2891 /**
2892  * If nothing selected, does the same as sp_nodepath_select_all();
2893  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2894  * (i.e., similar to "select all in layer", with the "selected" subpaths
2895  * being treated as "layers" in the path).
2896  */
2897 void
2898 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2900     if (!nodepath) return;
2902     if (g_list_length (nodepath->selected) == 0) {
2903         sp_nodepath_select_all (nodepath, invert);
2904         return;
2905     }
2907     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2908     GSList *subpaths = NULL;
2910     for (GList *l = copy; l != NULL; l = l->next) {
2911         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2912         Inkscape::NodePath::SubPath *subpath = n->subpath;
2913         if (!g_slist_find (subpaths, subpath))
2914             subpaths = g_slist_prepend (subpaths, subpath);
2915     }
2917     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2918         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2919         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2920             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2921             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2922         }
2923     }
2925     g_slist_free (subpaths);
2926     g_list_free (copy);
2929 /**
2930  * \brief Select the node after the last selected; if none is selected,
2931  * select the first within path.
2932  */
2933 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2935     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2937    Inkscape::NodePath::Node *last = NULL;
2938     if (nodepath->selected) {
2939         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2940            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2941             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2942             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2943                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2944                 if (node->selected) {
2945                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2946                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2947                             if (spl->next) { // there's a next subpath
2948                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2949                                 last = subpath_next->first;
2950                             } else if (spl->prev) { // there's a previous subpath
2951                                 last = NULL; // to be set later to the first node of first subpath
2952                             } else {
2953                                 last = node->n.other;
2954                             }
2955                         } else {
2956                             last = node->n.other;
2957                         }
2958                     } else {
2959                         if (node->n.other) {
2960                             last = node->n.other;
2961                         } else {
2962                             if (spl->next) { // there's a next subpath
2963                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2964                                 last = subpath_next->first;
2965                             } else if (spl->prev) { // there's a previous subpath
2966                                 last = NULL; // to be set later to the first node of first subpath
2967                             } else {
2968                                 last = (Inkscape::NodePath::Node *) subpath->first;
2969                             }
2970                         }
2971                     }
2972                 }
2973             }
2974         }
2975         sp_nodepath_deselect(nodepath);
2976     }
2978     if (last) { // there's at least one more node after selected
2979         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2980     } else { // no more nodes, select the first one in first subpath
2981        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2982         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2983     }
2986 /**
2987  * \brief Select the node before the first selected; if none is selected,
2988  * select the last within path
2989  */
2990 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2992     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2994    Inkscape::NodePath::Node *last = NULL;
2995     if (nodepath->selected) {
2996         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2997            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2998             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2999                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3000                 if (node->selected) {
3001                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3002                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3003                             if (spl->prev) { // there's a prev subpath
3004                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3005                                 last = subpath_prev->last;
3006                             } else if (spl->next) { // there's a next subpath
3007                                 last = NULL; // to be set later to the last node of last subpath
3008                             } else {
3009                                 last = node->p.other;
3010                             }
3011                         } else {
3012                             last = node->p.other;
3013                         }
3014                     } else {
3015                         if (node->p.other) {
3016                             last = node->p.other;
3017                         } else {
3018                             if (spl->prev) { // there's a prev subpath
3019                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3020                                 last = subpath_prev->last;
3021                             } else if (spl->next) { // there's a next subpath
3022                                 last = NULL; // to be set later to the last node of last subpath
3023                             } else {
3024                                 last = (Inkscape::NodePath::Node *) subpath->last;
3025                             }
3026                         }
3027                     }
3028                 }
3029             }
3030         }
3031         sp_nodepath_deselect(nodepath);
3032     }
3034     if (last) { // there's at least one more node before selected
3035         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3036     } else { // no more nodes, select the last one in last subpath
3037         GList *spl = g_list_last(nodepath->subpaths);
3038        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3039         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3040     }
3043 /**
3044  * \brief Select all nodes that are within the rectangle.
3045  */
3046 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3048     if (!incremental) {
3049         sp_nodepath_deselect(nodepath);
3050     }
3052     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3053        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3054         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3055            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3057             if (b.contains(node->pos)) {
3058                 sp_nodepath_node_select(node, TRUE, TRUE);
3059             }
3060         }
3061     }
3065 void
3066 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3068     g_assert (n);
3069     g_assert (nodepath);
3070     g_assert (n->subpath->nodepath == nodepath);
3072     if (g_list_length (nodepath->selected) == 0) {
3073         if (grow > 0) {
3074             sp_nodepath_node_select(n, TRUE, TRUE);
3075         }
3076         return;
3077     }
3079     if (g_list_length (nodepath->selected) == 1) {
3080         if (grow < 0) {
3081             sp_nodepath_deselect (nodepath);
3082             return;
3083         }
3084     }
3086         double n_sel_range = 0, p_sel_range = 0;
3087             Inkscape::NodePath::Node *farthest_n_node = n;
3088             Inkscape::NodePath::Node *farthest_p_node = n;
3090         // Calculate ranges
3091         {
3092             double n_range = 0, p_range = 0;
3093             bool n_going = true, p_going = true;
3094             Inkscape::NodePath::Node *n_node = n;
3095             Inkscape::NodePath::Node *p_node = n;
3096             do {
3097                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3098                 if (n_node && n_going)
3099                     n_node = n_node->n.other;
3100                 if (n_node == NULL) {
3101                     n_going = false;
3102                 } else {
3103                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3104                     if (n_node->selected) {
3105                         n_sel_range = n_range;
3106                         farthest_n_node = n_node;
3107                     }
3108                     if (n_node == p_node) {
3109                         n_going = false;
3110                         p_going = false;
3111                     }
3112                 }
3113                 if (p_node && p_going)
3114                     p_node = p_node->p.other;
3115                 if (p_node == NULL) {
3116                     p_going = false;
3117                 } else {
3118                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3119                     if (p_node->selected) {
3120                         p_sel_range = p_range;
3121                         farthest_p_node = p_node;
3122                     }
3123                     if (p_node == n_node) {
3124                         n_going = false;
3125                         p_going = false;
3126                     }
3127                 }
3128             } while (n_going || p_going);
3129         }
3131     if (grow > 0) {
3132         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3133                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3134         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3135                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3136         }
3137     } else {
3138         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3139                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3140         } else if (farthest_p_node && farthest_p_node->selected) {
3141                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3142         }
3143     }
3146 void
3147 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3149     g_assert (n);
3150     g_assert (nodepath);
3151     g_assert (n->subpath->nodepath == nodepath);
3153     if (g_list_length (nodepath->selected) == 0) {
3154         if (grow > 0) {
3155             sp_nodepath_node_select(n, TRUE, TRUE);
3156         }
3157         return;
3158     }
3160     if (g_list_length (nodepath->selected) == 1) {
3161         if (grow < 0) {
3162             sp_nodepath_deselect (nodepath);
3163             return;
3164         }
3165     }
3167     Inkscape::NodePath::Node *farthest_selected = NULL;
3168     double farthest_dist = 0;
3170     Inkscape::NodePath::Node *closest_unselected = NULL;
3171     double closest_dist = NR_HUGE;
3173     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3174        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3175         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3176            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3177            if (node == n)
3178                continue;
3179            if (node->selected) {
3180                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3181                    farthest_dist = Geom::L2(node->pos - n->pos);
3182                    farthest_selected = node;
3183                }
3184            } else {
3185                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3186                    closest_dist = Geom::L2(node->pos - n->pos);
3187                    closest_unselected = node;
3188                }
3189            }
3190         }
3191     }
3193     if (grow > 0) {
3194         if (closest_unselected) {
3195             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3196         }
3197     } else {
3198         if (farthest_selected) {
3199             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3200         }
3201     }
3205 /**
3206 \brief  Saves all nodes' and handles' current positions in their origin members
3207 */
3208 void
3209 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3211     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3212        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3213         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3214            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3215            n->origin = n->pos;
3216            n->p.origin = n->p.pos;
3217            n->n.origin = n->n.pos;
3218         }
3219     }
3222 /**
3223 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3224 */
3225 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3227     GList *r = NULL;
3228     if (nodepath->selected) {
3229         guint i = 0;
3230         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3231             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3232             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3233                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3234                 i++;
3235                 if (node->selected) {
3236                     r = g_list_append(r, GINT_TO_POINTER(i));
3237                 }
3238             }
3239         }
3240     }
3241     return r;
3244 /**
3245 \brief  Restores selection by selecting nodes whose positions are in the list
3246 */
3247 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3249     sp_nodepath_deselect(nodepath);
3251     guint i = 0;
3252     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3253        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3254         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3255            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3256             i++;
3257             if (g_list_find(r, GINT_TO_POINTER(i))) {
3258                 sp_nodepath_node_select(node, TRUE, TRUE);
3259             }
3260         }
3261     }
3265 /**
3266 \brief Adjusts handle according to node type and line code.
3267 */
3268 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3270     g_assert(node);
3272     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3273     if (node->type == Inkscape::NodePath::NODE_AUTO)
3274         return;
3276    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3277    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3279    // nothing to do if we are an end node
3280     if (me->other == NULL) return;
3281     if (other->other == NULL) return;
3283     // nothing to do if we are a cusp node
3284     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3286     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3287     NRPathcode mecode;
3288     if (which_adjust == 1) {
3289         mecode = (NRPathcode)me->other->code;
3290     } else {
3291         mecode = (NRPathcode)node->code;
3292     }
3293     if (mecode == NR_LINETO) return;
3295     if (sp_node_side_is_line(node, other)) {
3296         // other is a line, and we are either smooth or symm
3297        Inkscape::NodePath::Node *othernode = other->other;
3298         double len = Geom::L2(me->pos - node->pos);
3299         Geom::Point delta = node->pos - othernode->pos;
3300         double linelen = Geom::L2(delta);
3301         if (linelen < 1e-18)
3302             return;
3303         me->pos = node->pos + (len / linelen)*delta;
3304         return;
3305     }
3307     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3308         // symmetrize 
3309         me->pos = 2 * node->pos - other->pos;
3310         return;
3311     } else {
3312         // smoothify
3313         double len = Geom::L2(me->pos - node->pos);
3314         Geom::Point delta = other->pos - node->pos;
3315         double otherlen = Geom::L2(delta);
3316         if (otherlen < 1e-18) return;
3317         me->pos = node->pos - (len / otherlen) * delta;
3318     }
3321 /**
3322  \brief Adjusts both handles according to node type and line code
3323  */
3324 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3326     g_assert(node);
3328     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3330     /* we are either smooth or symm */
3332     if (node->p.other == NULL) return;
3333     if (node->n.other == NULL) return;
3335     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3336         sp_node_adjust_handles_auto(node);
3337         return;
3338     }
3340     if (sp_node_side_is_line(node, &node->p)) {
3341         sp_node_adjust_handle(node, 1);
3342         return;
3343     }
3345     if (sp_node_side_is_line(node, &node->n)) {
3346         sp_node_adjust_handle(node, -1);
3347         return;
3348     }
3350     /* both are curves */
3351     Geom::Point const delta( node->n.pos - node->p.pos );
3353     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3354         node->p.pos = node->pos - delta / 2;
3355         node->n.pos = node->pos + delta / 2;
3356         return;
3357     }
3359     /* We are smooth */
3360     double plen = Geom::L2(node->p.pos - node->pos);
3361     if (plen < 1e-18) return;
3362     double nlen = Geom::L2(node->n.pos - node->pos);
3363     if (nlen < 1e-18) return;
3364     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3365     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3368 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3370     if (node->p.other == NULL || node->n.other == NULL) {
3371         node->p.pos = node->pos;
3372         node->n.pos = node->pos;
3373         return;
3374     }
3376     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3377     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3378  
3379     double norm_leg_prev = Geom::L2(leg_prev);
3380     double norm_leg_next = Geom::L2(leg_next);
3381  
3382     Geom::Point delta;
3383     if (norm_leg_next > 0.0) {
3384         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3385         delta.normalize();
3386     }
3387  
3388     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3389     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3392 /**
3393  * Node event callback.
3394  */
3395 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3397     gboolean ret = FALSE;
3398     switch (event->type) {
3399         case GDK_ENTER_NOTIFY:
3400             Inkscape::NodePath::Path::active_node = n;
3401             break;
3402         case GDK_LEAVE_NOTIFY:
3403             Inkscape::NodePath::Path::active_node = NULL;
3404             break;
3405         case GDK_SCROLL:
3406             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3407                 switch (event->scroll.direction) {
3408                     case GDK_SCROLL_UP:
3409                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3410                         break;
3411                     case GDK_SCROLL_DOWN:
3412                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3413                         break;
3414                     default:
3415                         break;
3416                 }
3417                 ret = TRUE;
3418             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3419                 switch (event->scroll.direction) {
3420                     case GDK_SCROLL_UP:
3421                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3422                         break;
3423                     case GDK_SCROLL_DOWN:
3424                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3425                         break;
3426                     default:
3427                         break;
3428                 }
3429                 ret = TRUE;
3430             }
3431             break;
3432         case GDK_KEY_PRESS:
3433             switch (get_group0_keyval (&event->key)) {
3434                 case GDK_space:
3435                     if (event->key.state & GDK_BUTTON1_MASK) {
3436                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3437                         stamp_repr(nodepath);
3438                         ret = TRUE;
3439                     }
3440                     break;
3441                 case GDK_Page_Up:
3442                     if (event->key.state & GDK_CONTROL_MASK) {
3443                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3444                     } else {
3445                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3446                     }
3447                     break;
3448                 case GDK_Page_Down:
3449                     if (event->key.state & GDK_CONTROL_MASK) {
3450                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3451                     } else {
3452                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3453                     }
3454                     break;
3455                 default:
3456                     break;
3457             }
3458             break;
3459         default:
3460             break;
3461     }
3463     return ret;
3466 /**
3467  * Handle keypress on node; directly called.
3468  */
3469 gboolean node_key(GdkEvent *event)
3471     Inkscape::NodePath::Path *np;
3473     // there is no way to verify nodes so set active_node to nil when deleting!!
3474     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3476     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3477         gint ret = FALSE;
3478         switch (get_group0_keyval (&event->key)) {
3479             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3480             case GDK_BackSpace:
3481                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3482                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3483                 sp_nodepath_update_repr(np, _("Delete node"));
3484                 Inkscape::NodePath::Path::active_node = NULL;
3485                 ret = TRUE;
3486                 break;
3487             case GDK_c:
3488                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3489                 ret = TRUE;
3490                 break;
3491             case GDK_s:
3492                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3493                 ret = TRUE;
3494                 break;
3495             case GDK_a:
3496                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3497                 ret = TRUE;
3498                 break;
3499             case GDK_y:
3500                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3501                 ret = TRUE;
3502                 break;
3503             case GDK_b:
3504                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3505                 ret = TRUE;
3506                 break;
3507         }
3508         return ret;
3509     }
3510     return FALSE;
3513 /**
3514  * Mouseclick on node callback.
3515  */
3516 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3518    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3520     if (state & GDK_CONTROL_MASK) {
3521         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3523         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3524             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3525                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3526             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3527                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3528             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3529                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3530             } else {
3531                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3532             }
3533             sp_nodepath_update_repr(nodepath, _("Change node type"));
3534             sp_nodepath_update_statusbar(nodepath);
3536         } else { //ctrl+alt+click: delete node
3537             GList *node_to_delete = NULL;
3538             node_to_delete = g_list_append(node_to_delete, n);
3539             sp_node_delete_preserve(node_to_delete);
3540         }
3542     } else {
3543         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3544     }
3547 /**
3548  * Mouse grabbed node callback.
3549  */
3550 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3552    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3554     if (!n->selected) {
3555         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3556     }
3558     n->is_dragging = true;
3559     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3561     sp_nodepath_remember_origins (n->subpath->nodepath);
3564 /**
3565  * Mouse ungrabbed node callback.
3566  */
3567 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3569    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3571    n->dragging_out = NULL;
3572    n->is_dragging = false;
3573    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3575    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3578 /**
3579  * The point on a line, given by its angle, closest to the given point.
3580  * \param p  A point.
3581  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3582  * \param closest  Pointer to the point struct where the result is stored.
3583  * \todo FIXME: use dot product perhaps?
3584  */
3585 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3587     if (a == HUGE_VAL) { // vertical
3588         *closest = Geom::Point(0, (*p)[Geom::Y]);
3589     } else {
3590         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3591         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3592     }
3595 /**
3596  * Distance from the point to a line given by its angle.
3597  * \param p  A point.
3598  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3599  */
3600 static double point_line_distance(Geom::Point *p, double a)
3602     Geom::Point c;
3603     point_line_closest(p, a, &c);
3604     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]));
3607 /**
3608  * Callback for node "request" signal.
3609  * \todo fixme: This goes to "moved" event? (lauris)
3610  */
3611 static gboolean
3612 node_request(SPKnot */*knot*/, Geom::Point *p, guint state, gpointer data)
3614     double yn, xn, yp, xp;
3615     double an, ap, na, pa;
3616     double d_an, d_ap, d_na, d_pa;
3617     gboolean collinear = FALSE;
3618     Geom::Point c;
3619     Geom::Point pr;
3621     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3623     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3625     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3626     if ( (!n->subpath->nodepath->straight_path) &&
3627          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3628            || n->dragging_out ) )
3629     {
3630        Geom::Point mouse = (*p);
3632        if (!n->dragging_out) {
3633            // This is the first drag-out event; find out which handle to drag out
3634            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3635            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3637            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3638                return FALSE;
3640            Inkscape::NodePath::NodeSide *opposite;
3641            if (appr_p > appr_n) { // closer to p
3642                n->dragging_out = &n->p;
3643                opposite = &n->n;
3644                n->code = NR_CURVETO;
3645            } else if (appr_p < appr_n) { // closer to n
3646                n->dragging_out = &n->n;
3647                opposite = &n->p;
3648                n->n.other->code = NR_CURVETO;
3649            } else { // p and n nodes are the same
3650                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3651                    n->dragging_out = &n->p;
3652                    opposite = &n->n;
3653                    n->code = NR_CURVETO;
3654                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3655                    n->dragging_out = &n->n;
3656                    opposite = &n->p;
3657                    n->n.other->code = NR_CURVETO;
3658                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3659                    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);
3660                    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);
3661                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3662                        n->dragging_out = &n->n;
3663                        opposite = &n->p;
3664                        n->n.other->code = NR_CURVETO;
3665                    } else { // closer to other's n handle
3666                        n->dragging_out = &n->p;
3667                        opposite = &n->n;
3668                        n->code = NR_CURVETO;
3669                    }
3670                }
3671            }
3673            // if there's another handle, make sure the one we drag out starts parallel to it
3674            if (opposite->pos != n->pos) {
3675                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3676            }
3678            // knots might not be created yet!
3679            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3680            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3681        }
3683        // pass this on to the handle-moved callback
3684        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3685        sp_node_update_handles(n);
3686        return TRUE;
3687    }
3689     if (state & GDK_CONTROL_MASK) { // constrained motion
3691         // calculate relative distances of handles
3692         // n handle:
3693         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3694         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3695         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3696         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3697             if (n->n.other) { // if there is the next point
3698                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3699                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3700                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3701             }
3702         }
3703         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3704         if (yn < 0) { xn = -xn; yn = -yn; }
3706         // p handle:
3707         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3708         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3709         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3710         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3711             if (n->p.other) {
3712                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3713                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3714                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3715             }
3716         }
3717         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3718         if (yp < 0) { xp = -xp; yp = -yp; }
3720         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3721             // sliding on handles, only if at least one of the handles is non-vertical
3722             // (otherwise it's the same as ctrl+drag anyway)
3724             // calculate angles of the handles
3725             if (xn == 0) {
3726                 if (yn == 0) { // no handle, consider it the continuation of the other one
3727                     an = 0;
3728                     collinear = TRUE;
3729                 }
3730                 else an = 0; // vertical; set the angle to horizontal
3731             } else an = yn/xn;
3733             if (xp == 0) {
3734                 if (yp == 0) { // no handle, consider it the continuation of the other one
3735                     ap = an;
3736                 }
3737                 else ap = 0; // vertical; set the angle to horizontal
3738             } else  ap = yp/xp;
3740             if (collinear) an = ap;
3742             // angles of the perpendiculars; HUGE_VAL means vertical
3743             if (an == 0) na = HUGE_VAL; else na = -1/an;
3744             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3746             // mouse point relative to the node's original pos
3747             pr = (*p) - n->origin;
3749             // distances to the four lines (two handles and two perpendiculars)
3750             d_an = point_line_distance(&pr, an);
3751             d_na = point_line_distance(&pr, na);
3752             d_ap = point_line_distance(&pr, ap);
3753             d_pa = point_line_distance(&pr, pa);
3755             // find out which line is the closest, save its closest point in c
3756             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3757                 point_line_closest(&pr, an, &c);
3758             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3759                 point_line_closest(&pr, ap, &c);
3760             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3761                 point_line_closest(&pr, na, &c);
3762             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3763                 point_line_closest(&pr, pa, &c);
3764             }
3766             // move the node to the closest point
3767             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3768                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3769                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y], 
3770                                             true);
3772         } else {  // constraining to hor/vert
3774             if (fabs((*p)[Geom::X] - n->origin[Geom::X]) > fabs((*p)[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3775                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3776                                                 (*p)[Geom::X] - n->pos[Geom::X], 
3777                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3778                                                 true, 
3779                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3780             } else { // snap to vert
3781                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3782                                                 n->origin[Geom::X] - n->pos[Geom::X],
3783                                                 (*p)[Geom::Y] - n->pos[Geom::Y],
3784                                                 true,
3785                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3786             }
3787         }
3788     } else { // move freely
3789         if (n->is_dragging) {
3790             if (state & GDK_MOD1_MASK) { // sculpt
3791                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3792             } else {
3793                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3794                                             (*p)[Geom::X] - n->pos[Geom::X],
3795                                             (*p)[Geom::Y] - n->pos[Geom::Y],
3796                                             (state & GDK_SHIFT_MASK) == 0);
3797             }
3798         }
3799     }
3801     n->subpath->nodepath->desktop->scroll_to_point(*p);
3803     return TRUE;
3806 /**
3807  * Node handle clicked callback.
3808  */
3809 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3811    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3813     if (state & GDK_CONTROL_MASK) { // "delete" handle
3814         if (n->p.knot == knot) {
3815             n->p.pos = n->pos;
3816         } else if (n->n.knot == knot) {
3817             n->n.pos = n->pos;
3818         }
3819         sp_node_update_handles(n);
3820         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3821         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3822         sp_nodepath_update_statusbar(nodepath);
3824     } else { // just select or add to selection, depending in Shift
3825         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3826     }
3829 /**
3830  * Node handle grabbed callback.
3831  */
3832 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3834    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3836     // convert auto -> smooth when dragging handle
3837    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3838         n->type = Inkscape::NodePath::NODE_SMOOTH;
3839         sp_nodepath_update_node_knot (n);
3840    }
3842     if (!n->selected) {
3843         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3844     }
3846     // remember the origin point of the handle
3847     if (n->p.knot == knot) {
3848         n->p.origin_radial = n->p.pos - n->pos;
3849     } else if (n->n.knot == knot) {
3850         n->n.origin_radial = n->n.pos - n->pos;
3851     } else {
3852         g_assert_not_reached();
3853     }
3855     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3858 /**
3859  * Node handle ungrabbed callback.
3860  */
3861 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3863    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3865     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3866     if (n->p.knot == knot) {
3867         n->p.origin_radial.a = 0;
3868         sp_knot_set_position(knot, n->p.pos, state);
3869     } else if (n->n.knot == knot) {
3870         n->n.origin_radial.a = 0;
3871         sp_knot_set_position(knot, n->n.pos, state);
3872     } else {
3873         g_assert_not_reached();
3874     }
3876     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3879 /**
3880  * Node handle "request" signal callback.
3881  */
3882 static gboolean node_handle_request(SPKnot *knot, Geom::Point *p, guint state, gpointer data)
3884     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3886     Inkscape::NodePath::NodeSide *me, *opposite;
3887     gint which;
3888     if (n->p.knot == knot) {
3889         me = &n->p;
3890         opposite = &n->n;
3891         which = -1;
3892     } else if (n->n.knot == knot) {
3893         me = &n->n;
3894         opposite = &n->p;
3895         which = 1;
3896     } else {
3897         me = opposite = NULL;
3898         which = 0;
3899         g_assert_not_reached();
3900     }
3902     SPDesktop *desktop = n->subpath->nodepath->desktop;
3903     SnapManager &m = desktop->namedview->snap_manager;
3904     m.setup(desktop, true, n->subpath->nodepath->item);
3905     Inkscape::SnappedPoint s;
3906     
3907     if ((state & GDK_SHIFT_MASK) != 0) {
3908         // We will not try to snap when the shift-key is pressed
3909         // so remove the old snap indicator and don't wait for it to time-out  
3910         desktop->snapindicator->remove_snappoint();     
3911     }
3913     Inkscape::NodePath::Node *othernode = opposite->other;
3914     if (othernode) {
3915         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3916             /* We are smooth node adjacent with line */
3917             Geom::Point const delta = *p - n->pos;
3918             Geom::Coord const len = Geom::L2(delta);
3919             Inkscape::NodePath::Node *othernode = opposite->other;
3920             Geom::Point const ndelta = n->pos - othernode->pos;
3921             Geom::Coord const linelen = Geom::L2(ndelta);
3922             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3923                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3924                 (*p) = n->pos + (scal / linelen) * ndelta;
3925             }
3926             if ((state & GDK_SHIFT_MASK) == 0) {
3927                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(*p), Inkscape::Snapper::ConstraintLine(*p, ndelta));
3928             }
3929         } else {
3930                 if ((state & GDK_SHIFT_MASK) == 0) {
3931                         s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(*p));
3932                 }
3933         }
3934     } else {
3935         if ((state & GDK_SHIFT_MASK) == 0) {
3936                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(*p));
3937         }
3938     }
3939     
3940     Geom::Point pt2g = *p;
3941     s.getPoint(pt2g);
3942     *p = pt2g;
3943     
3944     sp_node_adjust_handle(n, -which);
3946     return FALSE;
3949 /**
3950  * Node handle moved callback.
3951  */
3952 static void node_handle_moved(SPKnot *knot, Geom::Point *p, guint state, gpointer data)
3954    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3955    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3957    Inkscape::NodePath::NodeSide *me;
3958    Inkscape::NodePath::NodeSide *other;
3959     if (n->p.knot == knot) {
3960         me = &n->p;
3961         other = &n->n;
3962     } else if (n->n.knot == knot) {
3963         me = &n->n;
3964         other = &n->p;
3965     } else {
3966         me = NULL;
3967         other = NULL;
3968         g_assert_not_reached();
3969     }
3971     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3972     Radial rme(me->pos - n->pos);
3973     Radial rother(other->pos - n->pos);
3974     Radial rnew(*p - n->pos);
3976     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3977         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
3978         /* 0 interpreted as "no snapping". */
3980         // 1. Snap to the closest PI/snaps angle, starting from zero.
3981         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3983         // 2. Snap to the original angle, its opposite and perpendiculars
3984         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3985             /* The closest PI/2 angle, starting from original angle */
3986             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3988             // Snap to the closest.
3989             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3990                        ? a_snapped
3991                        : a_ortho );
3992         }
3994         // 3. Snap to the angle of the opposite line, if any
3995         Inkscape::NodePath::Node *othernode = other->other;
3996         if (othernode) {
3997             Geom::Point other_to_snap(0,0);
3998             if (sp_node_side_is_line(n, other)) {
3999                 other_to_snap = othernode->pos - n->pos;
4000             } else {
4001                 other_to_snap = other->pos - n->pos;
4002             }
4003             if (Geom::L2(other_to_snap) > 1e-3) {
4004                 Radial rother_to_snap(other_to_snap);
4005                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4006                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4008                 // Snap to the closest.
4009                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4010                        ? a_snapped
4011                        : a_oppo );
4012             }
4013         }
4015         rnew.a = a_snapped;
4016     }
4018     if (state & GDK_MOD1_MASK) {
4019         // lock handle length
4020         rnew.r = me->origin_radial.r;
4021     }
4023     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
4024         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
4025         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4026         rother.a += rnew.a - rme.a;
4027         other->pos = Geom::Point(rother) + n->pos;
4028         if (other->knot) {
4029             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4030             sp_knot_moveto(other->knot, other->pos);
4031         }
4032     }
4034     me->pos = Geom::Point(rnew) + n->pos;
4035     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4037     // move knot, but without emitting the signal:
4038     // we cannot emit a "moved" signal because we're now processing it
4039     sp_knot_moveto(me->knot, me->pos);
4041     update_object(n->subpath->nodepath);
4043     /* status text */
4044     SPDesktop *desktop = n->subpath->nodepath->desktop;
4045     if (!desktop) return;
4046     SPEventContext *ec = desktop->event_context;
4047     if (!ec) return;
4049     Inkscape::MessageContext *mc = get_message_context(ec);
4051     if (!mc) return;
4053     double degrees = 180 / M_PI * rnew.a;
4054     if (degrees > 180) degrees -= 360;
4055     if (degrees < -180) degrees += 360;
4056     if (prefs->getBool("/options/compassangledisplay/value"))
4057         degrees = angle_to_compass (degrees);
4059     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4061     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4062          _("<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);
4064     g_string_free(length, TRUE);
4067 /**
4068  * Node handle event callback.
4069  */
4070 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4072     gboolean ret = FALSE;
4073     switch (event->type) {
4074         case GDK_KEY_PRESS:
4075             switch (get_group0_keyval (&event->key)) {
4076                 case GDK_space:
4077                     if (event->key.state & GDK_BUTTON1_MASK) {
4078                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4079                         stamp_repr(nodepath);
4080                         ret = TRUE;
4081                     }
4082                     break;
4083                 default:
4084                     break;
4085             }
4086             break;
4087         case GDK_ENTER_NOTIFY:
4088             // we use an experimentally determined threshold that seems to work fine
4089             if (Geom::L2(n->pos - knot->pos) < 0.75)
4090                 Inkscape::NodePath::Path::active_node = n;
4091             break;
4092         case GDK_LEAVE_NOTIFY:
4093             // we use an experimentally determined threshold that seems to work fine
4094             if (Geom::L2(n->pos - knot->pos) < 0.75)
4095                 Inkscape::NodePath::Path::active_node = NULL;
4096             break;
4097         default:
4098             break;
4099     }
4101     return ret;
4104 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4105                                  Radial &rme, Radial &rother, gboolean const both)
4107     rme.a += angle;
4108     if ( both
4109          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4110          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4111     {
4112         rother.a += angle;
4113     }
4116 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4117                                         Radial &rme, Radial &rother, gboolean const both)
4119     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4121     gdouble r;
4122     if ( both
4123          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4124          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4125     {
4126         r = MAX(rme.r, rother.r);
4127     } else {
4128         r = rme.r;
4129     }
4131     gdouble const weird_angle = atan2(norm_angle, r);
4132 /* Bulia says norm_angle is just the visible distance that the
4133  * object's end must travel on the screen.  Left as 'angle' for want of
4134  * a better name.*/
4136     rme.a += weird_angle;
4137     if ( both
4138          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4139          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4140     {
4141         rother.a += weird_angle;
4142     }
4145 /**
4146  * Rotate one node.
4147  */
4148 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4150     Inkscape::NodePath::NodeSide *me, *other;
4151     bool both = false;
4153     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4154     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4156     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4157         me = &(n->p);
4158         other = &(n->n);
4159     } else if (!n->p.other) {
4160         me = &(n->n);
4161         other = &(n->p);
4162     } else {
4163         if (which > 0) { // right handle
4164             if (xn > xp) {
4165                 me = &(n->n);
4166                 other = &(n->p);
4167             } else {
4168                 me = &(n->p);
4169                 other = &(n->n);
4170             }
4171         } else if (which < 0){ // left handle
4172             if (xn <= xp) {
4173                 me = &(n->n);
4174                 other = &(n->p);
4175             } else {
4176                 me = &(n->p);
4177                 other = &(n->n);
4178             }
4179         } else { // both handles
4180             me = &(n->n);
4181             other = &(n->p);
4182             both = true;
4183         }
4184     }
4186     Radial rme(me->pos - n->pos);
4187     Radial rother(other->pos - n->pos);
4189     if (screen) {
4190         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4191     } else {
4192         node_rotate_one_internal (*n, angle, rme, rother, both);
4193     }
4195     me->pos = n->pos + Geom::Point(rme);
4197     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4198         other->pos =  n->pos + Geom::Point(rother);
4199     }
4201     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4202     // so here we just move all the knots without emitting move signals, for speed
4203     sp_node_update_handles(n, false);
4206 /**
4207  * Rotate selected nodes.
4208  */
4209 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4211     if (!nodepath || !nodepath->selected) return;
4213     if (g_list_length(nodepath->selected) == 1) {
4214        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4215         node_rotate_one (n, angle, which, screen);
4216     } else {
4217        // rotate as an object:
4219         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4220         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4221         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4222             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4223             box.expandTo (n->pos); // contain all selected nodes
4224         }
4226         gdouble rot;
4227         if (screen) {
4228             gdouble const zoom = nodepath->desktop->current_zoom();
4229             gdouble const zmove = angle / zoom;
4230             gdouble const r = Geom::L2(box.max() - box.midpoint());
4231             rot = atan2(zmove, r);
4232         } else {
4233             rot = angle;
4234         }
4236         Geom::Point rot_center;
4237         if (Inkscape::NodePath::Path::active_node == NULL)
4238             rot_center = box.midpoint();
4239         else
4240             rot_center = Inkscape::NodePath::Path::active_node->pos;
4242         Geom::Matrix t =
4243             Geom::Matrix (Geom::Translate(-rot_center)) *
4244             Geom::Matrix (Geom::Rotate(rot)) *
4245             Geom::Matrix (Geom::Translate(rot_center));
4247         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4248             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4249             n->pos *= t;
4250             n->n.pos *= t;
4251             n->p.pos *= t;
4252             sp_node_update_handles(n, false);
4253         }
4254     }
4256     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4259 /**
4260  * Scale one node.
4261  */
4262 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4264     bool both = false;
4265     Inkscape::NodePath::NodeSide *me, *other;
4267     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4268     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4270     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4271         me = &(n->p);
4272         other = &(n->n);
4273         n->code = NR_CURVETO;
4274     } else if (!n->p.other) {
4275         me = &(n->n);
4276         other = &(n->p);
4277         if (n->n.other)
4278             n->n.other->code = NR_CURVETO;
4279     } else {
4280         if (which > 0) { // right handle
4281             if (xn > xp) {
4282                 me = &(n->n);
4283                 other = &(n->p);
4284                 if (n->n.other)
4285                     n->n.other->code = NR_CURVETO;
4286             } else {
4287                 me = &(n->p);
4288                 other = &(n->n);
4289                 n->code = NR_CURVETO;
4290             }
4291         } else if (which < 0){ // left handle
4292             if (xn <= xp) {
4293                 me = &(n->n);
4294                 other = &(n->p);
4295                 if (n->n.other)
4296                     n->n.other->code = NR_CURVETO;
4297             } else {
4298                 me = &(n->p);
4299                 other = &(n->n);
4300                 n->code = NR_CURVETO;
4301             }
4302         } else { // both handles
4303             me = &(n->n);
4304             other = &(n->p);
4305             both = true;
4306             n->code = NR_CURVETO;
4307             if (n->n.other)
4308                 n->n.other->code = NR_CURVETO;
4309         }
4310     }
4312     Radial rme(me->pos - n->pos);
4313     Radial rother(other->pos - n->pos);
4315     rme.r += grow;
4316     if (rme.r < 0) rme.r = 0;
4317     if (rme.a == HUGE_VAL) {
4318         if (me->other) { // if direction is unknown, initialize it towards the next node
4319             Radial rme_next(me->other->pos - n->pos);
4320             rme.a = rme_next.a;
4321         } else { // if there's no next, initialize to 0
4322             rme.a = 0;
4323         }
4324     }
4325     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4326         rother.r += grow;
4327         if (rother.r < 0) rother.r = 0;
4328         if (rother.a == HUGE_VAL) {
4329             rother.a = rme.a + M_PI;
4330         }
4331     }
4333     me->pos = n->pos + Geom::Point(rme);
4335     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4336         other->pos = n->pos + Geom::Point(rother);
4337     }
4339     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4340     // so here we just move all the knots without emitting move signals, for speed
4341     sp_node_update_handles(n, false);
4344 /**
4345  * Scale selected nodes.
4346  */
4347 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4349     if (!nodepath || !nodepath->selected) return;
4351     if (g_list_length(nodepath->selected) == 1) {
4352         // scale handles of the single selected node
4353         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4354         node_scale_one (n, grow, which);
4355     } else {
4356         // scale nodes as an "object":
4358         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4359         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4360         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4361             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4362             box.expandTo (n->pos); // contain all selected nodes
4363         }
4365         double scale = (box.maxExtent() + grow)/box.maxExtent();
4367         Geom::Point scale_center;
4368         if (Inkscape::NodePath::Path::active_node == NULL)
4369             scale_center = box.midpoint();
4370         else
4371             scale_center = Inkscape::NodePath::Path::active_node->pos;
4373         Geom::Matrix t =
4374             Geom::Matrix (Geom::Translate(-scale_center)) *
4375             Geom::Matrix (Geom::Scale(scale, scale)) *
4376             Geom::Matrix (Geom::Translate(scale_center));
4378         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4379             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4380             n->pos *= t;
4381             n->n.pos *= t;
4382             n->p.pos *= t;
4383             sp_node_update_handles(n, false);
4384         }
4385     }
4387     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4390 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4392     if (!nodepath) return;
4393     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4396 /**
4397  * Flip selected nodes horizontally/vertically.
4398  */
4399 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4401     if (!nodepath || !nodepath->selected) return;
4403     if (g_list_length(nodepath->selected) == 1 && !center) {
4404         // flip handles of the single selected node
4405         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4406         double temp = n->p.pos[axis];
4407         n->p.pos[axis] = n->n.pos[axis];
4408         n->n.pos[axis] = temp;
4409         sp_node_update_handles(n, false);
4410     } else {
4411         // scale nodes as an "object":
4413         Geom::Rect box = sp_node_selected_bbox (nodepath);
4414         if (!center) {
4415             center = box.midpoint();
4416         }
4417         Geom::Matrix t =
4418             Geom::Matrix (Geom::Translate(- *center)) *
4419             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4420             Geom::Matrix (Geom::Translate(*center));
4422         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4423             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4424             n->pos *= t;
4425             n->n.pos *= t;
4426             n->p.pos *= t;
4427             sp_node_update_handles(n, false);
4428         }
4429     }
4431     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4434 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4436     g_assert (nodepath->selected);
4438     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4439     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4440     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4441         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4442         box.expandTo (n->pos); // contain all selected nodes
4443     }
4444     return box;
4447 //-----------------------------------------------
4448 /**
4449  * Return new subpath under given nodepath.
4450  */
4451 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4453     g_assert(nodepath);
4454     g_assert(nodepath->desktop);
4456    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4458     s->nodepath = nodepath;
4459     s->closed = FALSE;
4460     s->nodes = NULL;
4461     s->first = NULL;
4462     s->last = NULL;
4464     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4465     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4466     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4468     return s;
4471 /**
4472  * Destroy nodes in subpath, then subpath itself.
4473  */
4474 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4476     g_assert(subpath);
4477     g_assert(subpath->nodepath);
4478     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4480     while (subpath->nodes) {
4481         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4482     }
4484     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4486     g_free(subpath);
4489 /**
4490  * Link head to tail in subpath.
4491  */
4492 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4494     g_assert(!sp->closed);
4495     g_assert(sp->last != sp->first);
4496     g_assert(sp->first->code == NR_MOVETO);
4498     sp->closed = TRUE;
4500     //Link the head to the tail
4501     sp->first->p.other = sp->last;
4502     sp->last->n.other  = sp->first;
4503     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4504     sp->first          = sp->last;
4506     //Remove the extra end node
4507     sp_nodepath_node_destroy(sp->last->n.other);
4510 /**
4511  * Open closed (loopy) subpath at node.
4512  */
4513 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4515     g_assert(sp->closed);
4516     g_assert(n->subpath == sp);
4517     g_assert(sp->first == sp->last);
4519     /* We create new startpoint, current node will become last one */
4521    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4522                                                 &n->pos, &n->pos, &n->n.pos);
4525     sp->closed        = FALSE;
4527     //Unlink to make a head and tail
4528     sp->first         = new_path;
4529     sp->last          = n;
4530     n->n.other        = NULL;
4531     new_path->p.other = NULL;
4534 /**
4535  * Return new node in subpath with given properties.
4536  * \param pos Position of node.
4537  * \param ppos Handle position in previous direction
4538  * \param npos Handle position in previous direction
4539  */
4540 Inkscape::NodePath::Node *
4541 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)
4543     g_assert(sp);
4544     g_assert(sp->nodepath);
4545     g_assert(sp->nodepath->desktop);
4547     if (nodechunk == NULL)
4548         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4550     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4552     n->subpath  = sp;
4554     if (type != Inkscape::NodePath::NODE_NONE) {
4555         // use the type from sodipodi:nodetypes
4556         n->type = type;
4557     } else {
4558         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4559             // points are (almost) collinear
4560             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4561                 // endnode, or a node with a retracted handle
4562                 n->type = Inkscape::NodePath::NODE_CUSP;
4563             } else {
4564                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4565             }
4566         } else {
4567             n->type = Inkscape::NodePath::NODE_CUSP;
4568         }
4569     }
4571     n->code     = code;
4572     n->selected = FALSE;
4573     n->pos      = *pos;
4574     n->p.pos    = *ppos;
4575     n->n.pos    = *npos;
4577     n->dragging_out = NULL;
4579     Inkscape::NodePath::Node *prev;
4580     if (next) {
4581         //g_assert(g_list_find(sp->nodes, next));
4582         prev = next->p.other;
4583     } else {
4584         prev = sp->last;
4585     }
4587     if (prev)
4588         prev->n.other = n;
4589     else
4590         sp->first = n;
4592     if (next)
4593         next->p.other = n;
4594     else
4595         sp->last = n;
4597     n->p.other = prev;
4598     n->n.other = next;
4600     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"));
4601     sp_knot_set_position(n->knot, *pos, 0);
4603     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4604     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4605     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4607     sp_nodepath_update_node_knot(n);
4609     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4610     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4611     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4612     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4613     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4614     sp_knot_show(n->knot);
4616     // We only create handle knots and lines on demand
4617     n->p.knot = NULL;
4618     n->p.line = NULL;
4619     n->n.knot = NULL;
4620     n->n.line = NULL;
4622     sp->nodes = g_list_prepend(sp->nodes, n);
4624     return n;
4627 /**
4628  * Destroy node and its knots, link neighbors in subpath.
4629  */
4630 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4632     g_assert(node);
4633     g_assert(node->subpath);
4634     g_assert(SP_IS_KNOT(node->knot));
4636    Inkscape::NodePath::SubPath *sp = node->subpath;
4638     if (node->selected) { // first, deselect
4639         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4640         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4641     }
4643     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4645     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4646     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4647     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4648     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4649     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4650     g_object_unref(G_OBJECT(node->knot));
4652     if (node->p.knot) {
4653         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4654         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4655         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4656         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4657         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4658         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4659         g_object_unref(G_OBJECT(node->p.knot));
4660         node->p.knot = NULL;
4661     }
4663     if (node->n.knot) {
4664         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4665         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4666         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4667         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4668         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4669         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4670         g_object_unref(G_OBJECT(node->n.knot));
4671         node->n.knot = NULL;
4672     }
4674     if (node->p.line)
4675         gtk_object_destroy(GTK_OBJECT(node->p.line));
4676     if (node->n.line)
4677         gtk_object_destroy(GTK_OBJECT(node->n.line));
4679     if (sp->nodes) { // there are others nodes on the subpath
4680         if (sp->closed) {
4681             if (sp->first == node) {
4682                 g_assert(sp->last == node);
4683                 sp->first = node->n.other;
4684                 sp->last = sp->first;
4685             }
4686             node->p.other->n.other = node->n.other;
4687             node->n.other->p.other = node->p.other;
4688         } else {
4689             if (sp->first == node) {
4690                 sp->first = node->n.other;
4691                 sp->first->code = NR_MOVETO;
4692             }
4693             if (sp->last == node) sp->last = node->p.other;
4694             if (node->p.other) node->p.other->n.other = node->n.other;
4695             if (node->n.other) node->n.other->p.other = node->p.other;
4696         }
4697     } else { // this was the last node on subpath
4698         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4699     }
4701     g_mem_chunk_free(nodechunk, node);
4704 /**
4705  * Returns one of the node's two sides.
4706  * \param which Indicates which side.
4707  * \return Pointer to previous node side if which==-1, next if which==1.
4708  */
4709 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4711     g_assert(node);
4712     Inkscape::NodePath::NodeSide * result = 0;
4713     switch (which) {
4714         case -1:
4715             result = &node->p;
4716             break;
4717         case 1:
4718             result = &node->n;
4719             break;
4720         default:
4721             g_assert_not_reached();
4722     }
4724     return result;
4727 /**
4728  * Return the other side of the node, given one of its sides.
4729  */
4730 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4732     g_assert(node);
4733     Inkscape::NodePath::NodeSide *result = 0;
4735     if (me == &node->p) {
4736         result = &node->n;
4737     } else if (me == &node->n) {
4738         result = &node->p;
4739     } else {
4740         g_assert_not_reached();
4741     }
4743     return result;
4746 /**
4747  * Return NRPathcode on the given side of the node.
4748  */
4749 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4751     g_assert(node);
4753     NRPathcode result = NR_END;
4754     if (me == &node->p) {
4755         if (node->p.other) {
4756             result = (NRPathcode)node->code;
4757         } else {
4758             result = NR_MOVETO;
4759         }
4760     } else if (me == &node->n) {
4761         if (node->n.other) {
4762             result = (NRPathcode)node->n.other->code;
4763         } else {
4764             result = NR_MOVETO;
4765         }
4766     } else {
4767         g_assert_not_reached();
4768     }
4770     return result;
4773 /**
4774  * Return node with the given index
4775  */
4776 Inkscape::NodePath::Node *
4777 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4779     Inkscape::NodePath::Node *e = NULL;
4781     if (!nodepath) {
4782         return e;
4783     }
4785     //find segment
4786     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4788         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4789         int n = g_list_length(sp->nodes);
4790         if (sp->closed) {
4791             n++;
4792         }
4794         //if the piece belongs to this subpath grab it
4795         //otherwise move onto the next subpath
4796         if (index < n) {
4797             e = sp->first;
4798             for (int i = 0; i < index; ++i) {
4799                 e = e->n.other;
4800             }
4801             break;
4802         } else {
4803             if (sp->closed) {
4804                 index -= (n+1);
4805             } else {
4806                 index -= n;
4807             }
4808         }
4809     }
4811     return e;
4814 /**
4815  * Returns plain text meaning of node type.
4816  */
4817 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4819     unsigned retracted = 0;
4820     bool endnode = false;
4822     for (int which = -1; which <= 1; which += 2) {
4823         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4824         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4825             retracted ++;
4826         if (!side->other)
4827             endnode = true;
4828     }
4830     if (retracted == 0) {
4831         if (endnode) {
4832                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4833                 return _("end node");
4834         } else {
4835             switch (node->type) {
4836                 case Inkscape::NodePath::NODE_CUSP:
4837                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4838                     return _("cusp");
4839                 case Inkscape::NodePath::NODE_SMOOTH:
4840                     // TRANSLATORS: "smooth" is an adjective here
4841                     return _("smooth");
4842                 case Inkscape::NodePath::NODE_AUTO:
4843                     return _("auto");
4844                 case Inkscape::NodePath::NODE_SYMM:
4845                     return _("symmetric");
4846             }
4847         }
4848     } else if (retracted == 1) {
4849         if (endnode) {
4850             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4851             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4852         } else {
4853             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4854         }
4855     } else {
4856         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4857     }
4859     return NULL;
4862 /**
4863  * Handles content of statusbar as long as node tool is active.
4864  */
4865 void
4866 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4868     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");
4869     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4871     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4872     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4873     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4874     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4876     SPDesktop *desktop = NULL;
4877     if (nodepath) {
4878         desktop = nodepath->desktop;
4879     } else {
4880         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4881     }
4883     SPEventContext *ec = desktop->event_context;
4884     if (!ec) return;
4886     Inkscape::MessageContext *mc = get_message_context(ec);
4887     if (!mc) return;
4889     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4891     if (selected_nodes == 0) {
4892         Inkscape::Selection *sel = desktop->selection;
4893         if (!sel || sel->isEmpty()) {
4894             mc->setF(Inkscape::NORMAL_MESSAGE,
4895                      _("Select a single object to edit its nodes or handles."));
4896         } else {
4897             if (nodepath) {
4898             mc->setF(Inkscape::NORMAL_MESSAGE,
4899                      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.",
4900                               "<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.",
4901                               total_nodes),
4902                      total_nodes);
4903             } else {
4904                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4905                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4906                 } else {
4907                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4908                 }
4909             }
4910         }
4911     } else if (nodepath && selected_nodes == 1) {
4912         mc->setF(Inkscape::NORMAL_MESSAGE,
4913                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4914                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4915                           total_nodes),
4916                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4917     } else {
4918         if (selected_subpaths > 1) {
4919             mc->setF(Inkscape::NORMAL_MESSAGE,
4920                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4921                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4922                               total_nodes),
4923                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4924         } else {
4925             mc->setF(Inkscape::NORMAL_MESSAGE,
4926                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4927                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4928                               total_nodes),
4929                      selected_nodes, total_nodes, when_selected);
4930         }
4931     }
4934 /*
4935  * returns a *copy* of the curve of that object.
4936  */
4937 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4938     if (!object)
4939         return NULL;
4941     SPCurve *curve = NULL;
4942     if (SP_IS_PATH(object)) {
4943         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4944         curve = curve_new->copy();
4945     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4946         const gchar *svgd = object->repr->attribute(key);
4947         if (svgd) {
4948             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4949             SPCurve *curve_new = new SPCurve(pv);
4950             if (curve_new) {
4951                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4952             }
4953         }
4954     }
4956     return curve;
4959 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4960     if (!np || !np->object || !curve)
4961         return;
4963     if (SP_IS_PATH(np->object)) {
4964         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4965             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4966         } else {
4967             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4968         }
4969     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4970         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
4971         if (lpe) {
4972             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
4973             if (pathparam) {
4974                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4975                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4976             }
4977         }
4978     }
4981 /*
4982 SPCanvasItem *
4983 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4984     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
4986 */
4988 /*
4989 SPCanvasItem *
4990 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4991     SPCurve *flash_curve = curve->copy();
4992     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4993     flash_curve->transform(i2d);
4994     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4995     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4996     // unless we also flash the nodes...
4997     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4998     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4999     sp_canvas_item_show(canvasitem);
5000     flash_curve->unref();
5001     return canvasitem;
5004 SPCanvasItem *
5005 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
5006     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5007     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
5008                                            prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff));
5010 */
5012 SPCanvasItem *
5013 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
5014     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
5015     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
5016     flash_curve->transform(i2d);
5017     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5018     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5019     // unless we also flash the nodes...
5020     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5021     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5022     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5023     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5024     sp_canvas_item_show(canvasitem);
5025     flash_curve->unref();
5026     return canvasitem;
5029 // TODO: Merge this with sp_nodepath_make_helper_item()!
5030 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5031     np->show_helperpath = show;
5033     if (show) {
5034         SPCurve *helper_curve = np->curve->copy();
5035         helper_curve->transform(np->i2d);
5036         if (!np->helper_path) {
5037             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5039             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5040             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);
5041             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5042             sp_canvas_item_move_to_z(np->helper_path, 0);
5043             sp_canvas_item_show(np->helper_path);
5044         } else {
5045             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5046         }
5047         helper_curve->unref();
5048     } else {
5049         if (np->helper_path) {
5050             GtkObject *temp = np->helper_path;
5051             np->helper_path = NULL;
5052             gtk_object_destroy(temp);
5053         }
5054     }
5057 /* sp_nodepath_make_straight_path:
5058  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5059  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5060  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5061  */
5062 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5063     np->straight_path = true;
5064     np->show_handles = false;
5065     g_message("add code to make the path straight.");
5066     // do sp_nodepath_convert_node_type on all nodes?
5067     // coding tip: search for this text : "Make selected segments lines"
5070 /*
5071   Local Variables:
5072   mode:c++
5073   c-file-style:"stroustrup"
5074   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5075   indent-tabs-mode:nil
5076   fill-column:99
5077   End:
5078 */
5079 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :