Code

Updated Russian translation
[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 "sp-text.h"
49 #include "sp-shape.h"
50 #include "libnr/nr-matrix-ops.h"
51 #include "svg/svg.h"
52 #include "verbs.h"
53 #include <2geom/bezier-utils.h>
54 #include <vector>
55 #include <algorithm>
56 #include <cstring>
57 #include <cmath>
58 #include "live_effects/lpeobject.h"
59 #include "live_effects/lpeobject-reference.h"
60 #include "live_effects/effect.h"
61 #include "live_effects/parameter/parameter.h"
62 #include "live_effects/parameter/path.h"
63 #include "util/mathfns.h"
64 #include "display/snap-indicator.h"
65 #include "snapped-point.h"
67 namespace Geom { class Matrix; }
69 /// \todo
70 /// evil evil evil. FIXME: conflict of two different Path classes!
71 /// There is a conflict in the namespace between two classes named Path.
72 /// #include "sp-flowtext.h"
73 /// #include "sp-flowregion.h"
75 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
76 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
77 GType sp_flowregion_get_type (void);
78 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
79 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
80 GType sp_flowtext_get_type (void);
81 // end evil workaround
83 #include "helper/stlport.h"
86 /// \todo fixme: Implement these via preferences */
88 #define NODE_FILL          0xbfbfbf00
89 #define NODE_STROKE        0x000000ff
90 #define NODE_FILL_HI       0xff000000
91 #define NODE_STROKE_HI     0x000000ff
92 #define NODE_FILL_SEL      0x0000ffff
93 #define NODE_STROKE_SEL    0x000000ff
94 #define NODE_FILL_SEL_HI   0xff000000
95 #define NODE_STROKE_SEL_HI 0x000000ff
96 #define KNOT_FILL          0xffffffff
97 #define KNOT_STROKE        0x000000ff
98 #define KNOT_FILL_HI       0xff000000
99 #define KNOT_STROKE_HI     0x000000ff
101 static GMemChunk *nodechunk = NULL;
103 /* Creation from object */
105 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
106 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
108 /* Object updating */
110 static void stamp_repr(Inkscape::NodePath::Path *np);
111 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
112 static gchar *create_typestr(Inkscape::NodePath::Path *np);
114 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
116 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
118 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
120 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
122 /* Adjust handle placement, if the node or the other handle is moved */
123 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
124 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
125 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node);
127 /* Node event callbacks */
128 static void node_clicked(SPKnot *knot, guint state, gpointer data);
129 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
130 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
131 static gboolean node_request(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
133 /* Handle event callbacks */
134 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
135 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
136 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
137 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data);
138 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
139 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
141 /* Constructors and destructors */
143 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
144 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
145 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
146 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
147 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
148                                          Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos);
149 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
151 /* Helpers */
153 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
154 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
155 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
157 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
158 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
160 // active_node indicates mouseover node
161 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
163 static SPCanvasItem *
164 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false, guint32 color = 0xff0000ff) {
165     SPCurve *helper_curve = curve->copy();
166     helper_curve->transform(np->i2d);
167     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
168     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
169     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
170     sp_canvas_item_move_to_z(helper_path, 0);
171     if (show) {
172         sp_canvas_item_show(helper_path);
173     }
174     helper_curve->unref();
175     return helper_path;
178 static void
179 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
180     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
181     if (!SP_IS_LPE_ITEM(np->item)) {
182         g_print ("Only LPEItems can have helperpaths!\n");
183         return;
184     }
186     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
187     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
188     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
189         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
190         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->get_lpe();
191         if (lpe) {
192             // create new canvas items from the effect's helper paths
193             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
194             for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
195                 SPCurve *helper_curve = new SPCurve(*j);
196                 SPCanvasItem * canvasitem = sp_nodepath_make_helper_item(np, helper_curve, true, 0x509050dd);
197                 np->helper_path_vec[lpe].push_back(canvasitem);
198                 helper_curve->unref();
199             }
200         }
201     }
204 void
205 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
206     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
207     if (!SP_IS_LPE_ITEM(np->item)) {
208         g_print ("Only LPEItems can have helperpaths!\n");
209         return;
210     }
212     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
213     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
214     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
215         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
216         if (lpe) {
217             /* update canvas items from the effect's helper paths; note that this code relies on the
218              * fact that getHelperPaths() will always return the same number of helperpaths in the same
219              * order as during their creation in sp_nodepath_create_helperpaths
220              */
221             std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
222             for (unsigned int j = 0; j < hpaths.size(); ++j) {
223                 SPCurve *curve = new SPCurve(hpaths[j]);
224                 curve->transform(np->i2d);
225                 sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH((np->helper_path_vec[lpe])[j]), curve);
226                 curve = curve->unref();
227             }
228         }
229     }
232 static void
233 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
234     for (HelperPathList::iterator i = np->helper_path_vec.begin(); i != np->helper_path_vec.end(); ++i) {
235         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
236             GtkObject *temp = *j;
237             *j = NULL;
238             gtk_object_destroy(temp);
239         }
240     }
241     np->helper_path_vec.clear();
245 /**
246  * \brief Creates new nodepath from item
247  *
248  * If repr_key_in is not NULL, object *has* to be a LivePathEffectObject !
249  *
250  * \todo create proper constructor for nodepath::path, this method returns null a constructor cannot so this cannot be simply converted to constructor.
251  */
252 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
254     if (repr_key_in) {
255         g_assert(IS_LIVEPATHEFFECT(object));
256     }
258     Inkscape::XML::Node *repr = object->repr;
260     /** \todo
261      * FIXME: remove this. We don't want to edit paths inside flowtext.
262      * Instead we will build our flowtext with cloned paths, so that the
263      * real paths are outside the flowtext and thus editable as usual.
264      */
265     if (SP_IS_FLOWTEXT(object)) {
266         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
267             if SP_IS_FLOWREGION(child) {
268                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
269                 if (grandchild && SP_IS_PATH(grandchild)) {
270                     object = SP_ITEM(grandchild);
271                     break;
272                 }
273             }
274         }
275     }
277     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
279     if (curve == NULL) {
280         return NULL;
281     }
283     if (curve->get_segment_count() < 1) {
284         curve->unref();
285         return NULL; // prevent crash for one-node paths
286     }
288     //Create new nodepath
289     Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path();
290     if (!np) {
291         curve->unref();
292         return NULL;
293     }
295     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
297     // Set defaults
298     np->desktop     = desktop;
299     np->object      = object;
300     np->subpaths    = NULL;
301     np->selected    = NULL;
302     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
303     np->local_change = 0;
304     np->show_handles = show_handles;
305     np->helper_path = NULL;
306     np->helperpath_rgba = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
307     np->helperpath_width = 1.0;
308     np->curve = curve->copy();
309     np->show_helperpath = prefs->getBool("/tools/nodes/show_helperpath");
310     if (SP_IS_LPE_ITEM(object)) {
311         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
312         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
313             np->show_helperpath = true;
314         }
315     }
316     np->straight_path = false;
317     if (IS_LIVEPATHEFFECT(object) && item) {
318         np->item = item;
319     } else {
320         np->item = SP_ITEM(object);
321     }
323     np->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
325     // we need to update item's transform from the repr here,
326     // because they may be out of sync when we respond
327     // to a change in repr by regenerating nodepath     --bb
328     sp_object_read_attr(SP_OBJECT(np->item), "transform");
330     np->i2d  = sp_item_i2d_affine(np->item);
331     np->d2i  = np->i2d.inverse();
333     np->repr = repr;
334     if (repr_key_in) { // apparently the object is an LPEObject
335         np->repr_key = g_strdup(repr_key_in);
336         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
337         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe();
338         if (!lpe) {
339             g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!");
340             delete np;
341         }
342         Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in);
343         if (lpeparam) {
344             lpeparam->param_setup_nodepath(np);
345         }
346     } else {
347         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
348         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
349             np->repr_key = g_strdup("inkscape:original-d");
351             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
352             if (lpe) {
353                 lpe->setup_nodepath(np);
354             }
355         } else {
356             np->repr_key = g_strdup("d");
357         }
358     }
360     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
361      * So for example a closed rectangle has a nodetypestring of length 5.
362      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
363     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
364     np->curve->set_pathvector(pathv_sanitized);
365     guint length = np->curve->get_segment_count();
366     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
367         length += pit->empty() ? 0 : 1;
368     }
370     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
371     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
373     // create the subpath(s) from the bpath
374     subpaths_from_pathvector(np, pathv_sanitized, typestr);
376     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
377     np->subpaths = g_list_reverse(np->subpaths);
379     delete[] typestr;
380     curve->unref();
382     // Draw helper curve
383     if (np->show_helperpath) {
384         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true, np->helperpath_rgba);
385     }
387     sp_nodepath_create_helperpaths(np);
389     return np;
392 /**
393  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
394  */
395 Inkscape::NodePath::Path::~Path() {
396     while (this->subpaths) {
397         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data);
398     }
400     //Inform the ShapeEditor that made me, if any, that I am gone.
401     if (this->shape_editor)
402         this->shape_editor->nodepath_destroyed();
404     g_assert(!this->selected);
406     if (this->helper_path) {
407         GtkObject *temp = this->helper_path;
408         this->helper_path = NULL;
409         gtk_object_destroy(temp);
410     }
411     if (this->curve) {
412         this->curve->unref();
413         this->curve = NULL;
414     }
416     if (this->repr_key) {
417         g_free(this->repr_key);
418         this->repr_key = NULL;
419     }
420     if (this->repr_nodetypes_key) {
421         g_free(this->repr_nodetypes_key);
422         this->repr_nodetypes_key = NULL;
423     }
425     sp_nodepath_destroy_helperpaths(this);
427     this->desktop = NULL;
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             if (!IS_FINITE(n->pos[0]) || !IS_FINITE(n->pos[1])){
775                 g_message("niet finite");
776             }
777             switch (n->code) {
778                 case NR_LINETO:
779                     curve->lineto(end_pt);
780                     break;
781                 case NR_CURVETO:
782                     curve->curveto(n->p.other->n.pos * np->d2i,
783                                      n->p.pos * np->d2i,
784                                      end_pt);
785                     break;
786                 default:
787                     g_assert_not_reached();
788                     break;
789             }
790             if (n != sp->last) {
791                 n = n->n.other;
792             } else {
793                 n = NULL;
794             }
795         }
796         if (sp->closed) {
797             curve->closepath();
798         }
799     }
801     return curve;
804 /**
805  * Convert path type string to sodipodi:nodetypes style.
806  */
807 static gchar *create_typestr(Inkscape::NodePath::Path *np)
809     gchar *typestr = g_new(gchar, 32);
810     gint len = 32;
811     gint pos = 0;
813     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
814        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
816         if (pos >= len) {
817             typestr = g_renew(gchar, typestr, len + 32);
818             len += 32;
819         }
821         typestr[pos++] = 'c';
823        Inkscape::NodePath::Node *n;
824         n = sp->first->n.other;
825         while (n) {
826             gchar code;
828             switch (n->type) {
829                 case Inkscape::NodePath::NODE_CUSP:
830                     code = 'c';
831                     break;
832                 case Inkscape::NodePath::NODE_SMOOTH:
833                     code = 's';
834                     break;
835                 case Inkscape::NodePath::NODE_AUTO:
836                     code = 'a';
837                     break;
838                 case Inkscape::NodePath::NODE_SYMM:
839                     code = 'z';
840                     break;
841                 default:
842                     g_assert_not_reached();
843                     code = '\0';
844                     break;
845             }
847             if (pos >= len) {
848                 typestr = g_renew(gchar, typestr, len + 32);
849                 len += 32;
850             }
852             typestr[pos++] = code;
854             if (n != sp->last) {
855                 n = n->n.other;
856             } else {
857                 n = NULL;
858             }
859         }
860     }
862     if (pos >= len) {
863         typestr = g_renew(gchar, typestr, len + 1);
864         len += 1;
865     }
867     typestr[pos++] = '\0';
869     return typestr;
872 // Returns different message contexts depending on the current context. This function should only
873 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
874 // other cases.
875 static Inkscape::MessageContext *
876 get_message_context(SPEventContext *ec)
878     Inkscape::MessageContext *mc = 0;
880     if (SP_IS_NODE_CONTEXT(ec)) {
881         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
882     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
883         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
884     } else {
885         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
886     }
888     return mc;
891 /**
892  \brief Fills node and handle positions for three nodes, splitting line
893   marked by end at distance t.
894  */
895 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
897     g_assert(new_path != NULL);
898     g_assert(end      != NULL);
900     g_assert(end->p.other == new_path);
901    Inkscape::NodePath::Node *start = new_path->p.other;
902     g_assert(start);
904     if (end->code == NR_LINETO) {
905         new_path->type =Inkscape::NodePath::NODE_CUSP;
906         new_path->code = NR_LINETO;
907         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
908     } else {
909         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
910         new_path->code = NR_CURVETO;
911         gdouble s      = 1 - t;
912         for (int dim = 0; dim < 2; dim++) {
913             Geom::Coord const f000 = start->pos[dim];
914             Geom::Coord const f001 = start->n.pos[dim];
915             Geom::Coord const f011 = end->p.pos[dim];
916             Geom::Coord const f111 = end->pos[dim];
917             Geom::Coord const f00t = s * f000 + t * f001;
918             Geom::Coord const f01t = s * f001 + t * f011;
919             Geom::Coord const f11t = s * f011 + t * f111;
920             Geom::Coord const f0tt = s * f00t + t * f01t;
921             Geom::Coord const f1tt = s * f01t + t * f11t;
922             Geom::Coord const fttt = s * f0tt + t * f1tt;
923             start->n.pos[dim]    = f00t;
924             new_path->p.pos[dim] = f0tt;
925             new_path->pos[dim]   = fttt;
926             new_path->n.pos[dim] = f1tt;
927             end->p.pos[dim]      = f11t;
928         }
929     }
932 /**
933  * Adds new node on direct line between two nodes, activates handles of all
934  * three nodes.
935  */
936 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
938     g_assert(end);
939     g_assert(end->subpath);
940     g_assert(g_list_find(end->subpath->nodes, end));
942    Inkscape::NodePath::Node *start = end->p.other;
943     g_assert( start->n.other == end );
944    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
945                                                end,
946                                                (NRPathcode)end->code == NR_LINETO?
947                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
948                                                (NRPathcode)end->code,
949                                                &start->pos, &start->pos, &start->n.pos);
950     sp_nodepath_line_midpoint(newnode, end, t);
952     sp_node_adjust_handles(start);
953     sp_node_update_handles(start);
954     sp_node_update_handles(newnode);
955     sp_node_adjust_handles(end);
956     sp_node_update_handles(end);
958     return newnode;
961 /**
962 \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
963 */
964 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
966     g_assert(node);
967     g_assert(node->subpath);
968     g_assert(g_list_find(node->subpath->nodes, node));
970     Inkscape::NodePath::Node* result = 0;
971     Inkscape::NodePath::SubPath *sp = node->subpath;
972     Inkscape::NodePath::Path *np    = sp->nodepath;
974     if (sp->closed) {
975         sp_nodepath_subpath_open(sp, node);
976         result = sp->first;
977     } else if ( (node == sp->first) || (node == sp->last ) ){
978         // no break for end nodes
979         result = 0;
980     } else {
981         // create a new subpath
982         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
984         // duplicate the break node as start of the new subpath
985         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
986                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
987                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
989         // attach rest of curve to new node
990         g_assert(node->n.other);
991         newnode->n.other = node->n.other; node->n.other = NULL;
992         newnode->n.other->p.other = newnode;
993         newsubpath->last = sp->last;
994         sp->last = node;
995         node = newnode;
996         while (node->n.other) {
997             node = node->n.other;
998             node->subpath = newsubpath;
999             sp->nodes = g_list_remove(sp->nodes, node);
1000             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
1001         }
1004         result = newnode;
1005     }
1006     return result;
1009 /**
1010  * Duplicate node and connect to neighbours.
1011  */
1012 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
1014     g_assert(node);
1015     g_assert(node->subpath);
1016     g_assert(g_list_find(node->subpath->nodes, node));
1018    Inkscape::NodePath::SubPath *sp = node->subpath;
1020     NRPathcode code = (NRPathcode) node->code;
1021     if (code == NR_MOVETO) { // if node is the endnode,
1022         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1023     }
1025     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1027     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1028         return node;
1029     } else {
1030         return newnode; // otherwise select the newly created node
1031     }
1034 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1036     node->p.pos = (node->pos + (node->pos - node->n.pos));
1039 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1041     node->n.pos = (node->pos + (node->pos - node->p.pos));
1044 /**
1045  * Change line type at node, with side effects on neighbours.
1046  */
1047 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1049     g_assert(end);
1050     g_assert(end->subpath);
1051     g_assert(end->p.other);
1053     if (end->code != static_cast<guint>(code) ) {
1054         Inkscape::NodePath::Node *start = end->p.other;
1056         end->code = code;
1058         if (code == NR_LINETO) {
1059             if (start->code == NR_LINETO) {
1060                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1061             }
1062             if (end->n.other) {
1063                 if (end->n.other->code == NR_LINETO) {
1064                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1065                 }
1066             }
1068             if (start->type == Inkscape::NodePath::NODE_AUTO)
1069                 start->type = Inkscape::NodePath::NODE_SMOOTH;
1070             if (end->type == Inkscape::NodePath::NODE_AUTO)
1071                 end->type = Inkscape::NodePath::NODE_SMOOTH;
1073             start->n.pos = start->pos;
1074             end->p.pos = end->pos;
1076             sp_node_adjust_handle(start, -1);
1077             sp_node_adjust_handle(end, 1);
1079         } else {
1080             Geom::Point delta = end->pos - start->pos;
1081             start->n.pos = start->pos + delta / 3;
1082             end->p.pos = end->pos - delta / 3;
1083             sp_node_adjust_handle(start, 1);
1084             sp_node_adjust_handle(end, -1);
1085         }
1087         sp_node_update_handles(start);
1088         sp_node_update_handles(end);
1089     }
1092 static void
1093 sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
1095     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1096         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1097         node->knot->setSize (node->selected? 11 : 9);
1098         sp_knot_update_ctrl(node->knot);
1099     } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
1100         node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1101         node->knot->setSize (node->selected? 11 : 9);
1102         sp_knot_update_ctrl(node->knot);
1103     } else {
1104         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1105         node->knot->setSize (node->selected? 9 : 7);
1106         sp_knot_update_ctrl(node->knot);
1107     }
1111 /**
1112  * Change node type, and its handles accordingly.
1113  */
1114 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1116     g_assert(node);
1117     g_assert(node->subpath);
1119     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1120         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1121             type =Inkscape::NodePath::NODE_CUSP;
1122         }
1123     }
1125     node->type = type;
1127     sp_nodepath_update_node_knot(node);
1129     // if one of handles is mouseovered, preserve its position
1130     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1131         sp_node_adjust_handle(node, 1);
1132     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1133         sp_node_adjust_handle(node, -1);
1134     } else {
1135         sp_node_adjust_handles(node);
1136     }
1138     sp_node_update_handles(node);
1140     sp_nodepath_update_statusbar(node->subpath->nodepath);
1142     return node;
1145 bool
1146 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1148 // TODO clean up multiple returns
1149         Inkscape::NodePath::Node *othernode = side->other;
1150         if (!othernode)
1151             return false;
1152         NRPathcode const code = sp_node_path_code_from_side(node, side);
1153         if (code == NR_LINETO)
1154             return true;
1155         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1156         if (&node->p == side) {
1157             other_to_me = &othernode->n;
1158         } else if (&node->n == side) {
1159             other_to_me = &othernode->p;
1160         }
1161         if (!other_to_me)
1162             return false;
1163         bool is_line =
1164              (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1165               Geom::L2(node->pos - side->pos) < 1e-6);
1166         return is_line;
1169 /**
1170  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1171  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1172  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too.
1173  * If already cusp and set to cusp, retracts handles.
1174 */
1175 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1177     if (type == Inkscape::NodePath::NODE_AUTO) {
1178         if (node->p.other != NULL)
1179             node->code = NR_CURVETO;
1180         if (node->n.other != NULL)
1181             node->n.other->code = NR_CURVETO;
1182     }
1184     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1186 /*
1187   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1189         if (two_handles) {
1190             // do nothing, adjust_handles called via set_node_type will line them up
1191         } else if (one_handle) {
1192             if (opposite_to_handle_is_line) {
1193                 if (lined_up) {
1194                     // already half-smooth; pull opposite handle too making it fully smooth
1195                 } else {
1196                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1197                 }
1198             } else {
1199                 // pull opposite handle in line with the existing one
1200             }
1201         } else if (no_handles) {
1202             if (both_segments_are_lines OR both_segments_are_curves) {
1203                 //pull both handles
1204             } else {
1205                 // pull the handle opposite to line segment, making node half-smooth
1206             }
1207         }
1208 */
1209         bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
1210         bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
1211         bool p_is_line = sp_node_side_is_line(node, &node->p);
1212         bool n_is_line = sp_node_side_is_line(node, &node->n);
1214         if (p_has_handle && n_has_handle) {
1215             // do nothing, adjust_handles will line them up
1216         } else if (p_has_handle || n_has_handle) {
1217             if (p_has_handle && n_is_line) {
1218                 Radial line (node->n.other->pos - node->pos);
1219                 Radial handle (node->pos - node->p.pos);
1220                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1221                     // already half-smooth; pull opposite handle too making it fully smooth
1222                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1223                 } else {
1224                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1225                 }
1226             } else if (n_has_handle && p_is_line) {
1227                 Radial line (node->p.other->pos - node->pos);
1228                 Radial handle (node->pos - node->n.pos);
1229                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1230                     // already half-smooth; pull opposite handle too making it fully smooth
1231                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1232                 } else {
1233                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1234                 }
1235             } else if (p_has_handle && node->n.other) {
1236                 // pull n handle
1237                 node->n.other->code = NR_CURVETO;
1238                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1239                     Geom::L2(node->p.pos - node->pos) :
1240                     Geom::L2(node->n.other->pos - node->pos) / 3;
1241                 node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1242             } else if (n_has_handle && node->p.other) {
1243                 // pull p handle
1244                 node->code = NR_CURVETO;
1245                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1246                     Geom::L2(node->n.pos - node->pos) :
1247                     Geom::L2(node->p.other->pos - node->pos) / 3;
1248                 node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1249             }
1250         } else if (!p_has_handle && !n_has_handle) {
1251             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1252                 // no handles, but both segments are either lnes or curves:
1253                 //pull both handles
1255                 // convert both to curves:
1256                 node->code = NR_CURVETO;
1257                 node->n.other->code = NR_CURVETO;
1259                 sp_node_adjust_handles_auto(node);
1260             } else {
1261                 // pull the handle opposite to line segment, making it half-smooth
1262                 if (p_is_line && node->n.other) {
1263                     if (type != Inkscape::NodePath::NODE_SYMM) {
1264                         // pull n handle
1265                         node->n.other->code = NR_CURVETO;
1266                         double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
1267                         node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1268                     }
1269                 } else if (n_is_line && node->p.other) {
1270                     if (type != Inkscape::NodePath::NODE_SYMM) {
1271                         // pull p handle
1272                         node->code = NR_CURVETO;
1273                         double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
1274                         node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1275                     }
1276                 }
1277             }
1278         }
1279     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1280         // cusping a cusp: retract nodes
1281         node->p.pos = node->pos;
1282         node->n.pos = node->pos;
1283     }
1285     sp_nodepath_set_node_type (node, type);
1288 /**
1289  * Move node to point, and adjust its and neighbouring handles.
1290  */
1291 void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
1293     if (node->type == Inkscape::NodePath::NODE_AUTO) {
1294         node->pos = p;
1295         sp_node_adjust_handles_auto(node);
1296     } else {
1297         Geom::Point delta = p - node->pos;
1298         node->pos = p;
1300         node->p.pos += delta;
1301         node->n.pos += delta;
1302     }
1304     Inkscape::NodePath::Node *node_p = NULL;
1305     Inkscape::NodePath::Node *node_n = NULL;
1307     if (node->p.other) {
1308         if (node->code == NR_LINETO) {
1309             sp_node_adjust_handle(node, 1);
1310             sp_node_adjust_handle(node->p.other, -1);
1311             node_p = node->p.other;
1312         }
1313         if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
1314             sp_node_adjust_handles_auto(node->p.other);
1315             node_p = node->p.other;
1316         }
1317     }
1318     if (node->n.other) {
1319         if (node->n.other->code == NR_LINETO) {
1320             sp_node_adjust_handle(node, -1);
1321             sp_node_adjust_handle(node->n.other, 1);
1322             node_n = node->n.other;
1323         }
1324         if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
1325             sp_node_adjust_handles_auto(node->n.other);
1326             node_n = node->n.other;
1327         }
1328     }
1330     // this function is only called from batch movers that will update display at the end
1331     // themselves, so here we just move all the knots without emitting move signals, for speed
1332     sp_node_update_handles(node, false);
1333     if (node_n) {
1334         sp_node_update_handles(node_n, false);
1335     }
1336     if (node_p) {
1337         sp_node_update_handles(node_p, false);
1338     }
1341 /**
1342  * Call sp_node_moveto() for node selection and handle possible snapping.
1343  */
1344 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
1345                                             bool const snap, bool constrained = false,
1346                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1348     Geom::Point delta(dx, dy);
1349     Geom::Point best_pt = delta;
1350     Inkscape::SnappedPoint best;
1352     if (snap) {
1353         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1354          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we
1355          * must provide that information. */
1357         // Build a list of the unselected nodes to which the snapper should snap
1358         std::vector<std::pair<Geom::Point, int> > unselected_nodes;
1359         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1360             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1361             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1362                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1363                 if (!node->selected) {
1364                     unselected_nodes.push_back(std::make_pair(to_2geom(node->pos), node->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPTARGET_NODE_SMOOTH : Inkscape::SNAPTARGET_NODE_CUSP));
1365                 }
1366             }
1367         }
1369         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1371         // When only the node closest to the mouse pointer is to be snapped
1372         // then we will not even try to snap to other points and discard those immediately
1373         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1374         bool closest_only = prefs->getBool("/options/snapclosestonly/value", false);
1376         Inkscape::NodePath::Node *closest_node = NULL;
1377         Geom::Coord closest_dist = NR_HUGE;
1379         if (closest_only) {
1380                 for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1381                         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1382                         Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
1383                         if (dist < closest_dist) {
1384                                 closest_node = n;
1385                                 closest_dist = dist;
1386                         }
1387                 }
1388         }
1390         // Iterate through all selected nodes
1391         m.setup(nodepath->desktop, false, nodepath->item, &unselected_nodes);
1392         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1393             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1394             if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
1395                     Inkscape::SnappedPoint s;
1396                     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
1397                     if (constrained) {
1398                         Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1399                         dedicated_constraint.setPoint(n->pos);
1400                         s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint, false);
1401                     } else {
1402                         s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type);
1403                     }
1405                     if (s.getSnapped()) {
1406                         s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));
1407                         if (!s.isOtherSnapBetter(best, true)) {
1408                                 best = s;
1409                                 best_pt = from_2geom(s.getPoint()) - n->pos;
1410                         }
1411                     }
1412             }
1413         }
1415         if (best.getSnapped()) {
1416             nodepath->desktop->snapindicator->set_new_snaptarget(best);
1417         } else {
1418             nodepath->desktop->snapindicator->remove_snaptarget();
1419         }
1420     }
1422     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1423         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1424         sp_node_moveto(n, n->pos + best_pt);
1425     }
1427     // do not update repr here so that node dragging is acceptably fast
1428     update_object(nodepath);
1431 /**
1432 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1433 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1434 near x = 0.
1435  */
1436 double
1437 sculpt_profile (double x, double alpha, guint profile)
1439     double result = 1;
1441     if (x >= 1) {
1442         result = 0;
1443     } else if (x <= 0) {
1444         result = 1;
1445     } else {
1446         switch (profile) {
1447             case SCULPT_PROFILE_LINEAR:
1448                 result = 1 - x;
1449                 break;
1450             case SCULPT_PROFILE_BELL:
1451                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1452                 break;
1453             case SCULPT_PROFILE_ELLIPTIC:
1454                 result = sqrt(1 - x*x);
1455                 break;
1456             default:
1457                 g_assert_not_reached();
1458         }
1459     }
1461     return result;
1464 double
1465 bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
1467     // extremely primitive for now, don't have time to look for the real one
1468     double lower = Geom::L2(b - a);
1469     double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
1470     return (lower + upper)/2;
1473 void
1474 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
1476     n->pos = n->origin + delta;
1477     n->n.pos = n->n.origin + delta_n;
1478     n->p.pos = n->p.origin + delta_p;
1479     sp_node_adjust_handles(n);
1480     sp_node_update_handles(n, false);
1483 /**
1484  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1485  * on how far they are from the dragged node n.
1486  */
1487 static void
1488 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
1490     g_assert (n);
1491     g_assert (nodepath);
1492     g_assert (n->subpath->nodepath == nodepath);
1494     double pressure = n->knot->pressure;
1495     if (pressure == 0)
1496         pressure = 0.5; // default
1497     pressure = CLAMP (pressure, 0.2, 0.8);
1499     // map pressure to alpha = 1/5 ... 5
1500     double alpha = 1 - 2 * fabs(pressure - 0.5);
1501     if (pressure > 0.5)
1502         alpha = 1/alpha;
1504     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1505     guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
1507     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1508         // Only one subpath has selected nodes:
1509         // use linear mode, where the distance from n to node being dragged is calculated along the path
1511         double n_sel_range = 0, p_sel_range = 0;
1512         guint n_nodes = 0, p_nodes = 0;
1513         guint n_sel_nodes = 0, p_sel_nodes = 0;
1515         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1516         {
1517             double n_range = 0, p_range = 0;
1518             bool n_going = true, p_going = true;
1519             Inkscape::NodePath::Node *n_node = n;
1520             Inkscape::NodePath::Node *p_node = n;
1521             do {
1522                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1523                 if (n_node && n_going)
1524                     n_node = n_node->n.other;
1525                 if (n_node == NULL) {
1526                     n_going = false;
1527                 } else {
1528                     n_nodes ++;
1529                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1530                     if (n_node->selected) {
1531                         n_sel_nodes ++;
1532                         n_sel_range = n_range;
1533                     }
1534                     if (n_node == p_node) {
1535                         n_going = false;
1536                         p_going = false;
1537                     }
1538                 }
1539                 if (p_node && p_going)
1540                     p_node = p_node->p.other;
1541                 if (p_node == NULL) {
1542                     p_going = false;
1543                 } else {
1544                     p_nodes ++;
1545                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1546                     if (p_node->selected) {
1547                         p_sel_nodes ++;
1548                         p_sel_range = p_range;
1549                     }
1550                     if (p_node == n_node) {
1551                         n_going = false;
1552                         p_going = false;
1553                     }
1554                 }
1555             } while (n_going || p_going);
1556         }
1558         // Second pass: actually move nodes in this subpath
1559         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1560         {
1561             double n_range = 0, p_range = 0;
1562             bool n_going = true, p_going = true;
1563             Inkscape::NodePath::Node *n_node = n;
1564             Inkscape::NodePath::Node *p_node = n;
1565             do {
1566                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1567                 if (n_node && n_going)
1568                     n_node = n_node->n.other;
1569                 if (n_node == NULL) {
1570                     n_going = false;
1571                 } else {
1572                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1573                     if (n_node->selected) {
1574                         sp_nodepath_move_node_and_handles (n_node,
1575                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1576                                                            sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1577                                                            sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1578                     }
1579                     if (n_node == p_node) {
1580                         n_going = false;
1581                         p_going = false;
1582                     }
1583                 }
1584                 if (p_node && p_going)
1585                     p_node = p_node->p.other;
1586                 if (p_node == NULL) {
1587                     p_going = false;
1588                 } else {
1589                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1590                     if (p_node->selected) {
1591                         sp_nodepath_move_node_and_handles (p_node,
1592                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1593                                                            sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1594                                                            sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1595                     }
1596                     if (p_node == n_node) {
1597                         n_going = false;
1598                         p_going = false;
1599                     }
1600                 }
1601             } while (n_going || p_going);
1602         }
1604     } else {
1605         // Multiple subpaths have selected nodes:
1606         // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
1607         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1608         // fix the pear-like shape when sculpting e.g. a ring
1610         // First pass: calculate range
1611         gdouble direct_range = 0;
1612         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1613             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1614             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1615                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1616                 if (node->selected) {
1617                     direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
1618                 }
1619             }
1620         }
1622         // Second pass: actually move nodes
1623         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1624             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1625             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1626                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1627                 if (node->selected) {
1628                     if (direct_range > 1e-6) {
1629                         sp_nodepath_move_node_and_handles (node,
1630                                                        sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1631                                                        sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1632                                                        sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1633                     } else {
1634                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1635                     }
1637                 }
1638             }
1639         }
1640     }
1642     // do not update repr here so that node dragging is acceptably fast
1643     update_object(nodepath);
1647 /**
1648  * Move node selection to point, adjust its and neighbouring handles,
1649  * handle possible snapping, and commit the change with possible undo.
1650  */
1651 void
1652 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1654     if (!nodepath) return;
1656     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1658     if (dx == 0) {
1659         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1660     } else if (dy == 0) {
1661         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1662     } else {
1663         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1664     }
1667 /**
1668  * Move node selection off screen and commit the change.
1669  */
1670 void
1671 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1673     // borrowed from sp_selection_move_screen in selection-chemistry.c
1674     // we find out the current zoom factor and divide deltas by it
1676     gdouble zoom = desktop->current_zoom();
1677     gdouble zdx = dx / zoom;
1678     gdouble zdy = dy / zoom;
1680     if (!nodepath) return;
1682     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1684     if (dx == 0) {
1685         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1686     } else if (dy == 0) {
1687         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1688     } else {
1689         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1690     }
1693 /**
1694  * Move selected nodes to the absolute position given
1695  */
1696 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1698     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1699         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1700         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1701         sp_node_moveto(n, npos);
1702     }
1704     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1707 /**
1708  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
1709  */
1710 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1712     boost::optional<Geom::Coord> no_coord;
1713     g_return_val_if_fail(nodepath->selected, no_coord);
1715     // determine coordinate of first selected node
1716     GList *nsel = nodepath->selected;
1717     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1718     Geom::Coord coord = n->pos[axis];
1719     bool coincide = true;
1721     // compare it to the coordinates of all the other selected nodes
1722     for (GList *l = nsel->next; l != NULL; l = l->next) {
1723         n = (Inkscape::NodePath::Node *) l->data;
1724         if (n->pos[axis] != coord) {
1725             coincide = false;
1726         }
1727     }
1728     if (coincide) {
1729         return coord;
1730     } else {
1731         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1732         // currently we return the coordinate of the bounding box midpoint because I don't know how
1733         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1734         return bbox.midpoint()[axis];
1735     }
1738 /** If they don't yet exist, creates knot and line for the given side of the node */
1739 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1741     if (!side->knot) {
1742         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"));
1744         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1745         side->knot->setSize (7);
1746         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1747         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1748         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1749         sp_knot_update_ctrl(side->knot);
1751         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1752         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1753         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1754         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1755         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1756         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1757     }
1759     if (!side->line) {
1760         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1761                                         SP_TYPE_CTRLLINE, NULL);
1762     }
1765 /**
1766  * Ensure the given handle of the node is visible/invisible, update its screen position
1767  */
1768 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1770     g_assert(node != NULL);
1772    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1773     NRPathcode code = sp_node_path_code_from_side(node, side);
1775     show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
1777     if (show_handle) {
1778         if (!side->knot) { // No handle knot at all
1779             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1780             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1781             side->knot->pos = side->pos;
1782             if (side->knot->item)
1783                 SP_CTRL(side->knot->item)->moveto(side->pos);
1784             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1785             sp_knot_show(side->knot);
1786         } else {
1787             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1788                 if (fire_move_signals) {
1789                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1790                 } else {
1791                     sp_knot_moveto(side->knot, side->pos);
1792                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1793                 }
1794             }
1795             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1796                 sp_knot_show(side->knot);
1797             }
1798         }
1799         sp_canvas_item_show(side->line);
1800     } else {
1801         if (side->knot) {
1802             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1803                 sp_knot_hide(side->knot);
1804             }
1805         }
1806         if (side->line) {
1807             sp_canvas_item_hide(side->line);
1808         }
1809     }
1812 /**
1813  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1814  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1815  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1816  * updated; otherwise, just move the knots silently (used in batch moves).
1817  */
1818 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1820     g_assert(node != NULL);
1822     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1823         sp_knot_show(node->knot);
1824     }
1826     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1827         if (fire_move_signals)
1828             sp_knot_set_position(node->knot, node->pos, 0);
1829         else
1830             sp_knot_moveto(node->knot, node->pos);
1831     }
1833     gboolean show_handles = node->selected;
1834     if (node->p.other != NULL) {
1835         if (node->p.other->selected) show_handles = TRUE;
1836     }
1837     if (node->n.other != NULL) {
1838         if (node->n.other->selected) show_handles = TRUE;
1839     }
1841     if (node->subpath->nodepath->show_handles == false)
1842         show_handles = FALSE;
1844     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1845     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1848 /**
1849  * Call sp_node_update_handles() for all nodes on subpath.
1850  */
1851 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1853     g_assert(subpath != NULL);
1855     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1856         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1857     }
1860 /**
1861  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1862  */
1863 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1865     g_assert(nodepath != NULL);
1867     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1868         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1869     }
1872 void
1873 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1875     if (nodepath) {
1876         nodepath->show_handles = show;
1877         sp_nodepath_update_handles(nodepath);
1878     }
1881 /**
1882  * Adds all selected nodes in nodepath to list.
1883  */
1884 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1886     StlConv<Node *>::list(l, selected);
1887 /// \todo this adds a copying, rework when the selection becomes a stl list
1890 /**
1891  * Align selected nodes on the specified axis.
1892  */
1893 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1895     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1896         return;
1897     }
1899     if ( !nodepath->selected->next ) { // only one node selected
1900         return;
1901     }
1902    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1903     Geom::Point dest(pNode->pos);
1904     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1905         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1906         if (pNode) {
1907             dest[axis] = pNode->pos[axis];
1908             sp_node_moveto(pNode, dest);
1909         }
1910     }
1912     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1915 /// Helper struct.
1916 struct NodeSort
1918    Inkscape::NodePath::Node *_node;
1919     Geom::Coord _coord;
1920     /// \todo use vectorof pointers instead of calling copy ctor
1921     NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
1922         _node(node), _coord(node->pos[axis])
1923     {}
1925 };
1927 static bool operator<(NodeSort const &a, NodeSort const &b)
1929     return (a._coord < b._coord);
1932 /**
1933  * Distribute selected nodes on the specified axis.
1934  */
1935 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1937     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1938         return;
1939     }
1941     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1942         return;
1943     }
1945    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1946     std::vector<NodeSort> sorted;
1947     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1948         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1949         if (pNode) {
1950             NodeSort n(pNode, axis);
1951             sorted.push_back(n);
1952             //dest[axis] = pNode->pos[axis];
1953             //sp_node_moveto(pNode, dest);
1954         }
1955     }
1956     std::sort(sorted.begin(), sorted.end());
1957     unsigned int len = sorted.size();
1958     //overall bboxes span
1959     float dist = (sorted.back()._coord -
1960                   sorted.front()._coord);
1961     //new distance between each bbox
1962     float step = (dist) / (len - 1);
1963     float pos = sorted.front()._coord;
1964     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1965           it < sorted.end();
1966           it ++ )
1967     {
1968         Geom::Point dest((*it)._node->pos);
1969         dest[axis] = pos;
1970         sp_node_moveto((*it)._node, dest);
1971         pos += step;
1972     }
1974     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1978 /**
1979  * Call sp_nodepath_line_add_node() for all selected segments.
1980  */
1981 void
1982 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1984     if (!nodepath) {
1985         return;
1986     }
1988     GList *nl = NULL;
1990     int n_added = 0;
1992     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1993        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1994         g_assert(t->selected);
1995         if (t->p.other && t->p.other->selected) {
1996             nl = g_list_prepend(nl, t);
1997         }
1998     }
2000     while (nl) {
2001        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
2002        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
2003        sp_nodepath_node_select(n, TRUE, FALSE);
2004        n_added ++;
2005        nl = g_list_remove(nl, t);
2006     }
2008     /** \todo fixme: adjust ? */
2009     sp_nodepath_update_handles(nodepath);
2011     if (n_added > 1) {
2012         sp_nodepath_update_repr(nodepath, _("Add nodes"));
2013     } else if (n_added > 0) {
2014         sp_nodepath_update_repr(nodepath, _("Add node"));
2015     }
2017     sp_nodepath_update_statusbar(nodepath);
2020 /**
2021  * Select segment nearest to point
2022  */
2023 void
2024 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
2026     if (!nodepath) {
2027         return;
2028     }
2030     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2031     Geom::PathVector const &pathv = curve->get_pathvector();
2032     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2033     if (!pvpos) {
2034         g_print ("Possible error?\n");
2035         return;
2036     }
2038     // calculate index for nodepath's representation.
2039     unsigned int segment_index = floor(pvpos->t) + 1;
2040     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2041         segment_index += pathv[i].size() + 1;
2042         if (pathv[i].closed()) {
2043             segment_index += 1;
2044         }
2045     }
2047     curve->unref();
2049     //find segment to segment
2050     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2052     //fixme: this can return NULL, so check before proceeding.
2053     g_return_if_fail(e != NULL);
2055     gboolean force = FALSE;
2056     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
2057         force = TRUE;
2058     }
2059     sp_nodepath_node_select(e, (gboolean) toggle, force);
2060     if (e->p.other)
2061         sp_nodepath_node_select(e->p.other, TRUE, force);
2063     sp_nodepath_update_handles(nodepath);
2065     sp_nodepath_update_statusbar(nodepath);
2068 /**
2069  * Add a node nearest to point
2070  */
2071 void
2072 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
2074     if (!nodepath) {
2075         return;
2076     }
2078     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2079     Geom::PathVector const &pathv = curve->get_pathvector();
2080     boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
2081     if (!pvpos) {
2082         g_print ("Possible error?\n");
2083         return;
2084     }
2086     // calculate index for nodepath's representation.
2087     double int_part;
2088     double t = std::modf(pvpos->t, &int_part);
2089     unsigned int segment_index = (unsigned int)int_part + 1;
2090     for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
2091         segment_index += pathv[i].size() + 1;
2092         if (pathv[i].closed()) {
2093             segment_index += 1;
2094         }
2095     }
2097     curve->unref();
2099     //find segment to split
2100     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2101     if (!e) {
2102         return;
2103     }
2105     //don't know why but t seems to flip for lines
2106     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2107         t = 1.0 - t;
2108     }
2110     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2111     sp_nodepath_node_select(n, FALSE, TRUE);
2113     /* fixme: adjust ? */
2114     sp_nodepath_update_handles(nodepath);
2116     sp_nodepath_update_repr(nodepath, _("Add node"));
2118     sp_nodepath_update_statusbar(nodepath);
2121 /*
2122  * Adjusts a segment so that t moves by a certain delta for dragging
2123  * converts lines to curves
2124  *
2125  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2126  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2127  */
2128 void
2129 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
2131     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2133     //fixme: e and e->p can be NULL, so check for those before proceeding
2134     g_return_if_fail(e != NULL);
2135     g_return_if_fail(&e->p != NULL);
2137     if (e->type == Inkscape::NodePath::NODE_AUTO) {
2138         e->type = Inkscape::NodePath::NODE_SMOOTH;
2139         sp_nodepath_update_node_knot (e);
2140     }
2141     if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
2142         e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
2143         sp_nodepath_update_node_knot (e->p.other);
2144     }
2146     /* feel good is an arbitrary parameter that distributes the delta between handles
2147      * if t of the drag point is less than 1/6 distance form the endpoint only
2148      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2149      */
2150     double feel_good;
2151     if (t <= 1.0 / 6.0)
2152         feel_good = 0;
2153     else if (t <= 0.5)
2154         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2155     else if (t <= 5.0 / 6.0)
2156         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2157     else
2158         feel_good = 1;
2160     //if we're dragging a line convert it to a curve
2161     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2162         sp_nodepath_set_line_type(e, NR_CURVETO);
2163     }
2165     Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2166     Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2167     e->p.other->n.pos += offsetcoord0;
2168     e->p.pos += offsetcoord1;
2170     // adjust handles of adjacent nodes where necessary
2171     sp_node_adjust_handle(e,1);
2172     sp_node_adjust_handle(e->p.other,-1);
2174     sp_nodepath_update_handles(e->subpath->nodepath);
2176     update_object(e->subpath->nodepath);
2178     sp_nodepath_update_statusbar(e->subpath->nodepath);
2182 /**
2183  * Call sp_nodepath_break() for all selected segments.
2184  */
2185 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2187     if (!nodepath) return;
2189     GList *tempin = g_list_copy(nodepath->selected);
2190     GList *temp = NULL;
2191     for (GList *l = tempin; l != NULL; l = l->next) {
2192        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2193        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2194         if (nn == NULL) continue; // no break, no new node
2195         temp = g_list_prepend(temp, nn);
2196     }
2197     g_list_free(tempin);
2199     if (temp) {
2200         sp_nodepath_deselect(nodepath);
2201     }
2202     for (GList *l = temp; l != NULL; l = l->next) {
2203         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2204     }
2206     sp_nodepath_update_handles(nodepath);
2208     sp_nodepath_update_repr(nodepath, _("Break path"));
2211 /**
2212  * Duplicate the selected node(s).
2213  */
2214 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2216     if (!nodepath) {
2217         return;
2218     }
2220     GList *temp = NULL;
2221     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2222        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2223        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2224         if (nn == NULL) continue; // could not duplicate
2225         temp = g_list_prepend(temp, nn);
2226     }
2228     if (temp) {
2229         sp_nodepath_deselect(nodepath);
2230     }
2231     for (GList *l = temp; l != NULL; l = l->next) {
2232         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2233     }
2235     sp_nodepath_update_handles(nodepath);
2237     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2240 /**
2241  *  Internal function to join two nodes by merging them into one.
2242  */
2243 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2245     /* a and b are endpoints */
2247     // if one of the two nodes is mouseovered, fix its position
2248     Geom::Point c;
2249     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2250         c = a->pos;
2251     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2252         c = b->pos;
2253     } else {
2254         // otherwise, move joined node to the midpoint
2255         c = (a->pos + b->pos) / 2;
2256     }
2258     if (a->subpath == b->subpath) {
2259        Inkscape::NodePath::SubPath *sp = a->subpath;
2260         sp_nodepath_subpath_close(sp);
2261         sp_node_moveto (sp->first, c);
2263         sp_nodepath_update_handles(sp->nodepath);
2264         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2265         return;
2266     }
2268     /* a and b are separate subpaths */
2269     Inkscape::NodePath::SubPath *sa = a->subpath;
2270     Inkscape::NodePath::SubPath *sb = b->subpath;
2271     Geom::Point p;
2272     Inkscape::NodePath::Node *n;
2273     NRPathcode code;
2274     if (a == sa->first) {
2275         // we will now reverse sa, so that a is its last node, not first, and drop that node
2276         p = sa->first->n.pos;
2277         code = (NRPathcode)sa->first->n.other->code;
2278         // create new subpath
2279        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2280        // create a first moveto node on it
2281         n = sa->last;
2282         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2283         n = n->p.other;
2284         if (n == sa->first) n = NULL;
2285         while (n) {
2286             // copy the rest of the nodes from sa to t, going backwards
2287             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2288             n = n->p.other;
2289             if (n == sa->first) n = NULL;
2290         }
2291         // replace sa with t
2292         sp_nodepath_subpath_destroy(sa);
2293         sa = t;
2294     } else if (a == sa->last) {
2295         // a is already last, just drop it
2296         p = sa->last->p.pos;
2297         code = (NRPathcode)sa->last->code;
2298         sp_nodepath_node_destroy(sa->last);
2299     } else {
2300         code = NR_END;
2301         g_assert_not_reached();
2302     }
2304     if (b == sb->first) {
2305         // copy all nodes from b to a, forward
2306         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2307         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2308             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2309         }
2310     } else if (b == sb->last) {
2311         // copy all nodes from b to a, backward
2312         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2313         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2314             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2315         }
2316     } else {
2317         g_assert_not_reached();
2318     }
2319     /* and now destroy sb */
2321     sp_nodepath_subpath_destroy(sb);
2323     sp_nodepath_update_handles(sa->nodepath);
2325     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2327     sp_nodepath_update_statusbar(nodepath);
2330 /**
2331  *  Internal function to join two nodes by adding a segment between them.
2332  */
2333 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2335     if (a->subpath == b->subpath) {
2336        Inkscape::NodePath::SubPath *sp = a->subpath;
2338         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2339         sp->closed = TRUE;
2341         sp->first->p.other = sp->last;
2342         sp->last->n.other  = sp->first;
2344         sp_node_handle_mirror_p_to_n(sp->last);
2345         sp_node_handle_mirror_n_to_p(sp->first);
2347         sp->first->code = sp->last->code;
2348         sp->first       = sp->last;
2350         sp_nodepath_update_handles(sp->nodepath);
2352         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2354         return;
2355     }
2357     /* a and b are separate subpaths */
2358     Inkscape::NodePath::SubPath *sa = a->subpath;
2359     Inkscape::NodePath::SubPath *sb = b->subpath;
2361     Inkscape::NodePath::Node *n;
2362     Geom::Point p;
2363     NRPathcode code;
2364     if (a == sa->first) {
2365         code = (NRPathcode) sa->first->n.other->code;
2366        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2367         n = sa->last;
2368         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2369         for (n = n->p.other; n != NULL; n = n->p.other) {
2370             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2371         }
2372         sp_nodepath_subpath_destroy(sa);
2373         sa = t;
2374     } else if (a == sa->last) {
2375         code = (NRPathcode)sa->last->code;
2376     } else {
2377         code = NR_END;
2378         g_assert_not_reached();
2379     }
2381     if (b == sb->first) {
2382         n = sb->first;
2383         sp_node_handle_mirror_p_to_n(sa->last);
2384         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2385         sp_node_handle_mirror_n_to_p(sa->last);
2386         for (n = n->n.other; n != NULL; n = n->n.other) {
2387             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2388         }
2389     } else if (b == sb->last) {
2390         n = sb->last;
2391         sp_node_handle_mirror_p_to_n(sa->last);
2392         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2393         sp_node_handle_mirror_n_to_p(sa->last);
2394         for (n = n->p.other; n != NULL; n = n->p.other) {
2395             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2396         }
2397     } else {
2398         g_assert_not_reached();
2399     }
2400     /* and now destroy sb */
2402     sp_nodepath_subpath_destroy(sb);
2404     sp_nodepath_update_handles(sa->nodepath);
2406     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2409 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2411 /**
2412  * Internal function to handle joining two nodes.
2413  */
2414 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2416     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2418     if (g_list_length(nodepath->selected) != 2) {
2419         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2420         return;
2421     }
2423     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2424     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2426     g_assert(a != b);
2427     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2428         // someone tried to join an orphan node (i.e. a single-node subpath).
2429         // this is not worth an error message, just fail silently.
2430         return;
2431     }
2433     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2434         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2435         return;
2436     }
2438     switch(mode) {
2439         case NODE_JOIN_ENDPOINTS:
2440             do_node_selected_join(nodepath, a, b);
2441             break;
2442         case NODE_JOIN_SEGMENT:
2443             do_node_selected_join_segment(nodepath, a, b);
2444             break;
2445     }
2448 /**
2449  *  Join two nodes by merging them into one.
2450  */
2451 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2453     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2456 /**
2457  *  Join two nodes by adding a segment between them.
2458  */
2459 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2461     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2464 /**
2465  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2466  */
2467 void sp_node_delete_preserve(GList *nodes_to_delete)
2469     GSList *nodepaths = NULL;
2471     while (nodes_to_delete) {
2472         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2473         Inkscape::NodePath::SubPath *sp = node->subpath;
2474         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2475         Inkscape::NodePath::Node *sample_cursor = NULL;
2476         Inkscape::NodePath::Node *sample_end = NULL;
2477         Inkscape::NodePath::Node *delete_cursor = node;
2478         bool just_delete = false;
2480         //find the start of this contiguous selection
2481         //move left to the first node that is not selected
2482         //or the start of the non-closed path
2483         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2484             delete_cursor = curr;
2485         }
2487         //just delete at the beginning of an open path
2488         if (!delete_cursor->p.other) {
2489             sample_cursor = delete_cursor;
2490             just_delete = true;
2491         } else {
2492             sample_cursor = delete_cursor->p.other;
2493         }
2495         //calculate points for each segment
2496         int rate = 5;
2497         float period = 1.0 / rate;
2498         std::vector<Geom::Point> data;
2499         if (!just_delete) {
2500             data.push_back(sample_cursor->pos);
2501             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2502                 //just delete at the end of an open path
2503                 if (!sp->closed && curr == sp->last) {
2504                     just_delete = true;
2505                     break;
2506                 }
2508                 //sample points on the contiguous selected segment
2509                 Geom::Point *bez;
2510                 bez = new Geom::Point [4];
2511                 bez[0] = curr->pos;
2512                 bez[1] = curr->n.pos;
2513                 bez[2] = curr->n.other->p.pos;
2514                 bez[3] = curr->n.other->pos;
2515                 for (int i=1; i<rate; i++) {
2516                     gdouble t = i * period;
2517                     Geom::Point p = bezier_pt(3, bez, t);
2518                     data.push_back(p);
2519                 }
2520                 data.push_back(curr->n.other->pos);
2522                 sample_end = curr->n.other;
2523                 //break if we've come full circle or hit the end of the selection
2524                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2525                     break;
2526                 }
2527             }
2528         }
2530         if (!just_delete) {
2531             //calculate the best fitting single segment and adjust the endpoints
2532             Geom::Point *adata;
2533             adata = new Geom::Point [data.size()];
2534             copy(data.begin(), data.end(), adata);
2536             Geom::Point *bez;
2537             bez = new Geom::Point [4];
2538             //would decreasing error create a better fitting approximation?
2539             gdouble error = 1.0;
2540             gint ret;
2541             ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error);
2543             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2544             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2545             //the resulting nodes behave as expected.
2546             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2547                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2548             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2549                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2551             //adjust endpoints
2552             sample_cursor->n.pos = bez[1];
2553             sample_end->p.pos = bez[2];
2554         }
2556         //destroy this contiguous selection
2557         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2558             Inkscape::NodePath::Node *temp = delete_cursor;
2559             if (delete_cursor->n.other == delete_cursor) {
2560                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2561                 delete_cursor = NULL;
2562             } else {
2563                 delete_cursor = delete_cursor->n.other;
2564             }
2565             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2566             sp_nodepath_node_destroy(temp);
2567         }
2569         sp_nodepath_update_handles(nodepath);
2571         if (!g_slist_find(nodepaths, nodepath))
2572             nodepaths = g_slist_prepend (nodepaths, nodepath);
2573     }
2575     for (GSList *i = nodepaths; i; i = i->next) {
2576         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2577         // different nodepaths will give us one undo event per nodepath
2578         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2580         // if the entire nodepath is removed, delete the selected object.
2581         if (nodepath->subpaths == NULL ||
2582             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2583             //at least 2
2584             sp_nodepath_get_node_count(nodepath) < 2) {
2585             SPDocument *document = sp_desktop_document (nodepath->desktop);
2586             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2587             //delete this nodepath's object, not the entire selection! (though at this time, this
2588             //does not matter)
2589             sp_selection_delete(nodepath->desktop);
2590             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2591                               _("Delete nodes"));
2592         } else {
2593             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2594             sp_nodepath_update_statusbar(nodepath);
2595         }
2596     }
2598     g_slist_free (nodepaths);
2601 /**
2602  * Delete one or more selected nodes.
2603  */
2604 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2606     if (!nodepath) return;
2607     if (!nodepath->selected) return;
2609     /** \todo fixme: do it the right way */
2610     while (nodepath->selected) {
2611        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2612         sp_nodepath_node_destroy(node);
2613     }
2616     //clean up the nodepath (such as for trivial subpaths)
2617     sp_nodepath_cleanup(nodepath);
2619     sp_nodepath_update_handles(nodepath);
2621     // if the entire nodepath is removed, delete the selected object.
2622     if (nodepath->subpaths == NULL ||
2623         sp_nodepath_get_node_count(nodepath) < 2) {
2624         SPDocument *document = sp_desktop_document (nodepath->desktop);
2625         sp_selection_delete(nodepath->desktop);
2626         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2627                           _("Delete nodes"));
2628         return;
2629     }
2631     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2633     sp_nodepath_update_statusbar(nodepath);
2636 /**
2637  * Delete one or more segments between two selected nodes.
2638  * This is the code for 'split'.
2639  */
2640 void
2641 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2643    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2644    Inkscape::NodePath::Node *curr, *next;     //Iterators
2646     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2648     if (g_list_length(nodepath->selected) != 2) {
2649         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2650                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2651         return;
2652     }
2654     //Selected nodes, not inclusive
2655    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2656    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2658     if ( ( a==b)                       ||  //same node
2659          (a->subpath  != b->subpath )  ||  //not the same path
2660          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2661          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2662     {
2663         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2664                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2665         return;
2666     }
2668     //###########################################
2669     //# BEGIN EDITS
2670     //###########################################
2671     //##################################
2672     //# CLOSED PATH
2673     //##################################
2674     if (a->subpath->closed) {
2677         gboolean reversed = FALSE;
2679         //Since we can go in a circle, we need to find the shorter distance.
2680         //  a->b or b->a
2681         start = end = NULL;
2682         int distance    = 0;
2683         int minDistance = 0;
2684         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2685             if (curr==b) {
2686                 //printf("a to b:%d\n", distance);
2687                 start = a;//go from a to b
2688                 end   = b;
2689                 minDistance = distance;
2690                 //printf("A to B :\n");
2691                 break;
2692             }
2693             distance++;
2694         }
2696         //try again, the other direction
2697         distance = 0;
2698         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2699             if (curr==a) {
2700                 //printf("b to a:%d\n", distance);
2701                 if (distance < minDistance) {
2702                     start    = b;  //we go from b to a
2703                     end      = a;
2704                     reversed = TRUE;
2705                     //printf("B to A\n");
2706                 }
2707                 break;
2708             }
2709             distance++;
2710         }
2713         //Copy everything from 'end' to 'start' to a new subpath
2714        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2715         for (curr=end ; curr ; curr=curr->n.other) {
2716             NRPathcode code = (NRPathcode) curr->code;
2717             if (curr == end)
2718                 code = NR_MOVETO;
2719             sp_nodepath_node_new(t, NULL,
2720                                  (Inkscape::NodePath::NodeType)curr->type, code,
2721                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2722             if (curr == start)
2723                 break;
2724         }
2725         sp_nodepath_subpath_destroy(a->subpath);
2728     }
2732     //##################################
2733     //# OPEN PATH
2734     //##################################
2735     else {
2737         //We need to get the direction of the list between A and B
2738         //Can we walk from a to b?
2739         start = end = NULL;
2740         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2741             if (curr==b) {
2742                 start = a;  //did it!  we go from a to b
2743                 end   = b;
2744                 //printf("A to B\n");
2745                 break;
2746             }
2747         }
2748         if (!start) {//didn't work?  let's try the other direction
2749             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2750                 if (curr==a) {
2751                     start = b;  //did it!  we go from b to a
2752                     end   = a;
2753                     //printf("B to A\n");
2754                     break;
2755                 }
2756             }
2757         }
2758         if (!start) {
2759             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2760                                                      _("Cannot find path between nodes."));
2761             return;
2762         }
2766         //Copy everything after 'end' to a new subpath
2767        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2768         for (curr=end ; curr ; curr=curr->n.other) {
2769             NRPathcode code = (NRPathcode) curr->code;
2770             if (curr == end)
2771                 code = NR_MOVETO;
2772             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2773                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2774         }
2776         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2777         for (curr = start->n.other ; curr  ; curr=next) {
2778             next = curr->n.other;
2779             sp_nodepath_node_destroy(curr);
2780         }
2782     }
2783     //###########################################
2784     //# END EDITS
2785     //###########################################
2787     //clean up the nodepath (such as for trivial subpaths)
2788     sp_nodepath_cleanup(nodepath);
2790     sp_nodepath_update_handles(nodepath);
2792     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2794     sp_nodepath_update_statusbar(nodepath);
2797 /**
2798  * Call sp_nodepath_set_line() for all selected segments.
2799  */
2800 void
2801 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2803     if (nodepath == NULL) return;
2805     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2806        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2807         g_assert(n->selected);
2808         if (n->p.other && n->p.other->selected) {
2809             sp_nodepath_set_line_type(n, code);
2810         }
2811     }
2813     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2816 /**
2817  * Call sp_nodepath_convert_node_type() for all selected nodes.
2818  */
2819 void
2820 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2822     if (nodepath == NULL) return;
2824     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2826     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2827         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2828     }
2830     sp_nodepath_update_repr(nodepath, _("Change node type"));
2833 /**
2834  * Change select status of node, update its own and neighbour handles.
2835  */
2836 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2838     node->selected = selected;
2840     if (selected) {
2841         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
2842         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2843         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2844         sp_knot_update_ctrl(node->knot);
2845     } else {
2846         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
2847         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2848         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2849         sp_knot_update_ctrl(node->knot);
2850     }
2852     sp_node_update_handles(node);
2853     if (node->n.other) sp_node_update_handles(node->n.other);
2854     if (node->p.other) sp_node_update_handles(node->p.other);
2857 /**
2858 \brief Select a node
2859 \param node     The node to select
2860 \param incremental   If true, add to selection, otherwise deselect others
2861 \param override   If true, always select this node, otherwise toggle selected status
2862 */
2863 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2865     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2867     if (incremental) {
2868         if (override) {
2869             if (!g_list_find(nodepath->selected, node)) {
2870                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2871             }
2872             sp_node_set_selected(node, TRUE);
2873         } else { // toggle
2874             if (node->selected) {
2875                 g_assert(g_list_find(nodepath->selected, node));
2876                 nodepath->selected = g_list_remove(nodepath->selected, node);
2877             } else {
2878                 g_assert(!g_list_find(nodepath->selected, node));
2879                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2880             }
2881             sp_node_set_selected(node, !node->selected);
2882         }
2883     } else {
2884         sp_nodepath_deselect(nodepath);
2885         nodepath->selected = g_list_prepend(nodepath->selected, node);
2886         sp_node_set_selected(node, TRUE);
2887     }
2889     sp_nodepath_update_statusbar(nodepath);
2893 /**
2894 \brief Deselect all nodes in the nodepath
2895 */
2896 void
2897 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2899     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2901     while (nodepath->selected) {
2902         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2903         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2904     }
2905     sp_nodepath_update_statusbar(nodepath);
2908 /**
2909 \brief Select or invert selection of all nodes in the nodepath
2910 */
2911 void
2912 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2914     if (!nodepath) return;
2916     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2917        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2918         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2919            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2920            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2921         }
2922     }
2925 /**
2926  * If nothing selected, does the same as sp_nodepath_select_all();
2927  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2928  * (i.e., similar to "select all in layer", with the "selected" subpaths
2929  * being treated as "layers" in the path).
2930  */
2931 void
2932 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2934     if (!nodepath) return;
2936     if (g_list_length (nodepath->selected) == 0) {
2937         sp_nodepath_select_all (nodepath, invert);
2938         return;
2939     }
2941     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2942     GSList *subpaths = NULL;
2944     for (GList *l = copy; l != NULL; l = l->next) {
2945         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2946         Inkscape::NodePath::SubPath *subpath = n->subpath;
2947         if (!g_slist_find (subpaths, subpath))
2948             subpaths = g_slist_prepend (subpaths, subpath);
2949     }
2951     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2952         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2953         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2954             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2955             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2956         }
2957     }
2959     g_slist_free (subpaths);
2960     g_list_free (copy);
2963 /**
2964  * \brief Select the node after the last selected; if none is selected,
2965  * select the first within path.
2966  */
2967 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2969     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2971    Inkscape::NodePath::Node *last = NULL;
2972     if (nodepath->selected) {
2973         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2974            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2975             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2976             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2977                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2978                 if (node->selected) {
2979                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2980                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2981                             if (spl->next) { // there's a next subpath
2982                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2983                                 last = subpath_next->first;
2984                             } else if (spl->prev) { // there's a previous subpath
2985                                 last = NULL; // to be set later to the first node of first subpath
2986                             } else {
2987                                 last = node->n.other;
2988                             }
2989                         } else {
2990                             last = node->n.other;
2991                         }
2992                     } else {
2993                         if (node->n.other) {
2994                             last = node->n.other;
2995                         } else {
2996                             if (spl->next) { // there's a next subpath
2997                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2998                                 last = subpath_next->first;
2999                             } else if (spl->prev) { // there's a previous subpath
3000                                 last = NULL; // to be set later to the first node of first subpath
3001                             } else {
3002                                 last = (Inkscape::NodePath::Node *) subpath->first;
3003                             }
3004                         }
3005                     }
3006                 }
3007             }
3008         }
3009         sp_nodepath_deselect(nodepath);
3010     }
3012     if (last) { // there's at least one more node after selected
3013         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3014     } else { // no more nodes, select the first one in first subpath
3015        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
3016         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
3017     }
3020 /**
3021  * \brief Select the node before the first selected; if none is selected,
3022  * select the last within path
3023  */
3024 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
3026     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
3028    Inkscape::NodePath::Node *last = NULL;
3029     if (nodepath->selected) {
3030         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
3031            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3032             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
3033                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3034                 if (node->selected) {
3035                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
3036                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
3037                             if (spl->prev) { // there's a prev subpath
3038                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3039                                 last = subpath_prev->last;
3040                             } else if (spl->next) { // there's a next subpath
3041                                 last = NULL; // to be set later to the last node of last subpath
3042                             } else {
3043                                 last = node->p.other;
3044                             }
3045                         } else {
3046                             last = node->p.other;
3047                         }
3048                     } else {
3049                         if (node->p.other) {
3050                             last = node->p.other;
3051                         } else {
3052                             if (spl->prev) { // there's a prev subpath
3053                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
3054                                 last = subpath_prev->last;
3055                             } else if (spl->next) { // there's a next subpath
3056                                 last = NULL; // to be set later to the last node of last subpath
3057                             } else {
3058                                 last = (Inkscape::NodePath::Node *) subpath->last;
3059                             }
3060                         }
3061                     }
3062                 }
3063             }
3064         }
3065         sp_nodepath_deselect(nodepath);
3066     }
3068     if (last) { // there's at least one more node before selected
3069         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
3070     } else { // no more nodes, select the last one in last subpath
3071         GList *spl = g_list_last(nodepath->subpaths);
3072        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3073         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
3074     }
3077 /**
3078  * \brief Select all nodes that are within the rectangle.
3079  */
3080 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
3082     if (!incremental) {
3083         sp_nodepath_deselect(nodepath);
3084     }
3086     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3087        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3088         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3089            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3091             if (b.contains(node->pos)) {
3092                 sp_nodepath_node_select(node, TRUE, TRUE);
3093             }
3094         }
3095     }
3099 void
3100 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3102     g_assert (n);
3103     g_assert (nodepath);
3104     g_assert (n->subpath->nodepath == nodepath);
3106     if (g_list_length (nodepath->selected) == 0) {
3107         if (grow > 0) {
3108             sp_nodepath_node_select(n, TRUE, TRUE);
3109         }
3110         return;
3111     }
3113     if (g_list_length (nodepath->selected) == 1) {
3114         if (grow < 0) {
3115             sp_nodepath_deselect (nodepath);
3116             return;
3117         }
3118     }
3120         double n_sel_range = 0, p_sel_range = 0;
3121             Inkscape::NodePath::Node *farthest_n_node = n;
3122             Inkscape::NodePath::Node *farthest_p_node = n;
3124         // Calculate ranges
3125         {
3126             double n_range = 0, p_range = 0;
3127             bool n_going = true, p_going = true;
3128             Inkscape::NodePath::Node *n_node = n;
3129             Inkscape::NodePath::Node *p_node = n;
3130             do {
3131                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3132                 if (n_node && n_going)
3133                     n_node = n_node->n.other;
3134                 if (n_node == NULL) {
3135                     n_going = false;
3136                 } else {
3137                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3138                     if (n_node->selected) {
3139                         n_sel_range = n_range;
3140                         farthest_n_node = n_node;
3141                     }
3142                     if (n_node == p_node) {
3143                         n_going = false;
3144                         p_going = false;
3145                     }
3146                 }
3147                 if (p_node && p_going)
3148                     p_node = p_node->p.other;
3149                 if (p_node == NULL) {
3150                     p_going = false;
3151                 } else {
3152                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3153                     if (p_node->selected) {
3154                         p_sel_range = p_range;
3155                         farthest_p_node = p_node;
3156                     }
3157                     if (p_node == n_node) {
3158                         n_going = false;
3159                         p_going = false;
3160                     }
3161                 }
3162             } while (n_going || p_going);
3163         }
3165     if (grow > 0) {
3166         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3167                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3168         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3169                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3170         }
3171     } else {
3172         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3173                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3174         } else if (farthest_p_node && farthest_p_node->selected) {
3175                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3176         }
3177     }
3180 void
3181 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3183     g_assert (n);
3184     g_assert (nodepath);
3185     g_assert (n->subpath->nodepath == nodepath);
3187     if (g_list_length (nodepath->selected) == 0) {
3188         if (grow > 0) {
3189             sp_nodepath_node_select(n, TRUE, TRUE);
3190         }
3191         return;
3192     }
3194     if (g_list_length (nodepath->selected) == 1) {
3195         if (grow < 0) {
3196             sp_nodepath_deselect (nodepath);
3197             return;
3198         }
3199     }
3201     Inkscape::NodePath::Node *farthest_selected = NULL;
3202     double farthest_dist = 0;
3204     Inkscape::NodePath::Node *closest_unselected = NULL;
3205     double closest_dist = NR_HUGE;
3207     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3208        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3209         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3210            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3211            if (node == n)
3212                continue;
3213            if (node->selected) {
3214                if (Geom::L2(node->pos - n->pos) > farthest_dist) {
3215                    farthest_dist = Geom::L2(node->pos - n->pos);
3216                    farthest_selected = node;
3217                }
3218            } else {
3219                if (Geom::L2(node->pos - n->pos) < closest_dist) {
3220                    closest_dist = Geom::L2(node->pos - n->pos);
3221                    closest_unselected = node;
3222                }
3223            }
3224         }
3225     }
3227     if (grow > 0) {
3228         if (closest_unselected) {
3229             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3230         }
3231     } else {
3232         if (farthest_selected) {
3233             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3234         }
3235     }
3239 /**
3240 \brief  Saves all nodes' and handles' current positions in their origin members
3241 */
3242 void
3243 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3245     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3246        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3247         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3248            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3249            n->origin = n->pos;
3250            n->p.origin = n->p.pos;
3251            n->n.origin = n->n.pos;
3252         }
3253     }
3256 /**
3257 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3258 */
3259 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3261     GList *r = NULL;
3262     if (nodepath->selected) {
3263         guint i = 0;
3264         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3265             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3266             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3267                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3268                 i++;
3269                 if (node->selected) {
3270                     r = g_list_append(r, GINT_TO_POINTER(i));
3271                 }
3272             }
3273         }
3274     }
3275     return r;
3278 /**
3279 \brief  Restores selection by selecting nodes whose positions are in the list
3280 */
3281 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3283     sp_nodepath_deselect(nodepath);
3285     guint i = 0;
3286     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3287        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3288         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3289            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3290             i++;
3291             if (g_list_find(r, GINT_TO_POINTER(i))) {
3292                 sp_nodepath_node_select(node, TRUE, TRUE);
3293             }
3294         }
3295     }
3299 /**
3300 \brief Adjusts handle according to node type and line code.
3301 */
3302 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3304     g_assert(node);
3306     // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
3307     if (node->type == Inkscape::NodePath::NODE_AUTO)
3308         return;
3310    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3311    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3313    // nothing to do if we are an end node
3314     if (me->other == NULL) return;
3315     if (other->other == NULL) return;
3317     // nothing to do if we are a cusp node
3318     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3320     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3321     NRPathcode mecode;
3322     if (which_adjust == 1) {
3323         mecode = (NRPathcode)me->other->code;
3324     } else {
3325         mecode = (NRPathcode)node->code;
3326     }
3327     if (mecode == NR_LINETO) return;
3329     if (sp_node_side_is_line(node, other)) {
3330         // other is a line, and we are either smooth or symm
3331        Inkscape::NodePath::Node *othernode = other->other;
3332         double len = Geom::L2(me->pos - node->pos);
3333         Geom::Point delta = node->pos - othernode->pos;
3334         double linelen = Geom::L2(delta);
3335         if (linelen < 1e-18)
3336             return;
3337         me->pos = node->pos + (len / linelen)*delta;
3338         return;
3339     }
3341     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3342         // symmetrize
3343         me->pos = 2 * node->pos - other->pos;
3344         return;
3345     } else {
3346         // smoothify
3347         double len = Geom::L2(me->pos - node->pos);
3348         Geom::Point delta = other->pos - node->pos;
3349         double otherlen = Geom::L2(delta);
3350         if (otherlen < 1e-18) return;
3351         me->pos = node->pos - (len / otherlen) * delta;
3352     }
3355 /**
3356  \brief Adjusts both handles according to node type and line code
3357  */
3358 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3360     g_assert(node);
3362     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3364     /* we are either smooth or symm */
3366     if (node->p.other == NULL) return;
3367     if (node->n.other == NULL) return;
3369     if (node->type == Inkscape::NodePath::NODE_AUTO) {
3370         sp_node_adjust_handles_auto(node);
3371         return;
3372     }
3374     if (sp_node_side_is_line(node, &node->p)) {
3375         sp_node_adjust_handle(node, 1);
3376         return;
3377     }
3379     if (sp_node_side_is_line(node, &node->n)) {
3380         sp_node_adjust_handle(node, -1);
3381         return;
3382     }
3384     /* both are curves */
3385     Geom::Point const delta( node->n.pos - node->p.pos );
3387     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3388         node->p.pos = node->pos - delta / 2;
3389         node->n.pos = node->pos + delta / 2;
3390         return;
3391     }
3393     /* We are smooth */
3394     double plen = Geom::L2(node->p.pos - node->pos);
3395     if (plen < 1e-18) return;
3396     double nlen = Geom::L2(node->n.pos - node->pos);
3397     if (nlen < 1e-18) return;
3398     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3399     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3402 static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
3404     if (node->p.other == NULL || node->n.other == NULL) {
3405         node->p.pos = node->pos;
3406         node->n.pos = node->pos;
3407         return;
3408     }
3410     Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
3411     Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
3413     double norm_leg_prev = Geom::L2(leg_prev);
3414     double norm_leg_next = Geom::L2(leg_next);
3416     Geom::Point delta;
3417     if (norm_leg_next > 0.0) {
3418         delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
3419         delta.normalize();
3420     }
3422     node->p.pos = node->pos - norm_leg_prev / 3 * delta;
3423     node->n.pos = node->pos + norm_leg_next / 3 * delta;
3426 /**
3427  * Node event callback.
3428  */
3429 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3431     gboolean ret = FALSE;
3432     switch (event->type) {
3433         case GDK_ENTER_NOTIFY:
3434             Inkscape::NodePath::Path::active_node = n;
3435             break;
3436         case GDK_LEAVE_NOTIFY:
3437             Inkscape::NodePath::Path::active_node = NULL;
3438             break;
3439         case GDK_SCROLL:
3440             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3441                 switch (event->scroll.direction) {
3442                     case GDK_SCROLL_UP:
3443                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3444                         break;
3445                     case GDK_SCROLL_DOWN:
3446                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3447                         break;
3448                     default:
3449                         break;
3450                 }
3451                 ret = TRUE;
3452             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3453                 switch (event->scroll.direction) {
3454                     case GDK_SCROLL_UP:
3455                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3456                         break;
3457                     case GDK_SCROLL_DOWN:
3458                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3459                         break;
3460                     default:
3461                         break;
3462                 }
3463                 ret = TRUE;
3464             }
3465             break;
3466         case GDK_KEY_PRESS:
3467             switch (get_group0_keyval (&event->key)) {
3468                 case GDK_space:
3469                     if (event->key.state & GDK_BUTTON1_MASK) {
3470                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3471                         stamp_repr(nodepath);
3472                         ret = TRUE;
3473                     }
3474                     break;
3475                 case GDK_Page_Up:
3476                     if (event->key.state & GDK_CONTROL_MASK) {
3477                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3478                     } else {
3479                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3480                     }
3481                     break;
3482                 case GDK_Page_Down:
3483                     if (event->key.state & GDK_CONTROL_MASK) {
3484                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3485                     } else {
3486                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3487                     }
3488                     break;
3489                 default:
3490                     break;
3491             }
3492             break;
3493         default:
3494             break;
3495     }
3497     return ret;
3500 /**
3501  * Handle keypress on node; directly called.
3502  */
3503 gboolean node_key(GdkEvent *event)
3505     Inkscape::NodePath::Path *np;
3507     // there is no way to verify nodes so set active_node to nil when deleting!!
3508     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3510     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3511         gint ret = FALSE;
3512         switch (get_group0_keyval (&event->key)) {
3513             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3514             case GDK_BackSpace:
3515                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3516                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3517                 sp_nodepath_update_repr(np, _("Delete node"));
3518                 Inkscape::NodePath::Path::active_node = NULL;
3519                 ret = TRUE;
3520                 break;
3521             case GDK_c:
3522                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3523                 ret = TRUE;
3524                 break;
3525             case GDK_s:
3526                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3527                 ret = TRUE;
3528                 break;
3529             case GDK_a:
3530                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
3531                 ret = TRUE;
3532                 break;
3533             case GDK_y:
3534                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3535                 ret = TRUE;
3536                 break;
3537             case GDK_b:
3538                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3539                 ret = TRUE;
3540                 break;
3541         }
3542         return ret;
3543     }
3544     return FALSE;
3547 /**
3548  * Mouseclick on node callback.
3549  */
3550 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3552    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3554     if (state & GDK_CONTROL_MASK) {
3555         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3557         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3558             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3559                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3560             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3561                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3562             } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
3563                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
3564             } else {
3565                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3566             }
3567             sp_nodepath_update_repr(nodepath, _("Change node type"));
3568             sp_nodepath_update_statusbar(nodepath);
3570         } else { //ctrl+alt+click: delete node
3571             GList *node_to_delete = NULL;
3572             node_to_delete = g_list_append(node_to_delete, n);
3573             sp_node_delete_preserve(node_to_delete);
3574         }
3576     } else {
3577         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3578     }
3581 /**
3582  * Mouse grabbed node callback.
3583  */
3584 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3586    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3588     if (!n->selected) {
3589         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3590     }
3592     n->is_dragging = true;
3593     //sp_event_context_snap_window_open(n->subpath->nodepath->desktop->canvas);
3594     // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping)
3595     n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin;
3597     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3599     sp_nodepath_remember_origins (n->subpath->nodepath);
3602 /**
3603  * Mouse ungrabbed node callback.
3604  */
3605 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3607    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3609    n->dragging_out = NULL;
3610    n->is_dragging = false;
3611    //sp_event_context_snap_window_closed(n->subpath->nodepath->desktop->canvas);
3612    n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
3613    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3615    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3618 /**
3619  * The point on a line, given by its angle, closest to the given point.
3620  * \param p  A point.
3621  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3622  * \param closest  Pointer to the point struct where the result is stored.
3623  * \todo FIXME: use dot product perhaps?
3624  */
3625 static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
3627     if (a == HUGE_VAL) { // vertical
3628         *closest = Geom::Point(0, (*p)[Geom::Y]);
3629     } else {
3630         (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
3631         (*closest)[Geom::Y] = a * (*closest)[Geom::X];
3632     }
3635 /**
3636  * Distance from the point to a line given by its angle.
3637  * \param p  A point.
3638  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3639  */
3640 static double point_line_distance(Geom::Point *p, double a)
3642     Geom::Point c;
3643     point_line_closest(p, a, &c);
3644     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]));
3647 /**
3648  * Callback for node "request" signal.
3649  * \todo fixme: This goes to "moved" event? (lauris)
3650  */
3651 static gboolean
3652 node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
3654     double yn, xn, yp, xp;
3655     double an, ap, na, pa;
3656     double d_an, d_ap, d_na, d_pa;
3657     gboolean collinear = FALSE;
3658     Geom::Point c;
3659     Geom::Point pr;
3661     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3663     n->subpath->nodepath->desktop->snapindicator->remove_snaptarget();
3665     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3666     if ( (!n->subpath->nodepath->straight_path) &&
3667          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3668            || n->dragging_out ) )
3669     {
3670        Geom::Point mouse = p;
3672        if (!n->dragging_out) {
3673            // This is the first drag-out event; find out which handle to drag out
3674            double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
3675            double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
3677            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3678                return FALSE;
3680            Inkscape::NodePath::NodeSide *opposite;
3681            if (appr_p > appr_n) { // closer to p
3682                n->dragging_out = &n->p;
3683                opposite = &n->n;
3684                n->code = NR_CURVETO;
3685            } else if (appr_p < appr_n) { // closer to n
3686                n->dragging_out = &n->n;
3687                opposite = &n->p;
3688                n->n.other->code = NR_CURVETO;
3689            } else { // p and n nodes are the same
3690                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3691                    n->dragging_out = &n->p;
3692                    opposite = &n->n;
3693                    n->code = NR_CURVETO;
3694                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3695                    n->dragging_out = &n->n;
3696                    opposite = &n->p;
3697                    n->n.other->code = NR_CURVETO;
3698                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3699                    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);
3700                    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);
3701                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3702                        n->dragging_out = &n->n;
3703                        opposite = &n->p;
3704                        n->n.other->code = NR_CURVETO;
3705                    } else { // closer to other's n handle
3706                        n->dragging_out = &n->p;
3707                        opposite = &n->n;
3708                        n->code = NR_CURVETO;
3709                    }
3710                }
3711            }
3713            // if there's another handle, make sure the one we drag out starts parallel to it
3714            if (opposite->pos != n->pos) {
3715                mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
3716            }
3718            // knots might not be created yet!
3719            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3720            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3721        }
3723        // pass this on to the handle-moved callback
3724        node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
3725        sp_node_update_handles(n);
3726        return TRUE;
3727    }
3729     if (state & GDK_CONTROL_MASK) { // constrained motion
3731         // calculate relative distances of handles
3732         // n handle:
3733         yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
3734         xn = n->n.pos[Geom::X] - n->pos[Geom::X];
3735         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3736         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3737             if (n->n.other) { // if there is the next point
3738                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3739                     yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
3740                     xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
3741             }
3742         }
3743         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3744         if (yn < 0) { xn = -xn; yn = -yn; }
3746         // p handle:
3747         yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
3748         xp = n->p.pos[Geom::X] - n->pos[Geom::X];
3749         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3750         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3751             if (n->p.other) {
3752                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3753                     yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
3754                     xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
3755             }
3756         }
3757         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3758         if (yp < 0) { xp = -xp; yp = -yp; }
3760         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3761             // sliding on handles, only if at least one of the handles is non-vertical
3762             // (otherwise it's the same as ctrl+drag anyway)
3764             // calculate angles of the handles
3765             if (xn == 0) {
3766                 if (yn == 0) { // no handle, consider it the continuation of the other one
3767                     an = 0;
3768                     collinear = TRUE;
3769                 }
3770                 else an = 0; // vertical; set the angle to horizontal
3771             } else an = yn/xn;
3773             if (xp == 0) {
3774                 if (yp == 0) { // no handle, consider it the continuation of the other one
3775                     ap = an;
3776                 }
3777                 else ap = 0; // vertical; set the angle to horizontal
3778             } else  ap = yp/xp;
3780             if (collinear) an = ap;
3782             // angles of the perpendiculars; HUGE_VAL means vertical
3783             if (an == 0) na = HUGE_VAL; else na = -1/an;
3784             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3786             // mouse point relative to the node's original pos
3787             pr = p - n->origin;
3789             // distances to the four lines (two handles and two perpendiculars)
3790             d_an = point_line_distance(&pr, an);
3791             d_na = point_line_distance(&pr, na);
3792             d_ap = point_line_distance(&pr, ap);
3793             d_pa = point_line_distance(&pr, pa);
3795             // find out which line is the closest, save its closest point in c
3796             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3797                 point_line_closest(&pr, an, &c);
3798             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3799                 point_line_closest(&pr, ap, &c);
3800             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3801                 point_line_closest(&pr, na, &c);
3802             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3803                 point_line_closest(&pr, pa, &c);
3804             }
3806             // move the node to the closest point
3807             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3808                                             n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
3809                                             n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y],
3810                                             true);
3812         } else {  // constraining to hor/vert
3814             if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
3815                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3816                                                 p[Geom::X] - n->pos[Geom::X],
3817                                                 n->origin[Geom::Y] - n->pos[Geom::Y],
3818                                                 true,
3819                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
3820             } else { // snap to vert
3821                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3822                                                 n->origin[Geom::X] - n->pos[Geom::X],
3823                                                 p[Geom::Y] - n->pos[Geom::Y],
3824                                                 true,
3825                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
3826             }
3827         }
3828     } else { // move freely
3829         if (n->is_dragging) {
3830             if (state & GDK_MOD1_MASK) { // sculpt
3831                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
3832             } else {
3833                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3834                                             p[Geom::X] - n->pos[Geom::X],
3835                                             p[Geom::Y] - n->pos[Geom::Y],
3836                                             (state & GDK_SHIFT_MASK) == 0);
3837             }
3838         }
3839     }
3841     n->subpath->nodepath->desktop->scroll_to_point(p);
3843     return TRUE;
3846 /**
3847  * Node handle clicked callback.
3848  */
3849 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3851    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3853     if (state & GDK_CONTROL_MASK) { // "delete" handle
3854         if (n->p.knot == knot) {
3855             n->p.pos = n->pos;
3856         } else if (n->n.knot == knot) {
3857             n->n.pos = n->pos;
3858         }
3859         sp_node_update_handles(n);
3860         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3861         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3862         sp_nodepath_update_statusbar(nodepath);
3864     } else { // just select or add to selection, depending in Shift
3865         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3866     }
3869 /**
3870  * Node handle grabbed callback.
3871  */
3872 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3874    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3876     // convert auto -> smooth when dragging handle
3877    if (n->type == Inkscape::NodePath::NODE_AUTO) {
3878         n->type = Inkscape::NodePath::NODE_SMOOTH;
3879         sp_nodepath_update_node_knot (n);
3880    }
3882     if (!n->selected) {
3883         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3884     }
3886     // remember the origin point of the handle
3887     if (n->p.knot == knot) {
3888         n->p.origin_radial = n->p.pos - n->pos;
3889     } else if (n->n.knot == knot) {
3890         n->n.origin_radial = n->n.pos - n->pos;
3891     } else {
3892         g_assert_not_reached();
3893     }
3895     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3898 /**
3899  * Node handle ungrabbed callback.
3900  */
3901 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3903    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3905     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3906     if (n->p.knot == knot) {
3907         n->p.origin_radial.a = 0;
3908         sp_knot_set_position(knot, n->p.pos, state);
3909     } else if (n->n.knot == knot) {
3910         n->n.origin_radial.a = 0;
3911         sp_knot_set_position(knot, n->n.pos, state);
3912     } else {
3913         g_assert_not_reached();
3914     }
3916     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3919 /**
3920  * Node handle "request" signal callback.
3921  */
3922 static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data)
3924     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3926     Inkscape::NodePath::NodeSide *me, *opposite;
3927     gint which;
3928     if (n->p.knot == knot) {
3929         me = &n->p;
3930         opposite = &n->n;
3931         which = -1;
3932     } else if (n->n.knot == knot) {
3933         me = &n->n;
3934         opposite = &n->p;
3935         which = 1;
3936     } else {
3937         me = opposite = NULL;
3938         which = 0;
3939         g_assert_not_reached();
3940     }
3942     SPDesktop *desktop = n->subpath->nodepath->desktop;
3943     SnapManager &m = desktop->namedview->snap_manager;
3944     m.setup(desktop, true, n->subpath->nodepath->item);
3945     Inkscape::SnappedPoint s;
3947     if ((state & GDK_SHIFT_MASK) != 0) {
3948         // We will not try to snap when the shift-key is pressed
3949         // so remove the old snap indicator and don't wait for it to time-out
3950         desktop->snapindicator->remove_snaptarget();
3951     }
3953     Inkscape::NodePath::Node *othernode = opposite->other;
3954     Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
3955     if (othernode) {
3956         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3957             /* We are smooth node adjacent with line */
3958             Geom::Point const delta = p - n->pos;
3959             Geom::Coord const len = Geom::L2(delta);
3960             Inkscape::NodePath::Node *othernode = opposite->other;
3961             Geom::Point const ndelta = n->pos - othernode->pos;
3962             Geom::Coord const linelen = Geom::L2(ndelta);
3963             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3964                 Geom::Coord const scal = dot(delta, ndelta) / linelen;
3965                 p = n->pos + (scal / linelen) * ndelta;
3966             }
3967             if ((state & GDK_SHIFT_MASK) == 0) {
3968                 s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type, Inkscape::Snapper::ConstraintLine(p, ndelta), false);
3969             }
3970         } else {
3971             if ((state & GDK_SHIFT_MASK) == 0) {
3972                 s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3973             }
3974         }
3975     } else {
3976         if ((state & GDK_SHIFT_MASK) == 0) {
3977             s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
3978         }
3979     }
3981     s.getPoint(p);
3983     sp_node_adjust_handle(n, -which);
3985     return FALSE;
3988 /**
3989  * Node handle moved callback.
3990  */
3991 static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
3993    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3994    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
3996    Inkscape::NodePath::NodeSide *me;
3997    Inkscape::NodePath::NodeSide *other;
3998     if (n->p.knot == knot) {
3999         me = &n->p;
4000         other = &n->n;
4001     } else if (n->n.knot == knot) {
4002         me = &n->n;
4003         other = &n->p;
4004     } else {
4005         me = NULL;
4006         other = NULL;
4007         g_assert_not_reached();
4008     }
4010     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
4011     Radial rme(me->pos - n->pos);
4012     Radial rother(other->pos - n->pos);
4013     Radial rnew(p - n->pos);
4015     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
4016         int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
4017         /* 0 interpreted as "no snapping". */
4019         // 1. Snap to the closest PI/snaps angle, starting from zero.
4020         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
4022         // 2. Snap to the original angle, its opposite and perpendiculars
4023         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
4024             /* The closest PI/2 angle, starting from original angle */
4025             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
4027             // Snap to the closest.
4028             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
4029                        ? a_snapped
4030                        : a_ortho );
4031         }
4033         // 3. Snap to the angle of the opposite line, if any
4034         Inkscape::NodePath::Node *othernode = other->other;
4035         if (othernode) {
4036             Geom::Point other_to_snap(0,0);
4037             if (sp_node_side_is_line(n, other)) {
4038                 other_to_snap = othernode->pos - n->pos;
4039             } else {
4040                 other_to_snap = other->pos - n->pos;
4041             }
4042             if (Geom::L2(other_to_snap) > 1e-3) {
4043                 Radial rother_to_snap(other_to_snap);
4044                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
4045                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
4047                 // Snap to the closest.
4048                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
4049                        ? a_snapped
4050                        : a_oppo );
4051             }
4052         }
4054         rnew.a = a_snapped;
4055     }
4057     if (state & GDK_MOD1_MASK) {
4058         // lock handle length
4059         rnew.r = me->origin_radial.r;
4060     }
4062     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (!n->dragging_out && (state & GDK_SHIFT_MASK)))
4063         && (rme.a != HUGE_VAL) && (rnew.a != HUGE_VAL) && ((fabs(rme.a - rnew.a) > 0.001) || (n->type ==Inkscape::NodePath::NODE_SYMM))) {
4064         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
4065         rother.a += rnew.a - rme.a;
4066         other->pos = Geom::Point(rother) + n->pos;
4067         if (other->knot) {
4068             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
4069             sp_knot_moveto(other->knot, other->pos);
4070         }
4071     }
4073     me->pos = Geom::Point(rnew) + n->pos;
4074     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
4076     // move knot, but without emitting the signal:
4077     // we cannot emit a "moved" signal because we're now processing it
4078     sp_knot_moveto(me->knot, me->pos);
4080     update_object(n->subpath->nodepath);
4082     /* status text */
4083     SPDesktop *desktop = n->subpath->nodepath->desktop;
4084     if (!desktop) return;
4085     SPEventContext *ec = desktop->event_context;
4086     if (!ec) return;
4088     Inkscape::MessageContext *mc = get_message_context(ec);
4090     if (!mc) return;
4092     double degrees = 180 / M_PI * rnew.a;
4093     if (degrees > 180) degrees -= 360;
4094     if (degrees < -180) degrees += 360;
4095     if (prefs->getBool("/options/compassangledisplay/value"))
4096         degrees = angle_to_compass (degrees);
4098     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
4100     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
4101          _("<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);
4103     g_string_free(length, TRUE);
4106 /**
4107  * Node handle event callback.
4108  */
4109 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
4111     gboolean ret = FALSE;
4112     switch (event->type) {
4113         case GDK_KEY_PRESS:
4114             switch (get_group0_keyval (&event->key)) {
4115                 case GDK_space:
4116                     if (event->key.state & GDK_BUTTON1_MASK) {
4117                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
4118                         stamp_repr(nodepath);
4119                         ret = TRUE;
4120                     }
4121                     break;
4122                 default:
4123                     break;
4124             }
4125             break;
4126         case GDK_ENTER_NOTIFY:
4127             // we use an experimentally determined threshold that seems to work fine
4128             if (Geom::L2(n->pos - knot->pos) < 0.75)
4129                 Inkscape::NodePath::Path::active_node = n;
4130             break;
4131         case GDK_LEAVE_NOTIFY:
4132             // we use an experimentally determined threshold that seems to work fine
4133             if (Geom::L2(n->pos - knot->pos) < 0.75)
4134                 Inkscape::NodePath::Path::active_node = NULL;
4135             break;
4136         default:
4137             break;
4138     }
4140     return ret;
4143 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4144                                  Radial &rme, Radial &rother, gboolean const both)
4146     rme.a += angle;
4147     if ( both
4148          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4149          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4150     {
4151         rother.a += angle;
4152     }
4155 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4156                                         Radial &rme, Radial &rother, gboolean const both)
4158     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4160     gdouble r;
4161     if ( both
4162          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4163          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4164     {
4165         r = MAX(rme.r, rother.r);
4166     } else {
4167         r = rme.r;
4168     }
4170     gdouble const weird_angle = atan2(norm_angle, r);
4171 /* Bulia says norm_angle is just the visible distance that the
4172  * object's end must travel on the screen.  Left as 'angle' for want of
4173  * a better name.*/
4175     rme.a += weird_angle;
4176     if ( both
4177          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4178          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4179     {
4180         rother.a += weird_angle;
4181     }
4184 /**
4185  * Rotate one node.
4186  */
4187 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4189     Inkscape::NodePath::NodeSide *me, *other;
4190     bool both = false;
4192     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4193     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4195     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4196         me = &(n->p);
4197         other = &(n->n);
4198     } else if (!n->p.other) {
4199         me = &(n->n);
4200         other = &(n->p);
4201     } else {
4202         if (which > 0) { // right handle
4203             if (xn > xp) {
4204                 me = &(n->n);
4205                 other = &(n->p);
4206             } else {
4207                 me = &(n->p);
4208                 other = &(n->n);
4209             }
4210         } else if (which < 0){ // left handle
4211             if (xn <= xp) {
4212                 me = &(n->n);
4213                 other = &(n->p);
4214             } else {
4215                 me = &(n->p);
4216                 other = &(n->n);
4217             }
4218         } else { // both handles
4219             me = &(n->n);
4220             other = &(n->p);
4221             both = true;
4222         }
4223     }
4225     Radial rme(me->pos - n->pos);
4226     Radial rother(other->pos - n->pos);
4228     if (screen) {
4229         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4230     } else {
4231         node_rotate_one_internal (*n, angle, rme, rother, both);
4232     }
4234     me->pos = n->pos + Geom::Point(rme);
4236     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4237         other->pos =  n->pos + Geom::Point(rother);
4238     }
4240     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4241     // so here we just move all the knots without emitting move signals, for speed
4242     sp_node_update_handles(n, false);
4245 /**
4246  * Rotate selected nodes.
4247  */
4248 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4250     if (!nodepath || !nodepath->selected) return;
4252     if (g_list_length(nodepath->selected) == 1) {
4253        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4254         node_rotate_one (n, angle, which, screen);
4255     } else {
4256        // rotate as an object:
4258         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4259         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4260         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4261             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4262             box.expandTo (n->pos); // contain all selected nodes
4263         }
4265         gdouble rot;
4266         if (screen) {
4267             gdouble const zoom = nodepath->desktop->current_zoom();
4268             gdouble const zmove = angle / zoom;
4269             gdouble const r = Geom::L2(box.max() - box.midpoint());
4270             rot = atan2(zmove, r);
4271         } else {
4272             rot = angle;
4273         }
4275         Geom::Point rot_center;
4276         if (Inkscape::NodePath::Path::active_node == NULL)
4277             rot_center = box.midpoint();
4278         else
4279             rot_center = Inkscape::NodePath::Path::active_node->pos;
4281         Geom::Matrix t =
4282             Geom::Matrix (Geom::Translate(-rot_center)) *
4283             Geom::Matrix (Geom::Rotate(rot)) *
4284             Geom::Matrix (Geom::Translate(rot_center));
4286         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4287             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4288             n->pos *= t;
4289             n->n.pos *= t;
4290             n->p.pos *= t;
4291             sp_node_update_handles(n, false);
4292         }
4293     }
4295     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4298 /**
4299  * Scale one node.
4300  */
4301 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4303     bool both = false;
4304     Inkscape::NodePath::NodeSide *me, *other;
4306     double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
4307     double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
4309     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4310         me = &(n->p);
4311         other = &(n->n);
4312         n->code = NR_CURVETO;
4313     } else if (!n->p.other) {
4314         me = &(n->n);
4315         other = &(n->p);
4316         if (n->n.other)
4317             n->n.other->code = NR_CURVETO;
4318     } else {
4319         if (which > 0) { // right handle
4320             if (xn > xp) {
4321                 me = &(n->n);
4322                 other = &(n->p);
4323                 if (n->n.other)
4324                     n->n.other->code = NR_CURVETO;
4325             } else {
4326                 me = &(n->p);
4327                 other = &(n->n);
4328                 n->code = NR_CURVETO;
4329             }
4330         } else if (which < 0){ // left handle
4331             if (xn <= xp) {
4332                 me = &(n->n);
4333                 other = &(n->p);
4334                 if (n->n.other)
4335                     n->n.other->code = NR_CURVETO;
4336             } else {
4337                 me = &(n->p);
4338                 other = &(n->n);
4339                 n->code = NR_CURVETO;
4340             }
4341         } else { // both handles
4342             me = &(n->n);
4343             other = &(n->p);
4344             both = true;
4345             n->code = NR_CURVETO;
4346             if (n->n.other)
4347                 n->n.other->code = NR_CURVETO;
4348         }
4349     }
4351     Radial rme(me->pos - n->pos);
4352     Radial rother(other->pos - n->pos);
4354     rme.r += grow;
4355     if (rme.r < 0) rme.r = 0;
4356     if (rme.a == HUGE_VAL) {
4357         if (me->other) { // if direction is unknown, initialize it towards the next node
4358             Radial rme_next(me->other->pos - n->pos);
4359             rme.a = rme_next.a;
4360         } else { // if there's no next, initialize to 0
4361             rme.a = 0;
4362         }
4363     }
4364     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4365         rother.r += grow;
4366         if (rother.r < 0) rother.r = 0;
4367         if (rother.a == HUGE_VAL) {
4368             rother.a = rme.a + M_PI;
4369         }
4370     }
4372     me->pos = n->pos + Geom::Point(rme);
4374     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4375         other->pos = n->pos + Geom::Point(rother);
4376     }
4378     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4379     // so here we just move all the knots without emitting move signals, for speed
4380     sp_node_update_handles(n, false);
4383 /**
4384  * Scale selected nodes.
4385  */
4386 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4388     if (!nodepath || !nodepath->selected) return;
4390     if (g_list_length(nodepath->selected) == 1) {
4391         // scale handles of the single selected node
4392         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4393         node_scale_one (n, grow, which);
4394     } else {
4395         // scale nodes as an "object":
4397         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4398         Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4399         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4400             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4401             box.expandTo (n->pos); // contain all selected nodes
4402         }
4404         if ( Geom::are_near(box.maxExtent(), 0) ) {
4405             SPEventContext *ec = nodepath->desktop->event_context;
4406             if (!ec) return;
4407             Inkscape::MessageContext *mc = get_message_context(ec);
4408             if (!mc) return;
4409             mc->setF(Inkscape::WARNING_MESSAGE,
4410                              _("Cannot scale nodes when all are at the same location."));
4411             return;
4412         }
4413         double scale = (box.maxExtent() + grow)/box.maxExtent();
4416         Geom::Point scale_center;
4417         if (Inkscape::NodePath::Path::active_node == NULL)
4418             scale_center = box.midpoint();
4419         else
4420             scale_center = Inkscape::NodePath::Path::active_node->pos;
4422         Geom::Matrix t =
4423             Geom::Translate(-scale_center) *
4424             Geom::Scale(scale, scale) *
4425             Geom::Translate(scale_center);
4427         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4428             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4429             n->pos *= t;
4430             n->n.pos *= t;
4431             n->p.pos *= t;
4432             sp_node_update_handles(n, false);
4433         }
4434     }
4436     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4439 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4441     if (!nodepath) return;
4442     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4445 /**
4446  * Flip selected nodes horizontally/vertically.
4447  */
4448 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
4450     if (!nodepath || !nodepath->selected) return;
4452     if (g_list_length(nodepath->selected) == 1 && !center) {
4453         // flip handles of the single selected node
4454         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4455         double temp = n->p.pos[axis];
4456         n->p.pos[axis] = n->n.pos[axis];
4457         n->n.pos[axis] = temp;
4458         sp_node_update_handles(n, false);
4459     } else {
4460         // scale nodes as an "object":
4462         Geom::Rect box = sp_node_selected_bbox (nodepath);
4463         if (!center) {
4464             center = box.midpoint();
4465         }
4466         Geom::Matrix t =
4467             Geom::Matrix (Geom::Translate(- *center)) *
4468             Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
4469             Geom::Matrix (Geom::Translate(*center));
4471         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4472             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4473             n->pos *= t;
4474             n->n.pos *= t;
4475             n->p.pos *= t;
4476             sp_node_update_handles(n, false);
4477         }
4478     }
4480     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4483 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4485     g_assert (nodepath->selected);
4487     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4488     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4489     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4490         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4491         box.expandTo (n->pos); // contain all selected nodes
4492     }
4493     return box;
4496 //-----------------------------------------------
4497 /**
4498  * Return new subpath under given nodepath.
4499  */
4500 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4502     g_assert(nodepath);
4503     g_assert(nodepath->desktop);
4505    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4507     s->nodepath = nodepath;
4508     s->closed = FALSE;
4509     s->nodes = NULL;
4510     s->first = NULL;
4511     s->last = NULL;
4513     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4514     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4515     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4517     return s;
4520 /**
4521  * Destroy nodes in subpath, then subpath itself.
4522  */
4523 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4525     g_assert(subpath);
4526     g_assert(subpath->nodepath);
4527     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4529     while (subpath->nodes) {
4530         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4531     }
4533     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4535     g_free(subpath);
4538 /**
4539  * Link head to tail in subpath.
4540  */
4541 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4543     g_assert(!sp->closed);
4544     g_assert(sp->last != sp->first);
4545     g_assert(sp->first->code == NR_MOVETO);
4547     sp->closed = TRUE;
4549     //Link the head to the tail
4550     sp->first->p.other = sp->last;
4551     sp->last->n.other  = sp->first;
4552     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4553     sp->first          = sp->last;
4555     //Remove the extra end node
4556     sp_nodepath_node_destroy(sp->last->n.other);
4559 /**
4560  * Open closed (loopy) subpath at node.
4561  */
4562 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4564     g_assert(sp->closed);
4565     g_assert(n->subpath == sp);
4566     g_assert(sp->first == sp->last);
4568     /* We create new startpoint, current node will become last one */
4570    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4571                                                 &n->pos, &n->pos, &n->n.pos);
4574     sp->closed        = FALSE;
4576     //Unlink to make a head and tail
4577     sp->first         = new_path;
4578     sp->last          = n;
4579     n->n.other        = NULL;
4580     new_path->p.other = NULL;
4583 /**
4584  * Return new node in subpath with given properties.
4585  * \param pos Position of node.
4586  * \param ppos Handle position in previous direction
4587  * \param npos Handle position in previous direction
4588  */
4589 Inkscape::NodePath::Node *
4590 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)
4592     g_assert(sp);
4593     g_assert(sp->nodepath);
4594     g_assert(sp->nodepath->desktop);
4596     if (nodechunk == NULL)
4597         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4599     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4601     n->subpath  = sp;
4603     if (type != Inkscape::NodePath::NODE_NONE) {
4604         // use the type from sodipodi:nodetypes
4605         n->type = type;
4606     } else {
4607         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4608             // points are (almost) collinear
4609             if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
4610                 // endnode, or a node with a retracted handle
4611                 n->type = Inkscape::NodePath::NODE_CUSP;
4612             } else {
4613                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4614             }
4615         } else {
4616             n->type = Inkscape::NodePath::NODE_CUSP;
4617         }
4618     }
4620     n->code     = code;
4621     n->selected = FALSE;
4622     n->pos      = *pos;
4623     n->p.pos    = *ppos;
4624     n->n.pos    = *npos;
4626     n->dragging_out = NULL;
4628     Inkscape::NodePath::Node *prev;
4629     if (next) {
4630         //g_assert(g_list_find(sp->nodes, next));
4631         prev = next->p.other;
4632     } else {
4633         prev = sp->last;
4634     }
4636     if (prev)
4637         prev->n.other = n;
4638     else
4639         sp->first = n;
4641     if (next)
4642         next->p.other = n;
4643     else
4644         sp->last = n;
4646     n->p.other = prev;
4647     n->n.other = next;
4649     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"));
4650     sp_knot_set_position(n->knot, *pos, 0);
4652     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4653     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4654     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4656     sp_nodepath_update_node_knot(n);
4658     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4659     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4660     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4661     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4662     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4663     sp_knot_show(n->knot);
4665     // We only create handle knots and lines on demand
4666     n->p.knot = NULL;
4667     n->p.line = NULL;
4668     n->n.knot = NULL;
4669     n->n.line = NULL;
4671     sp->nodes = g_list_prepend(sp->nodes, n);
4673     return n;
4676 /**
4677  * Destroy node and its knots, link neighbors in subpath.
4678  */
4679 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4681     g_assert(node);
4682     g_assert(node->subpath);
4683     g_assert(SP_IS_KNOT(node->knot));
4685    Inkscape::NodePath::SubPath *sp = node->subpath;
4687     if (node->selected) { // first, deselect
4688         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4689         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4690     }
4692     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4694     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4695     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4696     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4697     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4698     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4699     g_object_unref(G_OBJECT(node->knot));
4701     if (node->p.knot) {
4702         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4703         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4704         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4705         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4706         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4707         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4708         g_object_unref(G_OBJECT(node->p.knot));
4709         node->p.knot = NULL;
4710     }
4712     if (node->n.knot) {
4713         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4714         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4715         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4716         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4717         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4718         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4719         g_object_unref(G_OBJECT(node->n.knot));
4720         node->n.knot = NULL;
4721     }
4723     if (node->p.line)
4724         gtk_object_destroy(GTK_OBJECT(node->p.line));
4725     if (node->n.line)
4726         gtk_object_destroy(GTK_OBJECT(node->n.line));
4728     if (sp->nodes) { // there are others nodes on the subpath
4729         if (sp->closed) {
4730             if (sp->first == node) {
4731                 g_assert(sp->last == node);
4732                 sp->first = node->n.other;
4733                 sp->last = sp->first;
4734             }
4735             node->p.other->n.other = node->n.other;
4736             node->n.other->p.other = node->p.other;
4737         } else {
4738             if (sp->first == node) {
4739                 sp->first = node->n.other;
4740                 sp->first->code = NR_MOVETO;
4741             }
4742             if (sp->last == node) sp->last = node->p.other;
4743             if (node->p.other) node->p.other->n.other = node->n.other;
4744             if (node->n.other) node->n.other->p.other = node->p.other;
4745         }
4746     } else { // this was the last node on subpath
4747         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4748     }
4750     g_mem_chunk_free(nodechunk, node);
4753 /**
4754  * Returns one of the node's two sides.
4755  * \param which Indicates which side.
4756  * \return Pointer to previous node side if which==-1, next if which==1.
4757  */
4758 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4760     g_assert(node);
4761     Inkscape::NodePath::NodeSide * result = 0;
4762     switch (which) {
4763         case -1:
4764             result = &node->p;
4765             break;
4766         case 1:
4767             result = &node->n;
4768             break;
4769         default:
4770             g_assert_not_reached();
4771     }
4773     return result;
4776 /**
4777  * Return the other side of the node, given one of its sides.
4778  */
4779 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4781     g_assert(node);
4782     Inkscape::NodePath::NodeSide *result = 0;
4784     if (me == &node->p) {
4785         result = &node->n;
4786     } else if (me == &node->n) {
4787         result = &node->p;
4788     } else {
4789         g_assert_not_reached();
4790     }
4792     return result;
4795 /**
4796  * Return NRPathcode on the given side of the node.
4797  */
4798 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4800     g_assert(node);
4802     NRPathcode result = NR_END;
4803     if (me == &node->p) {
4804         if (node->p.other) {
4805             result = (NRPathcode)node->code;
4806         } else {
4807             result = NR_MOVETO;
4808         }
4809     } else if (me == &node->n) {
4810         if (node->n.other) {
4811             result = (NRPathcode)node->n.other->code;
4812         } else {
4813             result = NR_MOVETO;
4814         }
4815     } else {
4816         g_assert_not_reached();
4817     }
4819     return result;
4822 /**
4823  * Return node with the given index
4824  */
4825 Inkscape::NodePath::Node *
4826 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4828     Inkscape::NodePath::Node *e = NULL;
4830     if (!nodepath) {
4831         return e;
4832     }
4834     //find segment
4835     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4837         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4838         int n = g_list_length(sp->nodes);
4839         if (sp->closed) {
4840             n++;
4841         }
4843         //if the piece belongs to this subpath grab it
4844         //otherwise move onto the next subpath
4845         if (index < n) {
4846             e = sp->first;
4847             for (int i = 0; i < index; ++i) {
4848                 e = e->n.other;
4849             }
4850             break;
4851         } else {
4852             if (sp->closed) {
4853                 index -= (n+1);
4854             } else {
4855                 index -= n;
4856             }
4857         }
4858     }
4860     return e;
4863 /**
4864  * Returns plain text meaning of node type.
4865  */
4866 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4868     unsigned retracted = 0;
4869     bool endnode = false;
4871     for (int which = -1; which <= 1; which += 2) {
4872         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4873         if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
4874             retracted ++;
4875         if (!side->other)
4876             endnode = true;
4877     }
4879     if (retracted == 0) {
4880         if (endnode) {
4881                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4882                 return _("end node");
4883         } else {
4884             switch (node->type) {
4885                 case Inkscape::NodePath::NODE_CUSP:
4886                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4887                     return _("cusp");
4888                 case Inkscape::NodePath::NODE_SMOOTH:
4889                     // TRANSLATORS: "smooth" is an adjective here
4890                     return _("smooth");
4891                 case Inkscape::NodePath::NODE_AUTO:
4892                     return _("auto");
4893                 case Inkscape::NodePath::NODE_SYMM:
4894                     return _("symmetric");
4895             }
4896         }
4897     } else if (retracted == 1) {
4898         if (endnode) {
4899             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4900             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4901         } else {
4902             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4903         }
4904     } else {
4905         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4906     }
4908     return NULL;
4911 /**
4912  * Handles content of statusbar as long as node tool is active.
4913  */
4914 void
4915 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4917     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");
4918     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4920     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4921     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4922     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4923     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4925     SPDesktop *desktop = NULL;
4926     if (nodepath) {
4927         desktop = nodepath->desktop;
4928     } else {
4929         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4930     }
4932     SPEventContext *ec = desktop->event_context;
4933     if (!ec) return;
4935     Inkscape::MessageContext *mc = get_message_context(ec);
4936     if (!mc) return;
4938     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4940     if (selected_nodes == 0) {
4941         Inkscape::Selection *sel = desktop->selection;
4942         if (!sel || sel->isEmpty()) {
4943             mc->setF(Inkscape::NORMAL_MESSAGE,
4944                      _("Select a single object to edit its nodes or handles."));
4945         } else {
4946             if (nodepath) {
4947             mc->setF(Inkscape::NORMAL_MESSAGE,
4948                      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.",
4949                               "<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.",
4950                               total_nodes),
4951                      total_nodes);
4952             } else {
4953                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4954                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4955                 } else {
4956                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4957                 }
4958             }
4959         }
4960     } else if (nodepath && selected_nodes == 1) {
4961         mc->setF(Inkscape::NORMAL_MESSAGE,
4962                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4963                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4964                           total_nodes),
4965                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4966     } else {
4967         if (selected_subpaths > 1) {
4968             mc->setF(Inkscape::NORMAL_MESSAGE,
4969                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4970                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4971                               total_nodes),
4972                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4973         } else {
4974             mc->setF(Inkscape::NORMAL_MESSAGE,
4975                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4976                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4977                               total_nodes),
4978                      selected_nodes, total_nodes, when_selected);
4979         }
4980     }
4983 /*
4984  * returns a *copy* of the curve of that object.
4985  */
4986 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4987     if (!object)
4988         return NULL;
4990     SPCurve *curve = NULL;
4991     if (SP_IS_PATH(object)) {
4992         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4993         curve = curve_new->copy();
4994     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4995         const gchar *svgd = object->repr->attribute(key);
4996         if (svgd) {
4997             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4998             SPCurve *curve_new = new SPCurve(pv);
4999             if (curve_new) {
5000                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
5001             }
5002         }
5003     }
5005     return curve;
5008 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
5009     if (!np || !np->object || !curve)
5010         return;
5012     if (SP_IS_PATH(np->object)) {
5013         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
5014             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
5015         } else {
5016             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
5017         }
5018     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
5019         Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
5020         if (lpe) {
5021             Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
5022             if (pathparam) {
5023                 pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
5024                 np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
5025             }
5026         }
5027     }
5030 /*
5031 SPCanvasItem *
5032 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
5033     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
5035 */
5038 /// \todo this code to generate a helper canvasitem from an spcurve should be moved to different file
5039 SPCanvasItem *
5040 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const Geom::Matrix & i2d, guint32 color = 0xff0000ff) {
5041     SPCurve *flash_curve = curve->copy();
5042     flash_curve->transform(i2d);
5043     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
5044     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
5045     // unless we also flash the nodes...
5046     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
5047     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
5048     sp_canvas_item_show(canvasitem);
5049     flash_curve->unref();
5050     return canvasitem;
5053 SPCanvasItem *
5054 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) {
5055     if (!item || !desktop) {
5056         return NULL;
5057     }
5059     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
5060     guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
5062     Geom::Matrix i2d = sp_item_i2d_affine(item);
5064     SPCurve *curve = NULL;
5065     if (SP_IS_PATH(item)) {
5066         curve = sp_path_get_curve_for_edit(SP_PATH(item));
5067     } else if ( SP_IS_SHAPE(item) && SP_SHAPE(item)->curve ) {
5068         curve = sp_shape_get_curve (SP_SHAPE(item));
5069     } else if ( SP_IS_TEXT(item) ) {
5070         // do not display helperpath for text - we cannot do anything with it in Node tool anyway
5071         // curve = SP_TEXT(item)->getNormalizedBpath();
5072         return NULL;
5073     } else {
5074         g_warning ("-----> sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item): TODO: generate the helper path for this item type!\n");
5075         return NULL;
5076     }
5078     SPCanvasItem * helperpath = sp_nodepath_generate_helperpath(desktop, curve, i2d, color);
5080     curve->unref();
5082     return helperpath;
5086 // TODO: Merge this with sp_nodepath_make_helper_item()!
5087 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
5088     np->show_helperpath = show;
5090     if (show) {
5091         SPCurve *helper_curve = np->curve->copy();
5092         helper_curve->transform(np->i2d);
5093         if (!np->helper_path) {
5094             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
5096             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
5097             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);
5098             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
5099             sp_canvas_item_move_to_z(np->helper_path, 0);
5100             sp_canvas_item_show(np->helper_path);
5101         } else {
5102             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
5103         }
5104         helper_curve->unref();
5105     } else {
5106         if (np->helper_path) {
5107             GtkObject *temp = np->helper_path;
5108             np->helper_path = NULL;
5109             gtk_object_destroy(temp);
5110         }
5111     }
5114 /* sp_nodepath_make_straight_path:
5115  *   Prevents user from curving the path by dragging a segment or activating handles etc.
5116  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
5117  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
5118  */
5119 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
5120     np->straight_path = true;
5121     np->show_handles = false;
5122     g_message("add code to make the path straight.");
5123     // do sp_nodepath_convert_node_type on all nodes?
5124     // coding tip: search for this text : "Make selected segments lines"
5127 /*
5128   Local Variables:
5129   mode:c++
5130   c-file-style:"stroustrup"
5131   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
5132   indent-tabs-mode:nil
5133   fill-column:99
5134   End:
5135 */
5136 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :