Code

f6f9ef0d199cca4e6f17d551e67fa337f0c8bbae
[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 "knot.h"
30 #include "inkscape.h"
31 #include "document.h"
32 #include "sp-namedview.h"
33 #include "desktop.h"
34 #include "desktop-handles.h"
35 #include "snap.h"
36 #include "message-stack.h"
37 #include "message-context.h"
38 #include "node-context.h"
39 #include "shape-editor.h"
40 #include "selection-chemistry.h"
41 #include "selection.h"
42 #include "xml/repr.h"
43 #include "prefs-utils.h"
44 #include "sp-metrics.h"
45 #include "sp-path.h"
46 #include "libnr/nr-matrix-ops.h"
47 #include "svg/svg.h"
48 #include "verbs.h"
49 #include "display/bezier-utils.h"
50 #include <vector>
51 #include <algorithm>
52 #include <cstring>
53 #include <cmath>
54 #include <string>
55 #include "live_effects/lpeobject.h"
56 #include "live_effects/lpeobject-reference.h"
57 #include "live_effects/effect.h"
58 #include "live_effects/parameter/parameter.h"
59 #include "live_effects/parameter/path.h"
60 #include "util/mathfns.h"
61 #include "display/snap-indicator.h"
62 #include "snapped-point.h"
64 class NR::Matrix;
66 /// \todo
67 /// evil evil evil. FIXME: conflict of two different Path classes!
68 /// There is a conflict in the namespace between two classes named Path.
69 /// #include "sp-flowtext.h"
70 /// #include "sp-flowregion.h"
72 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
73 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
74 GType sp_flowregion_get_type (void);
75 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
76 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
77 GType sp_flowtext_get_type (void);
78 // end evil workaround
80 #include "helper/stlport.h"
83 /// \todo fixme: Implement these via preferences */
85 #define NODE_FILL          0xbfbfbf00
86 #define NODE_STROKE        0x000000ff
87 #define NODE_FILL_HI       0xff000000
88 #define NODE_STROKE_HI     0x000000ff
89 #define NODE_FILL_SEL      0x0000ffff
90 #define NODE_STROKE_SEL    0x000000ff
91 #define NODE_FILL_SEL_HI   0xff000000
92 #define NODE_STROKE_SEL_HI 0x000000ff
93 #define KNOT_FILL          0xffffffff
94 #define KNOT_STROKE        0x000000ff
95 #define KNOT_FILL_HI       0xff000000
96 #define KNOT_STROKE_HI     0x000000ff
98 static GMemChunk *nodechunk = NULL;
100 /* Creation from object */
102 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
103 static void add_curve_to_subpath( Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c,
104                                   Inkscape::NodePath::NodeType const *t, guint & i, NR::Point & ppos, NRPathcode & pcode  );
105 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
107 /* Object updating */
109 static void stamp_repr(Inkscape::NodePath::Path *np);
110 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
111 static gchar *create_typestr(Inkscape::NodePath::Path *np);
113 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
115 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
117 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
119 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
121 /* Adjust handle placement, if the node or the other handle is moved */
122 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
123 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
125 /* Node event callbacks */
126 static void node_clicked(SPKnot *knot, guint state, gpointer data);
127 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
128 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
129 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
131 /* Handle event callbacks */
132 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
134 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
135 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
136 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
137 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
139 /* Constructors and destructors */
141 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
142 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
143 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
144 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
145 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
146                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
147 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
149 /* Helpers */
151 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
152 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
153 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
155 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
156 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
158 // active_node indicates mouseover node
159 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
161 static SPCanvasItem *
162 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
163     g_print ("sp_nodepath_make_helper_item()\n");
164     SPCurve *helper_curve = curve->copy();
165     helper_curve->transform(to_2geom(np->i2d));
166     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
167     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
168     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
169     sp_canvas_item_move_to_z(helper_path, 0);
170     if (show) {
171         sp_canvas_item_show(helper_path);
172     }
173     helper_curve->unref();
174     return helper_path;
177 static SPCanvasItem *
178 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
179     g_print ("canvasitem_from_pathvec()\n");
180     SPCurve *helper_curve = new SPCurve(pathv);
181     return sp_nodepath_make_helper_item(np, helper_curve, show);
184 static void
185 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
186     g_print ("sp_nodepath_create_helperpaths()\n");
187     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
188     if (np->item) {
189         g_print ("np->item: %s\n", SP_OBJECT_REPR(np->item)->attribute("id"));
190     } else {
191         g_print ("np->item == NULL!\n");
192     }
194     if (!SP_IS_LPE_ITEM(np->item)) {
195         g_print ("Only LPEItems can have helperpaths!\n");
196         return;
197     }
199     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
200     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
201     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
202         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
203         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->lpe;
204         g_print ("Processing LPE %s\n", SP_OBJECT_REPR(lperef->lpeobject)->attribute("id"));
205         // create new canvas items from the effect's helper paths
206         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
207         for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
208             g_print ("   ... creating helper path\n");
209             (*np->helper_path_vec)[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
210         }
211     }
212     g_print ("\n");
215 static void
216 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
217     g_print ("sp_nodepath_update_helperpaths()\n");
218     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
219     if (!SP_IS_LPE_ITEM(np->item)) {
220         g_print ("Only LPEItems can have helperpaths!\n");
221         return;
222     }
224     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
225     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
226     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
227         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->lpe;
228         g_print ("Processing LPE %s\n", SP_OBJECT_REPR((*i)->lpeobject)->attribute("id"));
229         /* update canvas items from the effect's helper paths; note that this code relies on the
230          * fact that getHelperPaths() will always return the same number of helperpaths in the same
231          * order as during their creation in sp_nodepath_create_helperpaths
232          */
233         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
234         for (unsigned int j = 0; j < hpaths.size(); ++j) {
235             g_print ("   ... updating helper path\n");
236             SPCurve *curve = new SPCurve(hpaths[j]);
237             curve->transform(to_2geom(np->i2d));
238             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(((*np->helper_path_vec)[lpe])[j]), curve);
239             curve = curve->unref();
240         }
241     }
242     g_print ("\n");
245 static void
246 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
247     g_print ("sp_nodepath_destroy_helperpaths()\n");
248     for (HelperPathList::iterator i = np->helper_path_vec->begin(); i != np->helper_path_vec->end(); ++i) {
249         g_print ("processing helper paths for LPE %s\n", SP_OBJECT_REPR((*i).first->getLPEObj())->attribute("id"));
250         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
251             g_print ("   ... destroying helper path\n");
252             GtkObject *temp = *j;
253             *j = NULL;
254             gtk_object_destroy(temp);
255         }
256     }
257     g_print ("\n");
261 /**
262  * \brief Creates new nodepath from item
263  */
264 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
266     Inkscape::XML::Node *repr = object->repr;
268     /** \todo
269      * FIXME: remove this. We don't want to edit paths inside flowtext.
270      * Instead we will build our flowtext with cloned paths, so that the
271      * real paths are outside the flowtext and thus editable as usual.
272      */
273     if (SP_IS_FLOWTEXT(object)) {
274         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
275             if SP_IS_FLOWREGION(child) {
276                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
277                 if (grandchild && SP_IS_PATH(grandchild)) {
278                     object = SP_ITEM(grandchild);
279                     break;
280                 }
281             }
282         }
283     }
285     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
287     if (curve == NULL)
288         return NULL;
290     if (curve->get_segment_count() < 1) {
291         curve->unref();
292         return NULL; // prevent crash for one-node paths
293     }
295     //Create new nodepath
296     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
297     if (!np) {
298         curve->unref();
299         return NULL;
300     }
302     // Set defaults
303     np->desktop     = desktop;
304     np->object      = object;
305     np->subpaths    = NULL;
306     np->selected    = NULL;
307     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
308     np->local_change = 0;
309     np->show_handles = show_handles;
310     np->helper_path = NULL;
311     np->helper_path_vec = new HelperPathList;
312     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
313     np->helperpath_width = 1.0;
314     np->curve = curve->copy();
315     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
316     if (SP_IS_LPE_ITEM(object)) {
317         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
318         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
319             np->show_helperpath = true;
320         }            
321     }
322     np->straight_path = false;
323     if (IS_LIVEPATHEFFECT(object) && item) {
324         np->item = item;
325     } else {
326         np->item = SP_ITEM(object);
327     }
329     // we need to update item's transform from the repr here,
330     // because they may be out of sync when we respond
331     // to a change in repr by regenerating nodepath     --bb
332     sp_object_read_attr(SP_OBJECT(np->item), "transform");
334     np->i2d  = from_2geom(sp_item_i2d_affine(np->item));
335     np->d2i  = np->i2d.inverse();
337     np->repr = repr;
338     if (repr_key_in) { // apparantly the object is an LPEObject
339         np->repr_key = g_strdup(repr_key_in);
340         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
341         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
342         if (lpeparam) {
343             lpeparam->param_setup_nodepath(np);
344         }
345     } else {
346         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
347         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
348             np->repr_key = g_strdup("inkscape:original-d");
350             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
351             if (lpe) {
352                 lpe->setup_nodepath(np);
353             }
354         } else {
355             np->repr_key = g_strdup("d");
356         }
357     }
359     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
360      * So for example a closed rectangle has a nodetypestring of length 5.
361      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
362     Geom::PathVector const &pathv = curve->get_pathvector();
363     guint length = curve->get_segment_count();
364     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
365         length += pit->empty() ? 0 : 1;
366     }
368     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
369     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
371     // create the subpath(s) from the bpath
372     subpaths_from_pathvector(np, pathv, typestr);
374     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
375     np->subpaths = g_list_reverse(np->subpaths);
377     delete[] typestr;
378     curve->unref();
380     // Draw helper curve
381     if (np->show_helperpath) {
382         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
383     }
385     sp_nodepath_create_helperpaths(np);
387     return np;
390 /**
391  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
392  */
393 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
395     if (!np)  //soft fail, like delete
396         return;
398     while (np->subpaths) {
399         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
400     }
402     //Inform the ShapeEditor that made me, if any, that I am gone.
403     if (np->shape_editor)
404         np->shape_editor->nodepath_destroyed();
406     g_assert(!np->selected);
408     if (np->helper_path) {
409         GtkObject *temp = np->helper_path;
410         np->helper_path = NULL;
411         gtk_object_destroy(temp);
412     }
413     if (np->curve) {
414         np->curve->unref();
415         np->curve = NULL;
416     }
418     if (np->repr_key) {
419         g_free(np->repr_key);
420         np->repr_key = NULL;
421     }
422     if (np->repr_nodetypes_key) {
423         g_free(np->repr_nodetypes_key);
424         np->repr_nodetypes_key = NULL;
425     }
427     sp_nodepath_destroy_helperpaths(np);
428     delete np->helper_path_vec;
429     np->helper_path_vec = NULL;
431     np->desktop = NULL;
433     g_free(np);
436 /**
437  *  Return the node count of a given NodeSubPath.
438  */
439 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
441     if (!subpath)
442         return 0;
443     gint nodeCount = g_list_length(subpath->nodes);
444     return nodeCount;
447 /**
448  *  Return the node count of a given NodePath.
449  */
450 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
452     if (!np)
453         return 0;
454     gint nodeCount = 0;
455     for (GList *item = np->subpaths ; item ; item=item->next) {
456        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
457         nodeCount += g_list_length(subpath->nodes);
458     }
459     return nodeCount;
462 /**
463  *  Return the subpath count of a given NodePath.
464  */
465 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
467     if (!np)
468         return 0;
469     return g_list_length (np->subpaths);
472 /**
473  *  Return the selected node count of a given NodePath.
474  */
475 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
477     if (!np)
478         return 0;
479     return g_list_length (np->selected);
482 /**
483  *  Return the number of subpaths where nodes are selected in a given NodePath.
484  */
485 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
487     if (!np)
488         return 0;
489     if (!np->selected)
490         return 0;
491     if (!np->selected->next)
492         return 1;
493     gint count = 0;
494     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
495         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
496         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
497             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
498             if (node->selected) {
499                 count ++;
500                 break;
501             }
502         }
503     }
504     return count;
507 /**
508  * Clean up a nodepath after editing.
509  *
510  * Currently we are deleting trivial subpaths.
511  */
512 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
514     GList *badSubPaths = NULL;
516     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
517     for (GList *l = nodepath->subpaths; l ; l=l->next) {
518        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
519        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
520             badSubPaths = g_list_append(badSubPaths, sp);
521     }
523     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
524     //also removes the subpath from nodepath->subpaths
525     for (GList *l = badSubPaths; l ; l=l->next) {
526        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
527         sp_nodepath_subpath_destroy(sp);
528     }
530     g_list_free(badSubPaths);
533 /**
534  * Create new nodepaths from pathvector, make it subpaths of np.
535  * \param t The node type array.
536  */
537 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
539     guint i = 0;  // index into node type array
540     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
541         if (pit->empty())
542             continue;  // don't add single knot paths
544         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
546         NR::Point ppos = from_2geom(pit->initialPoint()) * np->i2d;
547         NRPathcode pcode = NR_MOVETO;
549         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
550             add_curve_to_subpath(np, sp, *cit, t, i, ppos, pcode);
551         }
553         if (pit->closed()) {
554             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
555             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
556              * If the length is zero, don't add it to the nodepath. */
557             Geom::Curve const &closing_seg = pit->back_closed();
558             if ( ! closing_seg.isDegenerate() ) {
559                 NR::Point pos = from_2geom(closing_seg.finalPoint()) * np->i2d;
560                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
561             }
563             sp_nodepath_subpath_close(sp);
564         }
565     }
567 // should add initial point of curve with type of previous curve:
568 static void add_curve_to_subpath(Inkscape::NodePath::Path *np, Inkscape::NodePath::SubPath *sp, Geom::Curve const & c, Inkscape::NodePath::NodeType const *t, guint & i,
569                                  NR::Point & ppos, NRPathcode & pcode)
571     if( dynamic_cast<Geom::LineSegment const*>(&c) ||
572         dynamic_cast<Geom::HLineSegment const*>(&c) ||
573         dynamic_cast<Geom::VLineSegment const*>(&c) )
574     {
575         NR::Point pos = from_2geom(c.initialPoint()) * np->i2d;
576         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
577         ppos = from_2geom(c.finalPoint());
578         pcode = NR_LINETO;
579     }
580     else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&c)) {
581         std::vector<Geom::Point> points = cubic_bezier->points();
582         NR::Point pos = from_2geom(points[0]) * np->i2d;
583         NR::Point npos = from_2geom(points[1]) * np->i2d;
584         sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
585         ppos = from_2geom(points[2]) * np->i2d;
586         pcode = NR_CURVETO;
587     }
588     else {
589         //this case handles sbasis as well as all other curve types
590         Geom::Path sbasis_path = Geom::cubicbezierpath_from_sbasis(c.toSBasis(), 0.1);
592         for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) {
593             add_curve_to_subpath(np, sp, *iter, t, i, ppos, pcode);
594         }
595     }
599 /**
600  * Convert from sodipodi:nodetypes to new style type array.
601  */
602 static
603 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
605     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
607     guint pos = 0;
609     if (types) {
610         for (guint i = 0; types[i] && ( i < length ); i++) {
611             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
612             if (types[i] != '\0') {
613                 switch (types[i]) {
614                     case 's':
615                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
616                         break;
617                     case 'z':
618                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
619                         break;
620                     case 'c':
621                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
622                         break;
623                     default:
624                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
625                         break;
626                 }
627             }
628         }
629     }
631     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
633     return typestr;
636 /**
637  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
638  * updated but repr is not (for speed). Used during curve and node drag.
639  */
640 static void update_object(Inkscape::NodePath::Path *np)
642     g_assert(np);
644     np->curve->unref();
645     np->curve = create_curve(np);
647     sp_nodepath_set_curve(np, np->curve);
649     if (np->show_helperpath) {
650         SPCurve * helper_curve = np->curve->copy();
651         helper_curve->transform(to_2geom(np->i2d));
652         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
653         helper_curve->unref();
654     }
656     sp_nodepath_update_helperpaths(np);
658     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
659     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
660     np->shape_editor->update_knotholder();
663 /**
664  * Update XML path node with data from path object.
665  */
666 static void update_repr_internal(Inkscape::NodePath::Path *np)
668     g_assert(np);
670     Inkscape::XML::Node *repr = np->object->repr;
672     np->curve->unref();
673     np->curve = create_curve(np);
675     gchar *typestr = create_typestr(np);
676     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
678     // determine if path has an effect applied and write to correct "d" attribute.
679     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
680         np->local_change++;
681         repr->setAttribute(np->repr_key, svgpath);
682     }
684     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
685         np->local_change++;
686         repr->setAttribute(np->repr_nodetypes_key, typestr);
687     }
689     g_free(svgpath);
690     g_free(typestr);
692     if (np->show_helperpath) {
693         SPCurve * helper_curve = np->curve->copy();
694         helper_curve->transform(to_2geom(np->i2d));
695         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
696         helper_curve->unref();
697     }
699     // TODO: do we need this call here? after all, update_object() should have been called just before
700     //sp_nodepath_update_helperpaths(np);
703 /**
704  * Update XML path node with data from path object, commit changes forever.
705  */
706 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
708     //fixme: np can be NULL, so check before proceeding
709     g_return_if_fail(np != NULL);
711     update_repr_internal(np);
712     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
714     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
715                      annotation);
718 /**
719  * Update XML path node with data from path object, commit changes with undo.
720  */
721 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
723     update_repr_internal(np);
724     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
725                            annotation);
728 /**
729  * Make duplicate of path, replace corresponding XML node in tree, commit.
730  */
731 static void stamp_repr(Inkscape::NodePath::Path *np)
733     g_assert(np);
735     Inkscape::XML::Node *old_repr = np->object->repr;
736     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
738     // remember the position of the item
739     gint pos = old_repr->position();
740     // remember parent
741     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
743     SPCurve *curve = create_curve(np);
744     gchar *typestr = create_typestr(np);
746     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
748     new_repr->setAttribute(np->repr_key, svgpath);
749     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
751     // add the new repr to the parent
752     parent->appendChild(new_repr);
753     // move to the saved position
754     new_repr->setPosition(pos > 0 ? pos : 0);
756     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
757                      _("Stamp"));
759     Inkscape::GC::release(new_repr);
760     g_free(svgpath);
761     g_free(typestr);
762     curve->unref();
765 /**
766  * Create curve from path.
767  */
768 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
770     SPCurve *curve = new SPCurve();
772     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
773        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
774         curve->moveto(sp->first->pos * np->d2i);
775        Inkscape::NodePath::Node *n = sp->first->n.other;
776         while (n) {
777             NR::Point const end_pt = n->pos * np->d2i;
778             switch (n->code) {
779                 case NR_LINETO:
780                     curve->lineto(end_pt);
781                     break;
782                 case NR_CURVETO:
783                     curve->curveto(n->p.other->n.pos * np->d2i,
784                                      n->p.pos * np->d2i,
785                                      end_pt);
786                     break;
787                 default:
788                     g_assert_not_reached();
789                     break;
790             }
791             if (n != sp->last) {
792                 n = n->n.other;
793             } else {
794                 n = NULL;
795             }
796         }
797         if (sp->closed) {
798             curve->closepath();
799         }
800     }
802     return curve;
805 /**
806  * Convert path type string to sodipodi:nodetypes style.
807  */
808 static gchar *create_typestr(Inkscape::NodePath::Path *np)
810     gchar *typestr = g_new(gchar, 32);
811     gint len = 32;
812     gint pos = 0;
814     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
815        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
817         if (pos >= len) {
818             typestr = g_renew(gchar, typestr, len + 32);
819             len += 32;
820         }
822         typestr[pos++] = 'c';
824        Inkscape::NodePath::Node *n;
825         n = sp->first->n.other;
826         while (n) {
827             gchar code;
829             switch (n->type) {
830                 case Inkscape::NodePath::NODE_CUSP:
831                     code = 'c';
832                     break;
833                 case Inkscape::NodePath::NODE_SMOOTH:
834                     code = 's';
835                     break;
836                 case Inkscape::NodePath::NODE_SYMM:
837                     code = 'z';
838                     break;
839                 default:
840                     g_assert_not_reached();
841                     code = '\0';
842                     break;
843             }
845             if (pos >= len) {
846                 typestr = g_renew(gchar, typestr, len + 32);
847                 len += 32;
848             }
850             typestr[pos++] = code;
852             if (n != sp->last) {
853                 n = n->n.other;
854             } else {
855                 n = NULL;
856             }
857         }
858     }
860     if (pos >= len) {
861         typestr = g_renew(gchar, typestr, len + 1);
862         len += 1;
863     }
865     typestr[pos++] = '\0';
867     return typestr;
870 /**
871  * Returns current path in context. // later eliminate this function at all!
872  */
873 static Inkscape::NodePath::Path *sp_nodepath_current()
875     if (!SP_ACTIVE_DESKTOP) {
876         return NULL;
877     }
879     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
881     if (!SP_IS_NODE_CONTEXT(event_context)) {
882         return NULL;
883     }
885     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
890 /**
891  \brief Fills node and handle positions for three nodes, splitting line
892   marked by end at distance t.
893  */
894 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
896     g_assert(new_path != NULL);
897     g_assert(end      != NULL);
899     g_assert(end->p.other == new_path);
900    Inkscape::NodePath::Node *start = new_path->p.other;
901     g_assert(start);
903     if (end->code == NR_LINETO) {
904         new_path->type =Inkscape::NodePath::NODE_CUSP;
905         new_path->code = NR_LINETO;
906         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
907     } else {
908         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
909         new_path->code = NR_CURVETO;
910         gdouble s      = 1 - t;
911         for (int dim = 0; dim < 2; dim++) {
912             NR::Coord const f000 = start->pos[dim];
913             NR::Coord const f001 = start->n.pos[dim];
914             NR::Coord const f011 = end->p.pos[dim];
915             NR::Coord const f111 = end->pos[dim];
916             NR::Coord const f00t = s * f000 + t * f001;
917             NR::Coord const f01t = s * f001 + t * f011;
918             NR::Coord const f11t = s * f011 + t * f111;
919             NR::Coord const f0tt = s * f00t + t * f01t;
920             NR::Coord const f1tt = s * f01t + t * f11t;
921             NR::Coord const fttt = s * f0tt + t * f1tt;
922             start->n.pos[dim]    = f00t;
923             new_path->p.pos[dim] = f0tt;
924             new_path->pos[dim]   = fttt;
925             new_path->n.pos[dim] = f1tt;
926             end->p.pos[dim]      = f11t;
927         }
928     }
931 /**
932  * Adds new node on direct line between two nodes, activates handles of all
933  * three nodes.
934  */
935 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
937     g_assert(end);
938     g_assert(end->subpath);
939     g_assert(g_list_find(end->subpath->nodes, end));
941    Inkscape::NodePath::Node *start = end->p.other;
942     g_assert( start->n.other == end );
943    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
944                                                end,
945                                                (NRPathcode)end->code == NR_LINETO?
946                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
947                                                (NRPathcode)end->code,
948                                                &start->pos, &start->pos, &start->n.pos);
949     sp_nodepath_line_midpoint(newnode, end, t);
951     sp_node_adjust_handles(start);
952     sp_node_update_handles(start);
953     sp_node_update_handles(newnode);
954     sp_node_adjust_handles(end);
955     sp_node_update_handles(end);
957     return newnode;
960 /**
961 \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
962 */
963 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
965     g_assert(node);
966     g_assert(node->subpath);
967     g_assert(g_list_find(node->subpath->nodes, node));
969    Inkscape::NodePath::SubPath *sp = node->subpath;
970     Inkscape::NodePath::Path *np    = sp->nodepath;
972     if (sp->closed) {
973         sp_nodepath_subpath_open(sp, node);
974         return sp->first;
975     } else {
976         // no break for end nodes
977         if (node == sp->first) return NULL;
978         if (node == sp->last ) return NULL;
980         // create a new subpath
981        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
983         // duplicate the break node as start of the new subpath
984         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
986         // attach rest of curve to new node
987         g_assert(node->n.other);
988         newnode->n.other = node->n.other; node->n.other = NULL;
989         newnode->n.other->p.other = newnode;
990         newsubpath->last = sp->last;
991         sp->last = node;
992         node = newnode;
993         while (node->n.other) {
994             node = node->n.other;
995             node->subpath = newsubpath;
996             sp->nodes = g_list_remove(sp->nodes, node);
997             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
998         }
1001         return newnode;
1002     }
1005 /**
1006  * Duplicate node and connect to neighbours.
1007  */
1008 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
1010     g_assert(node);
1011     g_assert(node->subpath);
1012     g_assert(g_list_find(node->subpath->nodes, node));
1014    Inkscape::NodePath::SubPath *sp = node->subpath;
1016     NRPathcode code = (NRPathcode) node->code;
1017     if (code == NR_MOVETO) { // if node is the endnode,
1018         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1019     }
1021     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1023     if (!node->n.other || !node->p.other) // if node is an endnode, select it
1024         return node;
1025     else
1026         return newnode; // otherwise select the newly created node
1029 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1031     node->p.pos = (node->pos + (node->pos - node->n.pos));
1034 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1036     node->n.pos = (node->pos + (node->pos - node->p.pos));
1039 /**
1040  * Change line type at node, with side effects on neighbours.
1041  */
1042 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1044     g_assert(end);
1045     g_assert(end->subpath);
1046     g_assert(end->p.other);
1048     if (end->code == static_cast< guint > ( code ) )
1049         return;
1051    Inkscape::NodePath::Node *start = end->p.other;
1053     end->code = code;
1055     if (code == NR_LINETO) {
1056         if (start->code == NR_LINETO) {
1057             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
1058         }
1059         if (end->n.other) {
1060             if (end->n.other->code == NR_LINETO) {
1061                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
1062             }
1063         }
1064     } else {
1065         NR::Point delta = end->pos - start->pos;
1066         start->n.pos = start->pos + delta / 3;
1067         end->p.pos = end->pos - delta / 3;
1068         sp_node_adjust_handle(start, 1);
1069         sp_node_adjust_handle(end, -1);
1070     }
1072     sp_node_update_handles(start);
1073     sp_node_update_handles(end);
1076 /**
1077  * Change node type, and its handles accordingly.
1078  */
1079 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1081     g_assert(node);
1082     g_assert(node->subpath);
1084     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1085         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1086             type =Inkscape::NodePath::NODE_CUSP;
1087         }
1088     }
1090     node->type = type;
1092     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1093         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1094         node->knot->setSize (node->selected? 11 : 9);
1095         sp_knot_update_ctrl(node->knot);
1096     } else {
1097         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1098         node->knot->setSize (node->selected? 9 : 7);
1099         sp_knot_update_ctrl(node->knot);
1100     }
1102     // if one of handles is mouseovered, preserve its position
1103     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1104         sp_node_adjust_handle(node, 1);
1105     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1106         sp_node_adjust_handle(node, -1);
1107     } else {
1108         sp_node_adjust_handles(node);
1109     }
1111     sp_node_update_handles(node);
1113     sp_nodepath_update_statusbar(node->subpath->nodepath);
1115     return node;
1118 bool
1119 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1121         Inkscape::NodePath::Node *othernode = side->other;
1122         if (!othernode)
1123             return false;
1124         NRPathcode const code = sp_node_path_code_from_side(node, side);
1125         if (code == NR_LINETO)
1126             return true;
1127         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1128         if (&node->p == side) {
1129             other_to_me = &othernode->n;
1130         } else if (&node->n == side) {
1131             other_to_me = &othernode->p;
1132         } 
1133         if (!other_to_me)
1134             return false;
1135         bool is_line = 
1136              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1137               NR::L2(node->pos - side->pos) < 1e-6);
1138         return is_line;
1141 /**
1142  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1143  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1144  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1145  * If already cusp and set to cusp, retracts handles.
1146 */
1147 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1149     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1151 /* 
1152   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1153  
1154         if (two_handles) {
1155             // do nothing, adjust_handles called via set_node_type will line them up
1156         } else if (one_handle) {
1157             if (opposite_to_handle_is_line) {
1158                 if (lined_up) {
1159                     // already half-smooth; pull opposite handle too making it fully smooth
1160                 } else {
1161                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1162                 }
1163             } else {
1164                 // pull opposite handle in line with the existing one
1165             }
1166         } else if (no_handles) {
1167             if (both_segments_are_lines OR both_segments_are_curves) {
1168                 //pull both handles
1169             } else {
1170                 // pull the handle opposite to line segment, making node half-smooth
1171             }
1172         }
1173 */
1174         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1175         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1176         bool p_is_line = sp_node_side_is_line(node, &node->p);
1177         bool n_is_line = sp_node_side_is_line(node, &node->n);
1179         if (p_has_handle && n_has_handle) {
1180             // do nothing, adjust_handles will line them up
1181         } else if (p_has_handle || n_has_handle) {
1182             if (p_has_handle && n_is_line) {
1183                 Radial line (node->n.other->pos - node->pos);
1184                 Radial handle (node->pos - node->p.pos);
1185                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1186                     // already half-smooth; pull opposite handle too making it fully smooth
1187                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1188                 } else {
1189                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1190                 }
1191             } else if (n_has_handle && p_is_line) {
1192                 Radial line (node->p.other->pos - node->pos);
1193                 Radial handle (node->pos - node->n.pos);
1194                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1195                     // already half-smooth; pull opposite handle too making it fully smooth
1196                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1197                 } else {
1198                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1199                 }
1200             } else if (p_has_handle && node->n.other) {
1201                 // pull n handle
1202                 node->n.other->code = NR_CURVETO;
1203                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1204                     NR::L2(node->p.pos - node->pos) :
1205                     NR::L2(node->n.other->pos - node->pos) / 3;
1206                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1207             } else if (n_has_handle && node->p.other) {
1208                 // pull p handle
1209                 node->code = NR_CURVETO;
1210                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1211                     NR::L2(node->n.pos - node->pos) :
1212                     NR::L2(node->p.other->pos - node->pos) / 3;
1213                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1214             }
1215         } else if (!p_has_handle && !n_has_handle) {
1216             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1217                 // no handles, but both segments are either lnes or curves:
1218                 //pull both handles
1220                 // convert both to curves:
1221                 node->code = NR_CURVETO;
1222                 node->n.other->code = NR_CURVETO;
1224                 NR::Point leg_prev = node->pos - node->p.other->pos;
1225                 NR::Point leg_next = node->pos - node->n.other->pos;
1227                 double norm_leg_prev = L2(leg_prev);
1228                 double norm_leg_next = L2(leg_next);
1230                 NR::Point delta;
1231                 if (norm_leg_next > 0.0) {
1232                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1233                     (&delta)->normalize();
1234                 }
1236                 if (type == Inkscape::NodePath::NODE_SYMM) {
1237                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1238                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1239                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1240                 } else {
1241                     // length of handle is proportional to distance to adjacent node
1242                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1243                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1244                 }
1246             } else {
1247                 // pull the handle opposite to line segment, making it half-smooth
1248                 if (p_is_line && node->n.other) {
1249                     if (type != Inkscape::NodePath::NODE_SYMM) {
1250                         // pull n handle
1251                         node->n.other->code = NR_CURVETO;
1252                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1253                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1254                     }
1255                 } else if (n_is_line && node->p.other) {
1256                     if (type != Inkscape::NodePath::NODE_SYMM) {
1257                         // pull p handle
1258                         node->code = NR_CURVETO;
1259                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1260                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1261                     }
1262                 }
1263             }
1264         }
1265     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1266         // cusping a cusp: retract nodes
1267         node->p.pos = node->pos;
1268         node->n.pos = node->pos;
1269     }
1271     sp_nodepath_set_node_type (node, type);
1274 /**
1275  * Move node to point, and adjust its and neighbouring handles.
1276  */
1277 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1279     NR::Point delta = p - node->pos;
1280     node->pos = p;
1282     node->p.pos += delta;
1283     node->n.pos += delta;
1285     Inkscape::NodePath::Node *node_p = NULL;
1286     Inkscape::NodePath::Node *node_n = NULL;
1288     if (node->p.other) {
1289         if (node->code == NR_LINETO) {
1290             sp_node_adjust_handle(node, 1);
1291             sp_node_adjust_handle(node->p.other, -1);
1292             node_p = node->p.other;
1293         }
1294     }
1295     if (node->n.other) {
1296         if (node->n.other->code == NR_LINETO) {
1297             sp_node_adjust_handle(node, -1);
1298             sp_node_adjust_handle(node->n.other, 1);
1299             node_n = node->n.other;
1300         }
1301     }
1303     // this function is only called from batch movers that will update display at the end
1304     // themselves, so here we just move all the knots without emitting move signals, for speed
1305     sp_node_update_handles(node, false);
1306     if (node_n) {
1307         sp_node_update_handles(node_n, false);
1308     }
1309     if (node_p) {
1310         sp_node_update_handles(node_p, false);
1311     }
1314 /**
1315  * Call sp_node_moveto() for node selection and handle possible snapping.
1316  */
1317 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1318                                             bool const snap, bool constrained = false, 
1319                                             Inkscape::Snapper::ConstraintLine const &constraint = NR::Point())
1321     NR::Coord best = NR_HUGE;
1322     NR::Point delta(dx, dy);
1323     NR::Point best_pt = delta;
1324     Inkscape::SnappedPoint best_abs;
1325     
1326     if (snap) {    
1327         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1328          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1329          * must provide that information. */
1330           
1331         // Build a list of the unselected nodes to which the snapper should snap 
1332         std::vector<NR::Point> unselected_nodes;
1333         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1334             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1335             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1336                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1337                 if (!node->selected) {
1338                     unselected_nodes.push_back(node->pos);
1339                 }    
1340             }
1341         }        
1342         
1343         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1344         
1345         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1346             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1347             m.setup(NULL, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1348             Inkscape::SnappedPoint s;
1349             if (constrained) {
1350                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1351                 dedicated_constraint.setPoint(n->pos);
1352                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, dedicated_constraint);
1353             } else {
1354                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);
1355             }            
1356             if (s.getSnapped() && (s.getDistance() < best)) {
1357                 best = s.getDistance();
1358                 best_abs = s;
1359                 best_pt = s.getPoint() - n->pos;
1360             }
1361         }
1362                         
1363         if (best_abs.getSnapped()) {
1364             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1365         } else {
1366             nodepath->desktop->snapindicator->remove_snappoint();    
1367         }
1368     }
1370     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1371         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1372         sp_node_moveto(n, n->pos + best_pt);
1373     }
1375     // do not update repr here so that node dragging is acceptably fast
1376     update_object(nodepath);
1379 /**
1380 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1381 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1382 near x = 0.
1383  */
1384 double
1385 sculpt_profile (double x, double alpha, guint profile)
1387     if (x >= 1)
1388         return 0;
1389     if (x <= 0)
1390         return 1;
1392     switch (profile) {
1393         case SCULPT_PROFILE_LINEAR:
1394         return 1 - x;
1395         case SCULPT_PROFILE_BELL:
1396         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1397         case SCULPT_PROFILE_ELLIPTIC:
1398         return sqrt(1 - x*x);
1399     }
1401     return 1;
1404 double
1405 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1407     // extremely primitive for now, don't have time to look for the real one
1408     double lower = NR::L2(b - a);
1409     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1410     return (lower + upper)/2;
1413 void
1414 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1416     n->pos = n->origin + delta;
1417     n->n.pos = n->n.origin + delta_n;
1418     n->p.pos = n->p.origin + delta_p;
1419     sp_node_adjust_handles(n);
1420     sp_node_update_handles(n, false);
1423 /**
1424  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1425  * on how far they are from the dragged node n.
1426  */
1427 static void
1428 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1430     g_assert (n);
1431     g_assert (nodepath);
1432     g_assert (n->subpath->nodepath == nodepath);
1434     double pressure = n->knot->pressure;
1435     if (pressure == 0)
1436         pressure = 0.5; // default
1437     pressure = CLAMP (pressure, 0.2, 0.8);
1439     // map pressure to alpha = 1/5 ... 5
1440     double alpha = 1 - 2 * fabs(pressure - 0.5);
1441     if (pressure > 0.5)
1442         alpha = 1/alpha;
1444     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1446     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1447         // Only one subpath has selected nodes:
1448         // use linear mode, where the distance from n to node being dragged is calculated along the path
1450         double n_sel_range = 0, p_sel_range = 0;
1451         guint n_nodes = 0, p_nodes = 0;
1452         guint n_sel_nodes = 0, p_sel_nodes = 0;
1454         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1455         {
1456             double n_range = 0, p_range = 0;
1457             bool n_going = true, p_going = true;
1458             Inkscape::NodePath::Node *n_node = n;
1459             Inkscape::NodePath::Node *p_node = n;
1460             do {
1461                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1462                 if (n_node && n_going)
1463                     n_node = n_node->n.other;
1464                 if (n_node == NULL) {
1465                     n_going = false;
1466                 } else {
1467                     n_nodes ++;
1468                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1469                     if (n_node->selected) {
1470                         n_sel_nodes ++;
1471                         n_sel_range = n_range;
1472                     }
1473                     if (n_node == p_node) {
1474                         n_going = false;
1475                         p_going = false;
1476                     }
1477                 }
1478                 if (p_node && p_going)
1479                     p_node = p_node->p.other;
1480                 if (p_node == NULL) {
1481                     p_going = false;
1482                 } else {
1483                     p_nodes ++;
1484                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1485                     if (p_node->selected) {
1486                         p_sel_nodes ++;
1487                         p_sel_range = p_range;
1488                     }
1489                     if (p_node == n_node) {
1490                         n_going = false;
1491                         p_going = false;
1492                     }
1493                 }
1494             } while (n_going || p_going);
1495         }
1497         // Second pass: actually move nodes in this subpath
1498         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1499         {
1500             double n_range = 0, p_range = 0;
1501             bool n_going = true, p_going = true;
1502             Inkscape::NodePath::Node *n_node = n;
1503             Inkscape::NodePath::Node *p_node = n;
1504             do {
1505                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1506                 if (n_node && n_going)
1507                     n_node = n_node->n.other;
1508                 if (n_node == NULL) {
1509                     n_going = false;
1510                 } else {
1511                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1512                     if (n_node->selected) {
1513                         sp_nodepath_move_node_and_handles (n_node,
1514                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1515                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1516                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1517                     }
1518                     if (n_node == p_node) {
1519                         n_going = false;
1520                         p_going = false;
1521                     }
1522                 }
1523                 if (p_node && p_going)
1524                     p_node = p_node->p.other;
1525                 if (p_node == NULL) {
1526                     p_going = false;
1527                 } else {
1528                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1529                     if (p_node->selected) {
1530                         sp_nodepath_move_node_and_handles (p_node,
1531                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1532                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1533                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1534                     }
1535                     if (p_node == n_node) {
1536                         n_going = false;
1537                         p_going = false;
1538                     }
1539                 }
1540             } while (n_going || p_going);
1541         }
1543     } else {
1544         // Multiple subpaths have selected nodes:
1545         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1546         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1547         // fix the pear-like shape when sculpting e.g. a ring
1549         // First pass: calculate range
1550         gdouble direct_range = 0;
1551         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1552             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1553             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1554                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1555                 if (node->selected) {
1556                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1557                 }
1558             }
1559         }
1561         // Second pass: actually move nodes
1562         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1563             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1564             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1565                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1566                 if (node->selected) {
1567                     if (direct_range > 1e-6) {
1568                         sp_nodepath_move_node_and_handles (node,
1569                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1570                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1571                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1572                     } else {
1573                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1574                     }
1576                 }
1577             }
1578         }
1579     }
1581     // do not update repr here so that node dragging is acceptably fast
1582     update_object(nodepath);
1586 /**
1587  * Move node selection to point, adjust its and neighbouring handles,
1588  * handle possible snapping, and commit the change with possible undo.
1589  */
1590 void
1591 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1593     if (!nodepath) return;
1595     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1597     if (dx == 0) {
1598         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1599     } else if (dy == 0) {
1600         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1601     } else {
1602         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1603     }
1606 /**
1607  * Move node selection off screen and commit the change.
1608  */
1609 void
1610 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1612     // borrowed from sp_selection_move_screen in selection-chemistry.c
1613     // we find out the current zoom factor and divide deltas by it
1614     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1616     gdouble zoom = desktop->current_zoom();
1617     gdouble zdx = dx / zoom;
1618     gdouble zdy = dy / zoom;
1620     if (!nodepath) return;
1622     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1624     if (dx == 0) {
1625         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1626     } else if (dy == 0) {
1627         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1628     } else {
1629         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1630     }
1633 /**
1634  * Move selected nodes to the absolute position given
1635  */
1636 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1638     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1639         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1640         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1641         sp_node_moveto(n, npos);
1642     }
1644     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1647 /**
1648  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1649  */
1650 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1652     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1653     g_return_val_if_fail(nodepath->selected, no_coord);
1655     // determine coordinate of first selected node
1656     GList *nsel = nodepath->selected;
1657     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1658     NR::Coord coord = n->pos[axis];
1659     bool coincide = true;
1661     // compare it to the coordinates of all the other selected nodes
1662     for (GList *l = nsel->next; l != NULL; l = l->next) {
1663         n = (Inkscape::NodePath::Node *) l->data;
1664         if (n->pos[axis] != coord) {
1665             coincide = false;
1666         }
1667     }
1668     if (coincide) {
1669         return coord;
1670     } else {
1671         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1672         // currently we return the coordinate of the bounding box midpoint because I don't know how
1673         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1674         return bbox.midpoint()[axis];
1675     }
1678 /** If they don't yet exist, creates knot and line for the given side of the node */
1679 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1681     if (!side->knot) {
1682         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"));
1684         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1685         side->knot->setSize (7);
1686         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1687         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1688         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1689         sp_knot_update_ctrl(side->knot);
1691         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1692         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1693         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1694         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1695         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1696         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1697     }
1699     if (!side->line) {
1700         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1701                                         SP_TYPE_CTRLLINE, NULL);
1702     }
1705 /**
1706  * Ensure the given handle of the node is visible/invisible, update its screen position
1707  */
1708 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1710     g_assert(node != NULL);
1712    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1713     NRPathcode code = sp_node_path_code_from_side(node, side);
1715     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1717     if (show_handle) {
1718         if (!side->knot) { // No handle knot at all
1719             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1720             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1721             side->knot->pos = side->pos;
1722             if (side->knot->item)
1723                 SP_CTRL(side->knot->item)->moveto(side->pos);
1724             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1725             sp_knot_show(side->knot);
1726         } else {
1727             if (side->knot->pos != side->pos) { // only if it's really moved
1728                 if (fire_move_signals) {
1729                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1730                 } else {
1731                     sp_knot_moveto(side->knot, &side->pos);
1732                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1733                 }
1734             }
1735             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1736                 sp_knot_show(side->knot);
1737             }
1738         }
1739         sp_canvas_item_show(side->line);
1740     } else {
1741         if (side->knot) {
1742             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1743                 sp_knot_hide(side->knot);
1744             }
1745         }
1746         if (side->line) {
1747             sp_canvas_item_hide(side->line);
1748         }
1749     }
1752 /**
1753  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1754  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1755  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1756  * updated; otherwise, just move the knots silently (used in batch moves).
1757  */
1758 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1760     g_assert(node != NULL);
1762     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1763         sp_knot_show(node->knot);
1764     }
1766     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1767         if (fire_move_signals)
1768             sp_knot_set_position(node->knot, &node->pos, 0);
1769         else
1770             sp_knot_moveto(node->knot, &node->pos);
1771     }
1773     gboolean show_handles = node->selected;
1774     if (node->p.other != NULL) {
1775         if (node->p.other->selected) show_handles = TRUE;
1776     }
1777     if (node->n.other != NULL) {
1778         if (node->n.other->selected) show_handles = TRUE;
1779     }
1781     if (node->subpath->nodepath->show_handles == false)
1782         show_handles = FALSE;
1784     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1785     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1788 /**
1789  * Call sp_node_update_handles() for all nodes on subpath.
1790  */
1791 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1793     g_assert(subpath != NULL);
1795     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1796         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1797     }
1800 /**
1801  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1802  */
1803 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1805     g_assert(nodepath != NULL);
1807     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1808         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1809     }
1812 void
1813 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1815     if (nodepath == NULL) return;
1817     nodepath->show_handles = show;
1818     sp_nodepath_update_handles(nodepath);
1821 /**
1822  * Adds all selected nodes in nodepath to list.
1823  */
1824 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1826     StlConv<Node *>::list(l, selected);
1827 /// \todo this adds a copying, rework when the selection becomes a stl list
1830 /**
1831  * Align selected nodes on the specified axis.
1832  */
1833 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1835     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1836         return;
1837     }
1839     if ( !nodepath->selected->next ) { // only one node selected
1840         return;
1841     }
1842    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1843     NR::Point dest(pNode->pos);
1844     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1845         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1846         if (pNode) {
1847             dest[axis] = pNode->pos[axis];
1848             sp_node_moveto(pNode, dest);
1849         }
1850     }
1852     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1855 /// Helper struct.
1856 struct NodeSort
1858    Inkscape::NodePath::Node *_node;
1859     NR::Coord _coord;
1860     /// \todo use vectorof pointers instead of calling copy ctor
1861     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1862         _node(node), _coord(node->pos[axis])
1863     {}
1865 };
1867 static bool operator<(NodeSort const &a, NodeSort const &b)
1869     return (a._coord < b._coord);
1872 /**
1873  * Distribute selected nodes on the specified axis.
1874  */
1875 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1877     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1878         return;
1879     }
1881     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1882         return;
1883     }
1885    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1886     std::vector<NodeSort> sorted;
1887     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1888         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1889         if (pNode) {
1890             NodeSort n(pNode, axis);
1891             sorted.push_back(n);
1892             //dest[axis] = pNode->pos[axis];
1893             //sp_node_moveto(pNode, dest);
1894         }
1895     }
1896     std::sort(sorted.begin(), sorted.end());
1897     unsigned int len = sorted.size();
1898     //overall bboxes span
1899     float dist = (sorted.back()._coord -
1900                   sorted.front()._coord);
1901     //new distance between each bbox
1902     float step = (dist) / (len - 1);
1903     float pos = sorted.front()._coord;
1904     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1905           it < sorted.end();
1906           it ++ )
1907     {
1908         NR::Point dest((*it)._node->pos);
1909         dest[axis] = pos;
1910         sp_node_moveto((*it)._node, dest);
1911         pos += step;
1912     }
1914     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1918 /**
1919  * Call sp_nodepath_line_add_node() for all selected segments.
1920  */
1921 void
1922 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1924     if (!nodepath) {
1925         return;
1926     }
1928     GList *nl = NULL;
1930     int n_added = 0;
1932     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1933        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1934         g_assert(t->selected);
1935         if (t->p.other && t->p.other->selected) {
1936             nl = g_list_prepend(nl, t);
1937         }
1938     }
1940     while (nl) {
1941        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1942        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1943        sp_nodepath_node_select(n, TRUE, FALSE);
1944        n_added ++;
1945        nl = g_list_remove(nl, t);
1946     }
1948     /** \todo fixme: adjust ? */
1949     sp_nodepath_update_handles(nodepath);
1951     if (n_added > 1) {
1952         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1953     } else if (n_added > 0) {
1954         sp_nodepath_update_repr(nodepath, _("Add node"));
1955     }
1957     sp_nodepath_update_statusbar(nodepath);
1960 /**
1961  * Select segment nearest to point
1962  */
1963 void
1964 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1966     if (!nodepath) {
1967         return;
1968     }
1970     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1971     Geom::PathVector const &pathv = curve->get_pathvector();
1972     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1974     // calculate index for nodepath's representation.
1975     unsigned int segment_index = floor(pvpos.t) + 1;
1976     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1977         segment_index += pathv[i].size() + 1;
1978         if (pathv[i].closed()) {
1979             segment_index += 1;
1980         }
1981     }
1983     curve->unref();
1985     //find segment to segment
1986     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(segment_index);
1988     //fixme: this can return NULL, so check before proceeding.
1989     g_return_if_fail(e != NULL);
1991     gboolean force = FALSE;
1992     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1993         force = TRUE;
1994     }
1995     sp_nodepath_node_select(e, (gboolean) toggle, force);
1996     if (e->p.other)
1997         sp_nodepath_node_select(e->p.other, TRUE, force);
1999     sp_nodepath_update_handles(nodepath);
2001     sp_nodepath_update_statusbar(nodepath);
2004 /**
2005  * Add a node nearest to point
2006  */
2007 void
2008 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
2010     if (!nodepath) {
2011         return;
2012     }
2014     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2015     Geom::PathVector const &pathv = curve->get_pathvector();
2016     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
2018     // calculate index for nodepath's representation.
2019     double int_part;
2020     double t = std::modf(pvpos.t, &int_part);
2021     unsigned int segment_index = (unsigned int)int_part + 1;
2022     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
2023         segment_index += pathv[i].size() + 1;
2024         if (pathv[i].closed()) {
2025             segment_index += 1;
2026         }
2027     }
2029     curve->unref();
2031     //find segment to split
2032     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(segment_index);
2034     //don't know why but t seems to flip for lines
2035     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2036         t = 1.0 - t;
2037     }
2039     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2040     sp_nodepath_node_select(n, FALSE, TRUE);
2042     /* fixme: adjust ? */
2043     sp_nodepath_update_handles(nodepath);
2045     sp_nodepath_update_repr(nodepath, _("Add node"));
2047     sp_nodepath_update_statusbar(nodepath);
2050 /*
2051  * Adjusts a segment so that t moves by a certain delta for dragging
2052  * converts lines to curves
2053  *
2054  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2055  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2056  */
2057 void
2058 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
2060     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
2062     //fixme: e and e->p can be NULL, so check for those before proceeding
2063     g_return_if_fail(e != NULL);
2064     g_return_if_fail(&e->p != NULL);
2066     /* feel good is an arbitrary parameter that distributes the delta between handles
2067      * if t of the drag point is less than 1/6 distance form the endpoint only
2068      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2069      */
2070     double feel_good;
2071     if (t <= 1.0 / 6.0)
2072         feel_good = 0;
2073     else if (t <= 0.5)
2074         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2075     else if (t <= 5.0 / 6.0)
2076         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2077     else
2078         feel_good = 1;
2080     //if we're dragging a line convert it to a curve
2081     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2082         sp_nodepath_set_line_type(e, NR_CURVETO);
2083     }
2085     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2086     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2087     e->p.other->n.pos += offsetcoord0;
2088     e->p.pos += offsetcoord1;
2090     // adjust handles of adjacent nodes where necessary
2091     sp_node_adjust_handle(e,1);
2092     sp_node_adjust_handle(e->p.other,-1);
2094     sp_nodepath_update_handles(e->subpath->nodepath);
2096     update_object(e->subpath->nodepath);
2098     sp_nodepath_update_statusbar(e->subpath->nodepath);
2102 /**
2103  * Call sp_nodepath_break() for all selected segments.
2104  */
2105 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2107     if (!nodepath) return;
2109     GList *tempin = g_list_copy(nodepath->selected);
2110     GList *temp = NULL;
2111     for (GList *l = tempin; l != NULL; l = l->next) {
2112        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2113        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2114         if (nn == NULL) continue; // no break, no new node
2115         temp = g_list_prepend(temp, nn);
2116     }
2117     g_list_free(tempin);
2119     if (temp) {
2120         sp_nodepath_deselect(nodepath);
2121     }
2122     for (GList *l = temp; l != NULL; l = l->next) {
2123         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2124     }
2126     sp_nodepath_update_handles(nodepath);
2128     sp_nodepath_update_repr(nodepath, _("Break path"));
2131 /**
2132  * Duplicate the selected node(s).
2133  */
2134 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2136     if (!nodepath) {
2137         return;
2138     }
2140     GList *temp = NULL;
2141     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2142        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2143        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2144         if (nn == NULL) continue; // could not duplicate
2145         temp = g_list_prepend(temp, nn);
2146     }
2148     if (temp) {
2149         sp_nodepath_deselect(nodepath);
2150     }
2151     for (GList *l = temp; l != NULL; l = l->next) {
2152         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2153     }
2155     sp_nodepath_update_handles(nodepath);
2157     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2160 /**
2161  *  Internal function to join two nodes by merging them into one.
2162  */
2163 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2165     /* a and b are endpoints */
2167     // if one of the two nodes is mouseovered, fix its position
2168     NR::Point c;
2169     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2170         c = a->pos;
2171     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2172         c = b->pos;
2173     } else {
2174         // otherwise, move joined node to the midpoint
2175         c = (a->pos + b->pos) / 2;
2176     }
2178     if (a->subpath == b->subpath) {
2179        Inkscape::NodePath::SubPath *sp = a->subpath;
2180         sp_nodepath_subpath_close(sp);
2181         sp_node_moveto (sp->first, c);
2183         sp_nodepath_update_handles(sp->nodepath);
2184         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2185         return;
2186     }
2188     /* a and b are separate subpaths */
2189     Inkscape::NodePath::SubPath *sa = a->subpath;
2190     Inkscape::NodePath::SubPath *sb = b->subpath;
2191     NR::Point p;
2192     Inkscape::NodePath::Node *n;
2193     NRPathcode code;
2194     if (a == sa->first) {
2195         // we will now reverse sa, so that a is its last node, not first, and drop that node
2196         p = sa->first->n.pos;
2197         code = (NRPathcode)sa->first->n.other->code;
2198         // create new subpath
2199        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2200        // create a first moveto node on it
2201         n = sa->last;
2202         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2203         n = n->p.other;
2204         if (n == sa->first) n = NULL;
2205         while (n) {
2206             // copy the rest of the nodes from sa to t, going backwards
2207             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2208             n = n->p.other;
2209             if (n == sa->first) n = NULL;
2210         }
2211         // replace sa with t
2212         sp_nodepath_subpath_destroy(sa);
2213         sa = t;
2214     } else if (a == sa->last) {
2215         // a is already last, just drop it
2216         p = sa->last->p.pos;
2217         code = (NRPathcode)sa->last->code;
2218         sp_nodepath_node_destroy(sa->last);
2219     } else {
2220         code = NR_END;
2221         g_assert_not_reached();
2222     }
2224     if (b == sb->first) {
2225         // copy all nodes from b to a, forward 
2226         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2227         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2228             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2229         }
2230     } else if (b == sb->last) {
2231         // copy all nodes from b to a, backward 
2232         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2233         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2234             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2235         }
2236     } else {
2237         g_assert_not_reached();
2238     }
2239     /* and now destroy sb */
2241     sp_nodepath_subpath_destroy(sb);
2243     sp_nodepath_update_handles(sa->nodepath);
2245     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2247     sp_nodepath_update_statusbar(nodepath);
2250 /**
2251  *  Internal function to join two nodes by adding a segment between them.
2252  */
2253 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2255     if (a->subpath == b->subpath) {
2256        Inkscape::NodePath::SubPath *sp = a->subpath;
2258         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2259         sp->closed = TRUE;
2261         sp->first->p.other = sp->last;
2262         sp->last->n.other  = sp->first;
2264         sp_node_handle_mirror_p_to_n(sp->last);
2265         sp_node_handle_mirror_n_to_p(sp->first);
2267         sp->first->code = sp->last->code;
2268         sp->first       = sp->last;
2270         sp_nodepath_update_handles(sp->nodepath);
2272         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2274         return;
2275     }
2277     /* a and b are separate subpaths */
2278     Inkscape::NodePath::SubPath *sa = a->subpath;
2279     Inkscape::NodePath::SubPath *sb = b->subpath;
2281     Inkscape::NodePath::Node *n;
2282     NR::Point p;
2283     NRPathcode code;
2284     if (a == sa->first) {
2285         code = (NRPathcode) sa->first->n.other->code;
2286        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2287         n = sa->last;
2288         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2289         for (n = n->p.other; n != NULL; n = n->p.other) {
2290             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2291         }
2292         sp_nodepath_subpath_destroy(sa);
2293         sa = t;
2294     } else if (a == sa->last) {
2295         code = (NRPathcode)sa->last->code;
2296     } else {
2297         code = NR_END;
2298         g_assert_not_reached();
2299     }
2301     if (b == sb->first) {
2302         n = sb->first;
2303         sp_node_handle_mirror_p_to_n(sa->last);
2304         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2305         sp_node_handle_mirror_n_to_p(sa->last);
2306         for (n = n->n.other; n != NULL; n = n->n.other) {
2307             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2308         }
2309     } else if (b == sb->last) {
2310         n = sb->last;
2311         sp_node_handle_mirror_p_to_n(sa->last);
2312         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2313         sp_node_handle_mirror_n_to_p(sa->last);
2314         for (n = n->p.other; n != NULL; n = n->p.other) {
2315             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2316         }
2317     } else {
2318         g_assert_not_reached();
2319     }
2320     /* and now destroy sb */
2322     sp_nodepath_subpath_destroy(sb);
2324     sp_nodepath_update_handles(sa->nodepath);
2326     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2329 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2331 /**
2332  * Internal function to handle joining two nodes.
2333  */
2334 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2336     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2338     if (g_list_length(nodepath->selected) != 2) {
2339         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2340         return;
2341     }
2343     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2344     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2346     g_assert(a != b);
2347     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2348         // someone tried to join an orphan node (i.e. a single-node subpath).
2349         // this is not worth an error message, just fail silently.
2350         return;
2351     }
2353     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2354         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2355         return;
2356     }
2358     switch(mode) {
2359         case NODE_JOIN_ENDPOINTS:
2360             do_node_selected_join(nodepath, a, b);
2361             break;
2362         case NODE_JOIN_SEGMENT:
2363             do_node_selected_join_segment(nodepath, a, b);
2364             break;
2365     }
2368 /**
2369  *  Join two nodes by merging them into one.
2370  */
2371 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2373     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2376 /**
2377  *  Join two nodes by adding a segment between them.
2378  */
2379 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2381     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2384 /**
2385  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2386  */
2387 void sp_node_delete_preserve(GList *nodes_to_delete)
2389     GSList *nodepaths = NULL;
2391     while (nodes_to_delete) {
2392         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2393         Inkscape::NodePath::SubPath *sp = node->subpath;
2394         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2395         Inkscape::NodePath::Node *sample_cursor = NULL;
2396         Inkscape::NodePath::Node *sample_end = NULL;
2397         Inkscape::NodePath::Node *delete_cursor = node;
2398         bool just_delete = false;
2400         //find the start of this contiguous selection
2401         //move left to the first node that is not selected
2402         //or the start of the non-closed path
2403         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2404             delete_cursor = curr;
2405         }
2407         //just delete at the beginning of an open path
2408         if (!delete_cursor->p.other) {
2409             sample_cursor = delete_cursor;
2410             just_delete = true;
2411         } else {
2412             sample_cursor = delete_cursor->p.other;
2413         }
2415         //calculate points for each segment
2416         int rate = 5;
2417         float period = 1.0 / rate;
2418         std::vector<NR::Point> data;
2419         if (!just_delete) {
2420             data.push_back(sample_cursor->pos);
2421             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2422                 //just delete at the end of an open path
2423                 if (!sp->closed && curr == sp->last) {
2424                     just_delete = true;
2425                     break;
2426                 }
2428                 //sample points on the contiguous selected segment
2429                 NR::Point *bez;
2430                 bez = new NR::Point [4];
2431                 bez[0] = curr->pos;
2432                 bez[1] = curr->n.pos;
2433                 bez[2] = curr->n.other->p.pos;
2434                 bez[3] = curr->n.other->pos;
2435                 for (int i=1; i<rate; i++) {
2436                     gdouble t = i * period;
2437                     NR::Point p = bezier_pt(3, bez, t);
2438                     data.push_back(p);
2439                 }
2440                 data.push_back(curr->n.other->pos);
2442                 sample_end = curr->n.other;
2443                 //break if we've come full circle or hit the end of the selection
2444                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2445                     break;
2446                 }
2447             }
2448         }
2450         if (!just_delete) {
2451             //calculate the best fitting single segment and adjust the endpoints
2452             NR::Point *adata;
2453             adata = new NR::Point [data.size()];
2454             copy(data.begin(), data.end(), adata);
2456             NR::Point *bez;
2457             bez = new NR::Point [4];
2458             //would decreasing error create a better fitting approximation?
2459             gdouble error = 1.0;
2460             gint ret;
2461             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2463             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2464             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2465             //the resulting nodes behave as expected.
2466             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2467                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2468             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2469                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2471             //adjust endpoints
2472             sample_cursor->n.pos = bez[1];
2473             sample_end->p.pos = bez[2];
2474         }
2476         //destroy this contiguous selection
2477         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2478             Inkscape::NodePath::Node *temp = delete_cursor;
2479             if (delete_cursor->n.other == delete_cursor) {
2480                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2481                 delete_cursor = NULL;
2482             } else {
2483                 delete_cursor = delete_cursor->n.other;
2484             }
2485             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2486             sp_nodepath_node_destroy(temp);
2487         }
2489         sp_nodepath_update_handles(nodepath);
2491         if (!g_slist_find(nodepaths, nodepath))
2492             nodepaths = g_slist_prepend (nodepaths, nodepath);
2493     }
2495     for (GSList *i = nodepaths; i; i = i->next) {
2496         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2497         // different nodepaths will give us one undo event per nodepath
2498         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2500         // if the entire nodepath is removed, delete the selected object.
2501         if (nodepath->subpaths == NULL ||
2502             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2503             //at least 2
2504             sp_nodepath_get_node_count(nodepath) < 2) {
2505             SPDocument *document = sp_desktop_document (nodepath->desktop);
2506             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2507             //delete this nodepath's object, not the entire selection! (though at this time, this
2508             //does not matter)
2509             sp_selection_delete();
2510             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2511                               _("Delete nodes"));
2512         } else {
2513             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2514             sp_nodepath_update_statusbar(nodepath);
2515         }
2516     }
2518     g_slist_free (nodepaths);
2521 /**
2522  * Delete one or more selected nodes.
2523  */
2524 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2526     if (!nodepath) return;
2527     if (!nodepath->selected) return;
2529     /** \todo fixme: do it the right way */
2530     while (nodepath->selected) {
2531        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2532         sp_nodepath_node_destroy(node);
2533     }
2536     //clean up the nodepath (such as for trivial subpaths)
2537     sp_nodepath_cleanup(nodepath);
2539     sp_nodepath_update_handles(nodepath);
2541     // if the entire nodepath is removed, delete the selected object.
2542     if (nodepath->subpaths == NULL ||
2543         sp_nodepath_get_node_count(nodepath) < 2) {
2544         SPDocument *document = sp_desktop_document (nodepath->desktop);
2545         sp_selection_delete();
2546         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2547                           _("Delete nodes"));
2548         return;
2549     }
2551     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2553     sp_nodepath_update_statusbar(nodepath);
2556 /**
2557  * Delete one or more segments between two selected nodes.
2558  * This is the code for 'split'.
2559  */
2560 void
2561 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2563    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2564    Inkscape::NodePath::Node *curr, *next;     //Iterators
2566     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2568     if (g_list_length(nodepath->selected) != 2) {
2569         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2570                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2571         return;
2572     }
2574     //Selected nodes, not inclusive
2575    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2576    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2578     if ( ( a==b)                       ||  //same node
2579          (a->subpath  != b->subpath )  ||  //not the same path
2580          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2581          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2582     {
2583         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2584                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2585         return;
2586     }
2588     //###########################################
2589     //# BEGIN EDITS
2590     //###########################################
2591     //##################################
2592     //# CLOSED PATH
2593     //##################################
2594     if (a->subpath->closed) {
2597         gboolean reversed = FALSE;
2599         //Since we can go in a circle, we need to find the shorter distance.
2600         //  a->b or b->a
2601         start = end = NULL;
2602         int distance    = 0;
2603         int minDistance = 0;
2604         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2605             if (curr==b) {
2606                 //printf("a to b:%d\n", distance);
2607                 start = a;//go from a to b
2608                 end   = b;
2609                 minDistance = distance;
2610                 //printf("A to B :\n");
2611                 break;
2612             }
2613             distance++;
2614         }
2616         //try again, the other direction
2617         distance = 0;
2618         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2619             if (curr==a) {
2620                 //printf("b to a:%d\n", distance);
2621                 if (distance < minDistance) {
2622                     start    = b;  //we go from b to a
2623                     end      = a;
2624                     reversed = TRUE;
2625                     //printf("B to A\n");
2626                 }
2627                 break;
2628             }
2629             distance++;
2630         }
2633         //Copy everything from 'end' to 'start' to a new subpath
2634        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2635         for (curr=end ; curr ; curr=curr->n.other) {
2636             NRPathcode code = (NRPathcode) curr->code;
2637             if (curr == end)
2638                 code = NR_MOVETO;
2639             sp_nodepath_node_new(t, NULL,
2640                                  (Inkscape::NodePath::NodeType)curr->type, code,
2641                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2642             if (curr == start)
2643                 break;
2644         }
2645         sp_nodepath_subpath_destroy(a->subpath);
2648     }
2652     //##################################
2653     //# OPEN PATH
2654     //##################################
2655     else {
2657         //We need to get the direction of the list between A and B
2658         //Can we walk from a to b?
2659         start = end = NULL;
2660         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2661             if (curr==b) {
2662                 start = a;  //did it!  we go from a to b
2663                 end   = b;
2664                 //printf("A to B\n");
2665                 break;
2666             }
2667         }
2668         if (!start) {//didn't work?  let's try the other direction
2669             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2670                 if (curr==a) {
2671                     start = b;  //did it!  we go from b to a
2672                     end   = a;
2673                     //printf("B to A\n");
2674                     break;
2675                 }
2676             }
2677         }
2678         if (!start) {
2679             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2680                                                      _("Cannot find path between nodes."));
2681             return;
2682         }
2686         //Copy everything after 'end' to a new subpath
2687        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2688         for (curr=end ; curr ; curr=curr->n.other) {
2689             NRPathcode code = (NRPathcode) curr->code;
2690             if (curr == end)
2691                 code = NR_MOVETO;
2692             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2693                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2694         }
2696         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2697         for (curr = start->n.other ; curr  ; curr=next) {
2698             next = curr->n.other;
2699             sp_nodepath_node_destroy(curr);
2700         }
2702     }
2703     //###########################################
2704     //# END EDITS
2705     //###########################################
2707     //clean up the nodepath (such as for trivial subpaths)
2708     sp_nodepath_cleanup(nodepath);
2710     sp_nodepath_update_handles(nodepath);
2712     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2714     sp_nodepath_update_statusbar(nodepath);
2717 /**
2718  * Call sp_nodepath_set_line() for all selected segments.
2719  */
2720 void
2721 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2723     if (nodepath == NULL) return;
2725     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2726        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2727         g_assert(n->selected);
2728         if (n->p.other && n->p.other->selected) {
2729             sp_nodepath_set_line_type(n, code);
2730         }
2731     }
2733     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2736 /**
2737  * Call sp_nodepath_convert_node_type() for all selected nodes.
2738  */
2739 void
2740 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2742     if (nodepath == NULL) return;
2744     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2746     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2747         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2748     }
2750     sp_nodepath_update_repr(nodepath, _("Change node type"));
2753 /**
2754  * Change select status of node, update its own and neighbour handles.
2755  */
2756 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2758     node->selected = selected;
2760     if (selected) {
2761         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2762         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2763         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2764         sp_knot_update_ctrl(node->knot);
2765     } else {
2766         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2767         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2768         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2769         sp_knot_update_ctrl(node->knot);
2770     }
2772     sp_node_update_handles(node);
2773     if (node->n.other) sp_node_update_handles(node->n.other);
2774     if (node->p.other) sp_node_update_handles(node->p.other);
2777 /**
2778 \brief Select a node
2779 \param node     The node to select
2780 \param incremental   If true, add to selection, otherwise deselect others
2781 \param override   If true, always select this node, otherwise toggle selected status
2782 */
2783 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2785     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2787     if (incremental) {
2788         if (override) {
2789             if (!g_list_find(nodepath->selected, node)) {
2790                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2791             }
2792             sp_node_set_selected(node, TRUE);
2793         } else { // toggle
2794             if (node->selected) {
2795                 g_assert(g_list_find(nodepath->selected, node));
2796                 nodepath->selected = g_list_remove(nodepath->selected, node);
2797             } else {
2798                 g_assert(!g_list_find(nodepath->selected, node));
2799                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2800             }
2801             sp_node_set_selected(node, !node->selected);
2802         }
2803     } else {
2804         sp_nodepath_deselect(nodepath);
2805         nodepath->selected = g_list_prepend(nodepath->selected, node);
2806         sp_node_set_selected(node, TRUE);
2807     }
2809     sp_nodepath_update_statusbar(nodepath);
2813 /**
2814 \brief Deselect all nodes in the nodepath
2815 */
2816 void
2817 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2819     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2821     while (nodepath->selected) {
2822         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2823         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2824     }
2825     sp_nodepath_update_statusbar(nodepath);
2828 /**
2829 \brief Select or invert selection of all nodes in the nodepath
2830 */
2831 void
2832 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2834     if (!nodepath) return;
2836     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2837        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2838         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2839            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2840            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2841         }
2842     }
2845 /**
2846  * If nothing selected, does the same as sp_nodepath_select_all();
2847  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2848  * (i.e., similar to "select all in layer", with the "selected" subpaths
2849  * being treated as "layers" in the path).
2850  */
2851 void
2852 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2854     if (!nodepath) return;
2856     if (g_list_length (nodepath->selected) == 0) {
2857         sp_nodepath_select_all (nodepath, invert);
2858         return;
2859     }
2861     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2862     GSList *subpaths = NULL;
2864     for (GList *l = copy; l != NULL; l = l->next) {
2865         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2866         Inkscape::NodePath::SubPath *subpath = n->subpath;
2867         if (!g_slist_find (subpaths, subpath))
2868             subpaths = g_slist_prepend (subpaths, subpath);
2869     }
2871     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2872         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2873         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2874             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2875             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2876         }
2877     }
2879     g_slist_free (subpaths);
2880     g_list_free (copy);
2883 /**
2884  * \brief Select the node after the last selected; if none is selected,
2885  * select the first within path.
2886  */
2887 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2889     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2891    Inkscape::NodePath::Node *last = NULL;
2892     if (nodepath->selected) {
2893         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2894            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2895             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2896             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2897                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2898                 if (node->selected) {
2899                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2900                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2901                             if (spl->next) { // there's a next subpath
2902                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2903                                 last = subpath_next->first;
2904                             } else if (spl->prev) { // there's a previous subpath
2905                                 last = NULL; // to be set later to the first node of first subpath
2906                             } else {
2907                                 last = node->n.other;
2908                             }
2909                         } else {
2910                             last = node->n.other;
2911                         }
2912                     } else {
2913                         if (node->n.other) {
2914                             last = node->n.other;
2915                         } else {
2916                             if (spl->next) { // there's a next subpath
2917                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2918                                 last = subpath_next->first;
2919                             } else if (spl->prev) { // there's a previous subpath
2920                                 last = NULL; // to be set later to the first node of first subpath
2921                             } else {
2922                                 last = (Inkscape::NodePath::Node *) subpath->first;
2923                             }
2924                         }
2925                     }
2926                 }
2927             }
2928         }
2929         sp_nodepath_deselect(nodepath);
2930     }
2932     if (last) { // there's at least one more node after selected
2933         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2934     } else { // no more nodes, select the first one in first subpath
2935        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2936         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2937     }
2940 /**
2941  * \brief Select the node before the first selected; if none is selected,
2942  * select the last within path
2943  */
2944 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2946     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2948    Inkscape::NodePath::Node *last = NULL;
2949     if (nodepath->selected) {
2950         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2951            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2952             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2953                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2954                 if (node->selected) {
2955                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2956                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2957                             if (spl->prev) { // there's a prev subpath
2958                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2959                                 last = subpath_prev->last;
2960                             } else if (spl->next) { // there's a next subpath
2961                                 last = NULL; // to be set later to the last node of last subpath
2962                             } else {
2963                                 last = node->p.other;
2964                             }
2965                         } else {
2966                             last = node->p.other;
2967                         }
2968                     } else {
2969                         if (node->p.other) {
2970                             last = node->p.other;
2971                         } else {
2972                             if (spl->prev) { // there's a prev subpath
2973                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2974                                 last = subpath_prev->last;
2975                             } else if (spl->next) { // there's a next subpath
2976                                 last = NULL; // to be set later to the last node of last subpath
2977                             } else {
2978                                 last = (Inkscape::NodePath::Node *) subpath->last;
2979                             }
2980                         }
2981                     }
2982                 }
2983             }
2984         }
2985         sp_nodepath_deselect(nodepath);
2986     }
2988     if (last) { // there's at least one more node before selected
2989         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2990     } else { // no more nodes, select the last one in last subpath
2991         GList *spl = g_list_last(nodepath->subpaths);
2992        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2993         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2994     }
2997 /**
2998  * \brief Select all nodes that are within the rectangle.
2999  */
3000 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
3002     if (!incremental) {
3003         sp_nodepath_deselect(nodepath);
3004     }
3006     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3007        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3008         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3009            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3011             if (b.contains(node->pos)) {
3012                 sp_nodepath_node_select(node, TRUE, TRUE);
3013             }
3014         }
3015     }
3019 void
3020 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3022     g_assert (n);
3023     g_assert (nodepath);
3024     g_assert (n->subpath->nodepath == nodepath);
3026     if (g_list_length (nodepath->selected) == 0) {
3027         if (grow > 0) {
3028             sp_nodepath_node_select(n, TRUE, TRUE);
3029         }
3030         return;
3031     }
3033     if (g_list_length (nodepath->selected) == 1) {
3034         if (grow < 0) {
3035             sp_nodepath_deselect (nodepath);
3036             return;
3037         }
3038     }
3040         double n_sel_range = 0, p_sel_range = 0;
3041             Inkscape::NodePath::Node *farthest_n_node = n;
3042             Inkscape::NodePath::Node *farthest_p_node = n;
3044         // Calculate ranges
3045         {
3046             double n_range = 0, p_range = 0;
3047             bool n_going = true, p_going = true;
3048             Inkscape::NodePath::Node *n_node = n;
3049             Inkscape::NodePath::Node *p_node = n;
3050             do {
3051                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3052                 if (n_node && n_going)
3053                     n_node = n_node->n.other;
3054                 if (n_node == NULL) {
3055                     n_going = false;
3056                 } else {
3057                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3058                     if (n_node->selected) {
3059                         n_sel_range = n_range;
3060                         farthest_n_node = n_node;
3061                     }
3062                     if (n_node == p_node) {
3063                         n_going = false;
3064                         p_going = false;
3065                     }
3066                 }
3067                 if (p_node && p_going)
3068                     p_node = p_node->p.other;
3069                 if (p_node == NULL) {
3070                     p_going = false;
3071                 } else {
3072                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3073                     if (p_node->selected) {
3074                         p_sel_range = p_range;
3075                         farthest_p_node = p_node;
3076                     }
3077                     if (p_node == n_node) {
3078                         n_going = false;
3079                         p_going = false;
3080                     }
3081                 }
3082             } while (n_going || p_going);
3083         }
3085     if (grow > 0) {
3086         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3087                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3088         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3089                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3090         }
3091     } else {
3092         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3093                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3094         } else if (farthest_p_node && farthest_p_node->selected) {
3095                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3096         }
3097     }
3100 void
3101 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3103     g_assert (n);
3104     g_assert (nodepath);
3105     g_assert (n->subpath->nodepath == nodepath);
3107     if (g_list_length (nodepath->selected) == 0) {
3108         if (grow > 0) {
3109             sp_nodepath_node_select(n, TRUE, TRUE);
3110         }
3111         return;
3112     }
3114     if (g_list_length (nodepath->selected) == 1) {
3115         if (grow < 0) {
3116             sp_nodepath_deselect (nodepath);
3117             return;
3118         }
3119     }
3121     Inkscape::NodePath::Node *farthest_selected = NULL;
3122     double farthest_dist = 0;
3124     Inkscape::NodePath::Node *closest_unselected = NULL;
3125     double closest_dist = NR_HUGE;
3127     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3128        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3129         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3130            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3131            if (node == n)
3132                continue;
3133            if (node->selected) {
3134                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3135                    farthest_dist = NR::L2(node->pos - n->pos);
3136                    farthest_selected = node;
3137                }
3138            } else {
3139                if (NR::L2(node->pos - n->pos) < closest_dist) {
3140                    closest_dist = NR::L2(node->pos - n->pos);
3141                    closest_unselected = node;
3142                }
3143            }
3144         }
3145     }
3147     if (grow > 0) {
3148         if (closest_unselected) {
3149             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3150         }
3151     } else {
3152         if (farthest_selected) {
3153             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3154         }
3155     }
3159 /**
3160 \brief  Saves all nodes' and handles' current positions in their origin members
3161 */
3162 void
3163 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3165     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3166        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3167         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3168            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3169            n->origin = n->pos;
3170            n->p.origin = n->p.pos;
3171            n->n.origin = n->n.pos;
3172         }
3173     }
3176 /**
3177 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3178 */
3179 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3181     if (!nodepath->selected) {
3182         return NULL;
3183     }
3185     GList *r = NULL;
3186     guint i = 0;
3187     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3188        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3189         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3190            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3191             i++;
3192             if (node->selected) {
3193                 r = g_list_append(r, GINT_TO_POINTER(i));
3194             }
3195         }
3196     }
3197     return r;
3200 /**
3201 \brief  Restores selection by selecting nodes whose positions are in the list
3202 */
3203 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3205     sp_nodepath_deselect(nodepath);
3207     guint i = 0;
3208     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3209        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3210         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3211            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3212             i++;
3213             if (g_list_find(r, GINT_TO_POINTER(i))) {
3214                 sp_nodepath_node_select(node, TRUE, TRUE);
3215             }
3216         }
3217     }
3221 /**
3222 \brief Adjusts handle according to node type and line code.
3223 */
3224 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3226     g_assert(node);
3228    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3229    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3231    // nothing to do if we are an end node
3232     if (me->other == NULL) return;
3233     if (other->other == NULL) return;
3235     // nothing to do if we are a cusp node
3236     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3238     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3239     NRPathcode mecode;
3240     if (which_adjust == 1) {
3241         mecode = (NRPathcode)me->other->code;
3242     } else {
3243         mecode = (NRPathcode)node->code;
3244     }
3245     if (mecode == NR_LINETO) return;
3247     if (sp_node_side_is_line(node, other)) {
3248         // other is a line, and we are either smooth or symm
3249        Inkscape::NodePath::Node *othernode = other->other;
3250         double len = NR::L2(me->pos - node->pos);
3251         NR::Point delta = node->pos - othernode->pos;
3252         double linelen = NR::L2(delta);
3253         if (linelen < 1e-18)
3254             return;
3255         me->pos = node->pos + (len / linelen)*delta;
3256         return;
3257     }
3259     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3260         // symmetrize 
3261         me->pos = 2 * node->pos - other->pos;
3262         return;
3263     } else {
3264         // smoothify
3265         double len = NR::L2(me->pos - node->pos);
3266         NR::Point delta = other->pos - node->pos;
3267         double otherlen = NR::L2(delta);
3268         if (otherlen < 1e-18) return;
3269         me->pos = node->pos - (len / otherlen) * delta;
3270     }
3273 /**
3274  \brief Adjusts both handles according to node type and line code
3275  */
3276 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3278     g_assert(node);
3280     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3282     /* we are either smooth or symm */
3284     if (node->p.other == NULL) return;
3285     if (node->n.other == NULL) return;
3287     if (sp_node_side_is_line(node, &node->p)) {
3288         sp_node_adjust_handle(node, 1);
3289         return;
3290     }
3292     if (sp_node_side_is_line(node, &node->n)) {
3293         sp_node_adjust_handle(node, -1);
3294         return;
3295     }
3297     /* both are curves */
3298     NR::Point const delta( node->n.pos - node->p.pos );
3300     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3301         node->p.pos = node->pos - delta / 2;
3302         node->n.pos = node->pos + delta / 2;
3303         return;
3304     }
3306     /* We are smooth */
3307     double plen = NR::L2(node->p.pos - node->pos);
3308     if (plen < 1e-18) return;
3309     double nlen = NR::L2(node->n.pos - node->pos);
3310     if (nlen < 1e-18) return;
3311     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3312     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3315 /**
3316  * Node event callback.
3317  */
3318 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3320     gboolean ret = FALSE;
3321     switch (event->type) {
3322         case GDK_ENTER_NOTIFY:
3323             Inkscape::NodePath::Path::active_node = n;
3324             break;
3325         case GDK_LEAVE_NOTIFY:
3326             Inkscape::NodePath::Path::active_node = NULL;
3327             break;
3328         case GDK_SCROLL:
3329             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3330                 switch (event->scroll.direction) {
3331                     case GDK_SCROLL_UP:
3332                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3333                         break;
3334                     case GDK_SCROLL_DOWN:
3335                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3336                         break;
3337                     default:
3338                         break;
3339                 }
3340                 ret = TRUE;
3341             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3342                 switch (event->scroll.direction) {
3343                     case GDK_SCROLL_UP:
3344                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3345                         break;
3346                     case GDK_SCROLL_DOWN:
3347                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3348                         break;
3349                     default:
3350                         break;
3351                 }
3352                 ret = TRUE;
3353             }
3354             break;
3355         case GDK_KEY_PRESS:
3356             switch (get_group0_keyval (&event->key)) {
3357                 case GDK_space:
3358                     if (event->key.state & GDK_BUTTON1_MASK) {
3359                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3360                         stamp_repr(nodepath);
3361                         ret = TRUE;
3362                     }
3363                     break;
3364                 case GDK_Page_Up:
3365                     if (event->key.state & GDK_CONTROL_MASK) {
3366                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3367                     } else {
3368                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3369                     }
3370                     break;
3371                 case GDK_Page_Down:
3372                     if (event->key.state & GDK_CONTROL_MASK) {
3373                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3374                     } else {
3375                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3376                     }
3377                     break;
3378                 default:
3379                     break;
3380             }
3381             break;
3382         default:
3383             break;
3384     }
3386     return ret;
3389 /**
3390  * Handle keypress on node; directly called.
3391  */
3392 gboolean node_key(GdkEvent *event)
3394     Inkscape::NodePath::Path *np;
3396     // there is no way to verify nodes so set active_node to nil when deleting!!
3397     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3399     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3400         gint ret = FALSE;
3401         switch (get_group0_keyval (&event->key)) {
3402             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3403             case GDK_BackSpace:
3404                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3405                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3406                 sp_nodepath_update_repr(np, _("Delete node"));
3407                 Inkscape::NodePath::Path::active_node = NULL;
3408                 ret = TRUE;
3409                 break;
3410             case GDK_c:
3411                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3412                 ret = TRUE;
3413                 break;
3414             case GDK_s:
3415                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3416                 ret = TRUE;
3417                 break;
3418             case GDK_y:
3419                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3420                 ret = TRUE;
3421                 break;
3422             case GDK_b:
3423                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3424                 ret = TRUE;
3425                 break;
3426         }
3427         return ret;
3428     }
3429     return FALSE;
3432 /**
3433  * Mouseclick on node callback.
3434  */
3435 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3437    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3439     if (state & GDK_CONTROL_MASK) {
3440         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3442         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3443             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3444                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3445             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3446                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3447             } else {
3448                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3449             }
3450             sp_nodepath_update_repr(nodepath, _("Change node type"));
3451             sp_nodepath_update_statusbar(nodepath);
3453         } else { //ctrl+alt+click: delete node
3454             GList *node_to_delete = NULL;
3455             node_to_delete = g_list_append(node_to_delete, n);
3456             sp_node_delete_preserve(node_to_delete);
3457         }
3459     } else {
3460         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3461     }
3464 /**
3465  * Mouse grabbed node callback.
3466  */
3467 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3469    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3471     if (!n->selected) {
3472         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3473     }
3475     n->is_dragging = true;
3476     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3478     sp_nodepath_remember_origins (n->subpath->nodepath);
3481 /**
3482  * Mouse ungrabbed node callback.
3483  */
3484 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3486    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3488    n->dragging_out = NULL;
3489    n->is_dragging = false;
3490    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3492    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3495 /**
3496  * The point on a line, given by its angle, closest to the given point.
3497  * \param p  A point.
3498  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3499  * \param closest  Pointer to the point struct where the result is stored.
3500  * \todo FIXME: use dot product perhaps?
3501  */
3502 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3504     if (a == HUGE_VAL) { // vertical
3505         *closest = NR::Point(0, (*p)[NR::Y]);
3506     } else {
3507         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3508         (*closest)[NR::Y] = a * (*closest)[NR::X];
3509     }
3512 /**
3513  * Distance from the point to a line given by its angle.
3514  * \param p  A point.
3515  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3516  */
3517 static double point_line_distance(NR::Point *p, double a)
3519     NR::Point c;
3520     point_line_closest(p, a, &c);
3521     return sqrt(((*p)[NR::X] - c[NR::X])*((*p)[NR::X] - c[NR::X]) + ((*p)[NR::Y] - c[NR::Y])*((*p)[NR::Y] - c[NR::Y]));
3524 /**
3525  * Callback for node "request" signal.
3526  * \todo fixme: This goes to "moved" event? (lauris)
3527  */
3528 static gboolean
3529 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3531     double yn, xn, yp, xp;
3532     double an, ap, na, pa;
3533     double d_an, d_ap, d_na, d_pa;
3534     gboolean collinear = FALSE;
3535     NR::Point c;
3536     NR::Point pr;
3538     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3540     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3542     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3543     if ( (!n->subpath->nodepath->straight_path) &&
3544          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3545            || n->dragging_out ) )
3546     {
3547        NR::Point mouse = (*p);
3549        if (!n->dragging_out) {
3550            // This is the first drag-out event; find out which handle to drag out
3551            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3552            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3554            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3555                return FALSE;
3557            Inkscape::NodePath::NodeSide *opposite;
3558            if (appr_p > appr_n) { // closer to p
3559                n->dragging_out = &n->p;
3560                opposite = &n->n;
3561                n->code = NR_CURVETO;
3562            } else if (appr_p < appr_n) { // closer to n
3563                n->dragging_out = &n->n;
3564                opposite = &n->p;
3565                n->n.other->code = NR_CURVETO;
3566            } else { // p and n nodes are the same
3567                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3568                    n->dragging_out = &n->p;
3569                    opposite = &n->n;
3570                    n->code = NR_CURVETO;
3571                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3572                    n->dragging_out = &n->n;
3573                    opposite = &n->p;
3574                    n->n.other->code = NR_CURVETO;
3575                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3576                    double appr_other_n = (n->n.other ? NR::L2(n->n.other->n.pos - n->pos) - NR::L2(n->n.other->n.pos - (*p)) : -HUGE_VAL);
3577                    double appr_other_p = (n->n.other ? NR::L2(n->n.other->p.pos - n->pos) - NR::L2(n->n.other->p.pos - (*p)) : -HUGE_VAL);
3578                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3579                        n->dragging_out = &n->n;
3580                        opposite = &n->p;
3581                        n->n.other->code = NR_CURVETO;
3582                    } else { // closer to other's n handle
3583                        n->dragging_out = &n->p;
3584                        opposite = &n->n;
3585                        n->code = NR_CURVETO;
3586                    }
3587                }
3588            }
3590            // if there's another handle, make sure the one we drag out starts parallel to it
3591            if (opposite->pos != n->pos) {
3592                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3593            }
3595            // knots might not be created yet!
3596            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3597            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3598        }
3600        // pass this on to the handle-moved callback
3601        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3602        sp_node_update_handles(n);
3603        return TRUE;
3604    }
3606     if (state & GDK_CONTROL_MASK) { // constrained motion
3608         // calculate relative distances of handles
3609         // n handle:
3610         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3611         xn = n->n.pos[NR::X] - n->pos[NR::X];
3612         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3613         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3614             if (n->n.other) { // if there is the next point
3615                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3616                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3617                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3618             }
3619         }
3620         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3621         if (yn < 0) { xn = -xn; yn = -yn; }
3623         // p handle:
3624         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3625         xp = n->p.pos[NR::X] - n->pos[NR::X];
3626         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3627         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3628             if (n->p.other) {
3629                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3630                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3631                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3632             }
3633         }
3634         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3635         if (yp < 0) { xp = -xp; yp = -yp; }
3637         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3638             // sliding on handles, only if at least one of the handles is non-vertical
3639             // (otherwise it's the same as ctrl+drag anyway)
3641             // calculate angles of the handles
3642             if (xn == 0) {
3643                 if (yn == 0) { // no handle, consider it the continuation of the other one
3644                     an = 0;
3645                     collinear = TRUE;
3646                 }
3647                 else an = 0; // vertical; set the angle to horizontal
3648             } else an = yn/xn;
3650             if (xp == 0) {
3651                 if (yp == 0) { // no handle, consider it the continuation of the other one
3652                     ap = an;
3653                 }
3654                 else ap = 0; // vertical; set the angle to horizontal
3655             } else  ap = yp/xp;
3657             if (collinear) an = ap;
3659             // angles of the perpendiculars; HUGE_VAL means vertical
3660             if (an == 0) na = HUGE_VAL; else na = -1/an;
3661             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3663             // mouse point relative to the node's original pos
3664             pr = (*p) - n->origin;
3666             // distances to the four lines (two handles and two perpendiculars)
3667             d_an = point_line_distance(&pr, an);
3668             d_na = point_line_distance(&pr, na);
3669             d_ap = point_line_distance(&pr, ap);
3670             d_pa = point_line_distance(&pr, pa);
3672             // find out which line is the closest, save its closest point in c
3673             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3674                 point_line_closest(&pr, an, &c);
3675             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3676                 point_line_closest(&pr, ap, &c);
3677             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3678                 point_line_closest(&pr, na, &c);
3679             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3680                 point_line_closest(&pr, pa, &c);
3681             }
3683             // move the node to the closest point
3684             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3685                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3686                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3687                                             true);
3689         } else {  // constraining to hor/vert
3691             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3692                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3693                                                 (*p)[NR::X] - n->pos[NR::X], 
3694                                                 n->origin[NR::Y] - n->pos[NR::Y],
3695                                                 true, 
3696                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3697             } else { // snap to vert
3698                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3699                                                 n->origin[NR::X] - n->pos[NR::X],
3700                                                 (*p)[NR::Y] - n->pos[NR::Y],
3701                                                 true,
3702                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3703             }
3704         }
3705     } else { // move freely
3706         if (n->is_dragging) {
3707             if (state & GDK_MOD1_MASK) { // sculpt
3708                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3709             } else {
3710                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3711                                             (*p)[NR::X] - n->pos[NR::X],
3712                                             (*p)[NR::Y] - n->pos[NR::Y],
3713                                             (state & GDK_SHIFT_MASK) == 0);
3714             }
3715         }
3716     }
3718     n->subpath->nodepath->desktop->scroll_to_point(p);
3720     return TRUE;
3723 /**
3724  * Node handle clicked callback.
3725  */
3726 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3728    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3730     if (state & GDK_CONTROL_MASK) { // "delete" handle
3731         if (n->p.knot == knot) {
3732             n->p.pos = n->pos;
3733         } else if (n->n.knot == knot) {
3734             n->n.pos = n->pos;
3735         }
3736         sp_node_update_handles(n);
3737         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3738         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3739         sp_nodepath_update_statusbar(nodepath);
3741     } else { // just select or add to selection, depending in Shift
3742         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3743     }
3746 /**
3747  * Node handle grabbed callback.
3748  */
3749 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3751    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3753     if (!n->selected) {
3754         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3755     }
3757     // remember the origin point of the handle
3758     if (n->p.knot == knot) {
3759         n->p.origin_radial = n->p.pos - n->pos;
3760     } else if (n->n.knot == knot) {
3761         n->n.origin_radial = n->n.pos - n->pos;
3762     } else {
3763         g_assert_not_reached();
3764     }
3766     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3769 /**
3770  * Node handle ungrabbed callback.
3771  */
3772 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3774    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3776     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3777     if (n->p.knot == knot) {
3778         n->p.origin_radial.a = 0;
3779         sp_knot_set_position(knot, &n->p.pos, state);
3780     } else if (n->n.knot == knot) {
3781         n->n.origin_radial.a = 0;
3782         sp_knot_set_position(knot, &n->n.pos, state);
3783     } else {
3784         g_assert_not_reached();
3785     }
3787     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3790 /**
3791  * Node handle "request" signal callback.
3792  */
3793 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3795     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3797     Inkscape::NodePath::NodeSide *me, *opposite;
3798     gint which;
3799     if (n->p.knot == knot) {
3800         me = &n->p;
3801         opposite = &n->n;
3802         which = -1;
3803     } else if (n->n.knot == knot) {
3804         me = &n->n;
3805         opposite = &n->p;
3806         which = 1;
3807     } else {
3808         me = opposite = NULL;
3809         which = 0;
3810         g_assert_not_reached();
3811     }
3813     SPDesktop *desktop = n->subpath->nodepath->desktop;
3814     SnapManager &m = desktop->namedview->snap_manager;
3815     m.setup(desktop, n->subpath->nodepath->item);
3816     Inkscape::SnappedPoint s;
3817     
3818     if ((state & GDK_SHIFT_MASK) != 0) {
3819         // We will not try to snap when the shift-key is pressed
3820         // so remove the old snap indicator and don't wait for it to time-out  
3821         desktop->snapindicator->remove_snappoint();     
3822     }
3824     Inkscape::NodePath::Node *othernode = opposite->other;
3825     if (othernode) {
3826         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3827             /* We are smooth node adjacent with line */
3828             NR::Point const delta = *p - n->pos;
3829             NR::Coord const len = NR::L2(delta);
3830             Inkscape::NodePath::Node *othernode = opposite->other;
3831             NR::Point const ndelta = n->pos - othernode->pos;
3832             NR::Coord const linelen = NR::L2(ndelta);
3833             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3834                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3835                 (*p) = n->pos + (scal / linelen) * ndelta;
3836             }
3837             if ((state & GDK_SHIFT_MASK) == 0) {
3838                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3839             }
3840         } else {
3841                 if ((state & GDK_SHIFT_MASK) == 0) {
3842                         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3843                 }
3844         }
3845     } else {
3846         if ((state & GDK_SHIFT_MASK) == 0) {
3847                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3848         }
3849     }
3850     
3851     s.getPoint(*p);
3852     
3853     sp_node_adjust_handle(n, -which);
3855     return FALSE;
3858 /**
3859  * Node handle moved callback.
3860  */
3861 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3863    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3865    Inkscape::NodePath::NodeSide *me;
3866    Inkscape::NodePath::NodeSide *other;
3867     if (n->p.knot == knot) {
3868         me = &n->p;
3869         other = &n->n;
3870     } else if (n->n.knot == knot) {
3871         me = &n->n;
3872         other = &n->p;
3873     } else {
3874         me = NULL;
3875         other = NULL;
3876         g_assert_not_reached();
3877     }
3879     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3880     Radial rme(me->pos - n->pos);
3881     Radial rother(other->pos - n->pos);
3882     Radial rnew(*p - n->pos);
3884     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3885         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3886         /* 0 interpreted as "no snapping". */
3888         // 1. Snap to the closest PI/snaps angle, starting from zero.
3889         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3891         // 2. Snap to the original angle, its opposite and perpendiculars
3892         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3893             /* The closest PI/2 angle, starting from original angle */
3894             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3896             // Snap to the closest.
3897             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3898                        ? a_snapped
3899                        : a_ortho );
3900         }
3902         // 3. Snap to the angle of the opposite line, if any
3903         Inkscape::NodePath::Node *othernode = other->other;
3904         if (othernode) {
3905             NR::Point other_to_snap(0,0);
3906             if (sp_node_side_is_line(n, other)) {
3907                 other_to_snap = othernode->pos - n->pos;
3908             } else {
3909                 other_to_snap = other->pos - n->pos;
3910             }
3911             if (NR::L2(other_to_snap) > 1e-3) {
3912                 Radial rother_to_snap(other_to_snap);
3913                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3914                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3916                 // Snap to the closest.
3917                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3918                        ? a_snapped
3919                        : a_oppo );
3920             }
3921         }
3923         rnew.a = a_snapped;
3924     }
3926     if (state & GDK_MOD1_MASK) {
3927         // lock handle length
3928         rnew.r = me->origin_radial.r;
3929     }
3931     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3932         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3933         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3934         rother.a += rnew.a - rme.a;
3935         other->pos = NR::Point(rother) + n->pos;
3936         if (other->knot) {
3937             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3938             sp_knot_moveto(other->knot, &other->pos);
3939         }
3940     }
3942     me->pos = NR::Point(rnew) + n->pos;
3943     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3945     // move knot, but without emitting the signal:
3946     // we cannot emit a "moved" signal because we're now processing it
3947     sp_knot_moveto(me->knot, &(me->pos));
3949     update_object(n->subpath->nodepath);
3951     /* status text */
3952     SPDesktop *desktop = n->subpath->nodepath->desktop;
3953     if (!desktop) return;
3954     SPEventContext *ec = desktop->event_context;
3955     if (!ec) return;
3956     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3957     if (!mc) return;
3959     double degrees = 180 / M_PI * rnew.a;
3960     if (degrees > 180) degrees -= 360;
3961     if (degrees < -180) degrees += 360;
3962     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3963         degrees = angle_to_compass (degrees);
3965     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3967     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3968          _("<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);
3970     g_string_free(length, TRUE);
3973 /**
3974  * Node handle event callback.
3975  */
3976 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3978     gboolean ret = FALSE;
3979     switch (event->type) {
3980         case GDK_KEY_PRESS:
3981             switch (get_group0_keyval (&event->key)) {
3982                 case GDK_space:
3983                     if (event->key.state & GDK_BUTTON1_MASK) {
3984                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3985                         stamp_repr(nodepath);
3986                         ret = TRUE;
3987                     }
3988                     break;
3989                 default:
3990                     break;
3991             }
3992             break;
3993         case GDK_ENTER_NOTIFY:
3994             // we use an experimentally determined threshold that seems to work fine
3995             if (NR::L2(n->pos - knot->pos) < 0.75)
3996                 Inkscape::NodePath::Path::active_node = n;
3997             break;
3998         case GDK_LEAVE_NOTIFY:
3999             // we use an experimentally determined threshold that seems to work fine
4000             if (NR::L2(n->pos - knot->pos) < 0.75)
4001                 Inkscape::NodePath::Path::active_node = NULL;
4002             break;
4003         default:
4004             break;
4005     }
4007     return ret;
4010 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4011                                  Radial &rme, Radial &rother, gboolean const both)
4013     rme.a += angle;
4014     if ( both
4015          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4016          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4017     {
4018         rother.a += angle;
4019     }
4022 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4023                                         Radial &rme, Radial &rother, gboolean const both)
4025     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4027     gdouble r;
4028     if ( both
4029          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4030          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4031     {
4032         r = MAX(rme.r, rother.r);
4033     } else {
4034         r = rme.r;
4035     }
4037     gdouble const weird_angle = atan2(norm_angle, r);
4038 /* Bulia says norm_angle is just the visible distance that the
4039  * object's end must travel on the screen.  Left as 'angle' for want of
4040  * a better name.*/
4042     rme.a += weird_angle;
4043     if ( both
4044          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4045          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4046     {
4047         rother.a += weird_angle;
4048     }
4051 /**
4052  * Rotate one node.
4053  */
4054 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4056     Inkscape::NodePath::NodeSide *me, *other;
4057     bool both = false;
4059     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4060     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4062     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4063         me = &(n->p);
4064         other = &(n->n);
4065     } else if (!n->p.other) {
4066         me = &(n->n);
4067         other = &(n->p);
4068     } else {
4069         if (which > 0) { // right handle
4070             if (xn > xp) {
4071                 me = &(n->n);
4072                 other = &(n->p);
4073             } else {
4074                 me = &(n->p);
4075                 other = &(n->n);
4076             }
4077         } else if (which < 0){ // left handle
4078             if (xn <= xp) {
4079                 me = &(n->n);
4080                 other = &(n->p);
4081             } else {
4082                 me = &(n->p);
4083                 other = &(n->n);
4084             }
4085         } else { // both handles
4086             me = &(n->n);
4087             other = &(n->p);
4088             both = true;
4089         }
4090     }
4092     Radial rme(me->pos - n->pos);
4093     Radial rother(other->pos - n->pos);
4095     if (screen) {
4096         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4097     } else {
4098         node_rotate_one_internal (*n, angle, rme, rother, both);
4099     }
4101     me->pos = n->pos + NR::Point(rme);
4103     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4104         other->pos =  n->pos + NR::Point(rother);
4105     }
4107     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4108     // so here we just move all the knots without emitting move signals, for speed
4109     sp_node_update_handles(n, false);
4112 /**
4113  * Rotate selected nodes.
4114  */
4115 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4117     if (!nodepath || !nodepath->selected) return;
4119     if (g_list_length(nodepath->selected) == 1) {
4120        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4121         node_rotate_one (n, angle, which, screen);
4122     } else {
4123        // rotate as an object:
4125         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4126         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4127         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4128             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4129             box.expandTo (n->pos); // contain all selected nodes
4130         }
4132         gdouble rot;
4133         if (screen) {
4134             gdouble const zoom = nodepath->desktop->current_zoom();
4135             gdouble const zmove = angle / zoom;
4136             gdouble const r = NR::L2(box.max() - box.midpoint());
4137             rot = atan2(zmove, r);
4138         } else {
4139             rot = angle;
4140         }
4142         NR::Point rot_center;
4143         if (Inkscape::NodePath::Path::active_node == NULL)
4144             rot_center = box.midpoint();
4145         else
4146             rot_center = Inkscape::NodePath::Path::active_node->pos;
4148         NR::Matrix t =
4149             NR::Matrix (NR::translate(-rot_center)) *
4150             NR::Matrix (NR::rotate(rot)) *
4151             NR::Matrix (NR::translate(rot_center));
4153         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4154             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4155             n->pos *= t;
4156             n->n.pos *= t;
4157             n->p.pos *= t;
4158             sp_node_update_handles(n, false);
4159         }
4160     }
4162     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4165 /**
4166  * Scale one node.
4167  */
4168 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4170     bool both = false;
4171     Inkscape::NodePath::NodeSide *me, *other;
4173     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4174     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4176     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4177         me = &(n->p);
4178         other = &(n->n);
4179         n->code = NR_CURVETO;
4180     } else if (!n->p.other) {
4181         me = &(n->n);
4182         other = &(n->p);
4183         if (n->n.other)
4184             n->n.other->code = NR_CURVETO;
4185     } else {
4186         if (which > 0) { // right handle
4187             if (xn > xp) {
4188                 me = &(n->n);
4189                 other = &(n->p);
4190                 if (n->n.other)
4191                     n->n.other->code = NR_CURVETO;
4192             } else {
4193                 me = &(n->p);
4194                 other = &(n->n);
4195                 n->code = NR_CURVETO;
4196             }
4197         } else if (which < 0){ // left handle
4198             if (xn <= xp) {
4199                 me = &(n->n);
4200                 other = &(n->p);
4201                 if (n->n.other)
4202                     n->n.other->code = NR_CURVETO;
4203             } else {
4204                 me = &(n->p);
4205                 other = &(n->n);
4206                 n->code = NR_CURVETO;
4207             }
4208         } else { // both handles
4209             me = &(n->n);
4210             other = &(n->p);
4211             both = true;
4212             n->code = NR_CURVETO;
4213             if (n->n.other)
4214                 n->n.other->code = NR_CURVETO;
4215         }
4216     }
4218     Radial rme(me->pos - n->pos);
4219     Radial rother(other->pos - n->pos);
4221     rme.r += grow;
4222     if (rme.r < 0) rme.r = 0;
4223     if (rme.a == HUGE_VAL) {
4224         if (me->other) { // if direction is unknown, initialize it towards the next node
4225             Radial rme_next(me->other->pos - n->pos);
4226             rme.a = rme_next.a;
4227         } else { // if there's no next, initialize to 0
4228             rme.a = 0;
4229         }
4230     }
4231     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4232         rother.r += grow;
4233         if (rother.r < 0) rother.r = 0;
4234         if (rother.a == HUGE_VAL) {
4235             rother.a = rme.a + M_PI;
4236         }
4237     }
4239     me->pos = n->pos + NR::Point(rme);
4241     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4242         other->pos = n->pos + NR::Point(rother);
4243     }
4245     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4246     // so here we just move all the knots without emitting move signals, for speed
4247     sp_node_update_handles(n, false);
4250 /**
4251  * Scale selected nodes.
4252  */
4253 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4255     if (!nodepath || !nodepath->selected) return;
4257     if (g_list_length(nodepath->selected) == 1) {
4258         // scale handles of the single selected node
4259         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4260         node_scale_one (n, grow, which);
4261     } else {
4262         // scale nodes as an "object":
4264         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4265         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4266         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4267             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4268             box.expandTo (n->pos); // contain all selected nodes
4269         }
4271         double scale = (box.maxExtent() + grow)/box.maxExtent();
4273         NR::Point scale_center;
4274         if (Inkscape::NodePath::Path::active_node == NULL)
4275             scale_center = box.midpoint();
4276         else
4277             scale_center = Inkscape::NodePath::Path::active_node->pos;
4279         NR::Matrix t =
4280             NR::Matrix (NR::translate(-scale_center)) *
4281             NR::Matrix (NR::scale(scale, scale)) *
4282             NR::Matrix (NR::translate(scale_center));
4284         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4285             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4286             n->pos *= t;
4287             n->n.pos *= t;
4288             n->p.pos *= t;
4289             sp_node_update_handles(n, false);
4290         }
4291     }
4293     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4296 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4298     if (!nodepath) return;
4299     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4302 /**
4303  * Flip selected nodes horizontally/vertically.
4304  */
4305 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4307     if (!nodepath || !nodepath->selected) return;
4309     if (g_list_length(nodepath->selected) == 1 && !center) {
4310         // flip handles of the single selected node
4311         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4312         double temp = n->p.pos[axis];
4313         n->p.pos[axis] = n->n.pos[axis];
4314         n->n.pos[axis] = temp;
4315         sp_node_update_handles(n, false);
4316     } else {
4317         // scale nodes as an "object":
4319         NR::Rect box = sp_node_selected_bbox (nodepath);
4320         if (!center) {
4321             center = box.midpoint();
4322         }
4323         NR::Matrix t =
4324             NR::Matrix (NR::translate(- *center)) *
4325             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4326             NR::Matrix (NR::translate(*center));
4328         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4329             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4330             n->pos *= t;
4331             n->n.pos *= t;
4332             n->p.pos *= t;
4333             sp_node_update_handles(n, false);
4334         }
4335     }
4337     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4340 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4342     g_assert (nodepath->selected);
4344     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4345     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4346     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4347         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4348         box.expandTo (n->pos); // contain all selected nodes
4349     }
4350     return box;
4353 //-----------------------------------------------
4354 /**
4355  * Return new subpath under given nodepath.
4356  */
4357 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4359     g_assert(nodepath);
4360     g_assert(nodepath->desktop);
4362    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4364     s->nodepath = nodepath;
4365     s->closed = FALSE;
4366     s->nodes = NULL;
4367     s->first = NULL;
4368     s->last = NULL;
4370     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4371     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4372     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4374     return s;
4377 /**
4378  * Destroy nodes in subpath, then subpath itself.
4379  */
4380 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4382     g_assert(subpath);
4383     g_assert(subpath->nodepath);
4384     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4386     while (subpath->nodes) {
4387         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4388     }
4390     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4392     g_free(subpath);
4395 /**
4396  * Link head to tail in subpath.
4397  */
4398 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4400     g_assert(!sp->closed);
4401     g_assert(sp->last != sp->first);
4402     g_assert(sp->first->code == NR_MOVETO);
4404     sp->closed = TRUE;
4406     //Link the head to the tail
4407     sp->first->p.other = sp->last;
4408     sp->last->n.other  = sp->first;
4409     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4410     sp->first          = sp->last;
4412     //Remove the extra end node
4413     sp_nodepath_node_destroy(sp->last->n.other);
4416 /**
4417  * Open closed (loopy) subpath at node.
4418  */
4419 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4421     g_assert(sp->closed);
4422     g_assert(n->subpath == sp);
4423     g_assert(sp->first == sp->last);
4425     /* We create new startpoint, current node will become last one */
4427    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4428                                                 &n->pos, &n->pos, &n->n.pos);
4431     sp->closed        = FALSE;
4433     //Unlink to make a head and tail
4434     sp->first         = new_path;
4435     sp->last          = n;
4436     n->n.other        = NULL;
4437     new_path->p.other = NULL;
4440 /**
4441  * Return new node in subpath with given properties.
4442  * \param pos Position of node.
4443  * \param ppos Handle position in previous direction
4444  * \param npos Handle position in previous direction
4445  */
4446 Inkscape::NodePath::Node *
4447 sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node *next, Inkscape::NodePath::NodeType type, NRPathcode code, NR::Point *ppos, NR::Point *pos, NR::Point *npos)
4449     g_assert(sp);
4450     g_assert(sp->nodepath);
4451     g_assert(sp->nodepath->desktop);
4453     if (nodechunk == NULL)
4454         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4456     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4458     n->subpath  = sp;
4460     if (type != Inkscape::NodePath::NODE_NONE) {
4461         // use the type from sodipodi:nodetypes
4462         n->type = type;
4463     } else {
4464         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4465             // points are (almost) collinear
4466             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4467                 // endnode, or a node with a retracted handle
4468                 n->type = Inkscape::NodePath::NODE_CUSP;
4469             } else {
4470                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4471             }
4472         } else {
4473             n->type = Inkscape::NodePath::NODE_CUSP;
4474         }
4475     }
4477     n->code     = code;
4478     n->selected = FALSE;
4479     n->pos      = *pos;
4480     n->p.pos    = *ppos;
4481     n->n.pos    = *npos;
4483     n->dragging_out = NULL;
4485     Inkscape::NodePath::Node *prev;
4486     if (next) {
4487         //g_assert(g_list_find(sp->nodes, next));
4488         prev = next->p.other;
4489     } else {
4490         prev = sp->last;
4491     }
4493     if (prev)
4494         prev->n.other = n;
4495     else
4496         sp->first = n;
4498     if (next)
4499         next->p.other = n;
4500     else
4501         sp->last = n;
4503     n->p.other = prev;
4504     n->n.other = next;
4506     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"));
4507     sp_knot_set_position(n->knot, pos, 0);
4509     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4510     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4511     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4512     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4513     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4514     sp_knot_update_ctrl(n->knot);
4516     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4517     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4518     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4519     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4520     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4521     sp_knot_show(n->knot);
4523     // We only create handle knots and lines on demand
4524     n->p.knot = NULL;
4525     n->p.line = NULL;
4526     n->n.knot = NULL;
4527     n->n.line = NULL;
4529     sp->nodes = g_list_prepend(sp->nodes, n);
4531     return n;
4534 /**
4535  * Destroy node and its knots, link neighbors in subpath.
4536  */
4537 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4539     g_assert(node);
4540     g_assert(node->subpath);
4541     g_assert(SP_IS_KNOT(node->knot));
4543    Inkscape::NodePath::SubPath *sp = node->subpath;
4545     if (node->selected) { // first, deselect
4546         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4547         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4548     }
4550     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4552     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4553     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4554     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4555     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4556     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4557     g_object_unref(G_OBJECT(node->knot));
4559     if (node->p.knot) {
4560         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4561         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4562         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4563         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4564         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4565         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4566         g_object_unref(G_OBJECT(node->p.knot));
4567         node->p.knot = NULL;
4568     }
4570     if (node->n.knot) {
4571         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4572         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4573         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4574         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4575         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4576         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4577         g_object_unref(G_OBJECT(node->n.knot));
4578         node->n.knot = NULL;
4579     }
4581     if (node->p.line)
4582         gtk_object_destroy(GTK_OBJECT(node->p.line));
4583     if (node->n.line)
4584         gtk_object_destroy(GTK_OBJECT(node->n.line));
4586     if (sp->nodes) { // there are others nodes on the subpath
4587         if (sp->closed) {
4588             if (sp->first == node) {
4589                 g_assert(sp->last == node);
4590                 sp->first = node->n.other;
4591                 sp->last = sp->first;
4592             }
4593             node->p.other->n.other = node->n.other;
4594             node->n.other->p.other = node->p.other;
4595         } else {
4596             if (sp->first == node) {
4597                 sp->first = node->n.other;
4598                 sp->first->code = NR_MOVETO;
4599             }
4600             if (sp->last == node) sp->last = node->p.other;
4601             if (node->p.other) node->p.other->n.other = node->n.other;
4602             if (node->n.other) node->n.other->p.other = node->p.other;
4603         }
4604     } else { // this was the last node on subpath
4605         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4606     }
4608     g_mem_chunk_free(nodechunk, node);
4611 /**
4612  * Returns one of the node's two sides.
4613  * \param which Indicates which side.
4614  * \return Pointer to previous node side if which==-1, next if which==1.
4615  */
4616 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4618     g_assert(node);
4620     switch (which) {
4621         case -1:
4622             return &node->p;
4623         case 1:
4624             return &node->n;
4625         default:
4626             break;
4627     }
4629     g_assert_not_reached();
4631     return NULL;
4634 /**
4635  * Return the other side of the node, given one of its sides.
4636  */
4637 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4639     g_assert(node);
4641     if (me == &node->p) return &node->n;
4642     if (me == &node->n) return &node->p;
4644     g_assert_not_reached();
4646     return NULL;
4649 /**
4650  * Return NRPathcode on the given side of the node.
4651  */
4652 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4654     g_assert(node);
4656     if (me == &node->p) {
4657         if (node->p.other) return (NRPathcode)node->code;
4658         return NR_MOVETO;
4659     }
4661     if (me == &node->n) {
4662         if (node->n.other) return (NRPathcode)node->n.other->code;
4663         return NR_MOVETO;
4664     }
4666     g_assert_not_reached();
4668     return NR_END;
4671 /**
4672  * Return node with the given index
4673  */
4674 Inkscape::NodePath::Node *
4675 sp_nodepath_get_node_by_index(int index)
4677     Inkscape::NodePath::Node *e = NULL;
4679     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4680     if (!nodepath) {
4681         return e;
4682     }
4684     //find segment
4685     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4687         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4688         int n = g_list_length(sp->nodes);
4689         if (sp->closed) {
4690             n++;
4691         }
4693         //if the piece belongs to this subpath grab it
4694         //otherwise move onto the next subpath
4695         if (index < n) {
4696             e = sp->first;
4697             for (int i = 0; i < index; ++i) {
4698                 e = e->n.other;
4699             }
4700             break;
4701         } else {
4702             if (sp->closed) {
4703                 index -= (n+1);
4704             } else {
4705                 index -= n;
4706             }
4707         }
4708     }
4710     return e;
4713 /**
4714  * Returns plain text meaning of node type.
4715  */
4716 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4718     unsigned retracted = 0;
4719     bool endnode = false;
4721     for (int which = -1; which <= 1; which += 2) {
4722         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4723         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4724             retracted ++;
4725         if (!side->other)
4726             endnode = true;
4727     }
4729     if (retracted == 0) {
4730         if (endnode) {
4731                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4732                 return _("end node");
4733         } else {
4734             switch (node->type) {
4735                 case Inkscape::NodePath::NODE_CUSP:
4736                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4737                     return _("cusp");
4738                 case Inkscape::NodePath::NODE_SMOOTH:
4739                     // TRANSLATORS: "smooth" is an adjective here
4740                     return _("smooth");
4741                 case Inkscape::NodePath::NODE_SYMM:
4742                     return _("symmetric");
4743             }
4744         }
4745     } else if (retracted == 1) {
4746         if (endnode) {
4747             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4748             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4749         } else {
4750             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4751         }
4752     } else {
4753         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4754     }
4756     return NULL;
4759 /**
4760  * Handles content of statusbar as long as node tool is active.
4761  */
4762 void
4763 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4765     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");
4766     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4768     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4769     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4770     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4771     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4773     SPDesktop *desktop = NULL;
4774     if (nodepath) {
4775         desktop = nodepath->desktop;
4776     } else {
4777         desktop = SP_ACTIVE_DESKTOP;
4778     }
4780     SPEventContext *ec = desktop->event_context;
4781     if (!ec) return;
4782     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4783     if (!mc) return;
4785     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4787     if (selected_nodes == 0) {
4788         Inkscape::Selection *sel = desktop->selection;
4789         if (!sel || sel->isEmpty()) {
4790             mc->setF(Inkscape::NORMAL_MESSAGE,
4791                      _("Select a single object to edit its nodes or handles."));
4792         } else {
4793             if (nodepath) {
4794             mc->setF(Inkscape::NORMAL_MESSAGE,
4795                      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.",
4796                               "<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.",
4797                               total_nodes),
4798                      total_nodes);
4799             } else {
4800                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4801                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4802                 } else {
4803                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4804                 }
4805             }
4806         }
4807     } else if (nodepath && selected_nodes == 1) {
4808         mc->setF(Inkscape::NORMAL_MESSAGE,
4809                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4810                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4811                           total_nodes),
4812                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4813     } else {
4814         if (selected_subpaths > 1) {
4815             mc->setF(Inkscape::NORMAL_MESSAGE,
4816                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4817                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4818                               total_nodes),
4819                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4820         } else {
4821             mc->setF(Inkscape::NORMAL_MESSAGE,
4822                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4823                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4824                               total_nodes),
4825                      selected_nodes, total_nodes, when_selected);
4826         }
4827     }
4830 /*
4831  * returns a *copy* of the curve of that object.
4832  */
4833 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4834     if (!object)
4835         return NULL;
4837     SPCurve *curve = NULL;
4838     if (SP_IS_PATH(object)) {
4839         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4840         curve = curve_new->copy();
4841     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4842         const gchar *svgd = object->repr->attribute(key);
4843         if (svgd) {
4844             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4845             SPCurve *curve_new = new SPCurve(pv);
4846             if (curve_new) {
4847                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4848             }
4849         }
4850     }
4852     return curve;
4855 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4856     if (!np || !np->object || !curve)
4857         return;
4859     if (SP_IS_PATH(np->object)) {
4860         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4861             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4862         } else {
4863             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4864         }
4865     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4866         Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) );
4867         if (pathparam) {
4868             pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4869             np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4870         }
4871     }
4874 /**
4875 SPCanvasItem *
4876 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4877     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
4879 **/
4881 /**
4882 SPCanvasItem *
4883 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4884     SPCurve *flash_curve = curve->copy();
4885     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4886     flash_curve->transform(i2d);
4887     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4888     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4889     // unless we also flash the nodes...
4890     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4891     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4892     sp_canvas_item_show(canvasitem);
4893     flash_curve->unref();
4894     return canvasitem;
4897 SPCanvasItem *
4898 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4899     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4900                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4902 **/
4904 SPCanvasItem *
4905 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
4906     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
4907     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
4908     flash_curve->transform(i2d);
4909     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4910     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4911     // unless we also flash the nodes...
4912     guint32 color = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
4913     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4914     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4915     sp_canvas_item_show(canvasitem);
4916     flash_curve->unref();
4917     return canvasitem;
4920 // TODO: Merge this with sp_nodepath_make_helper_item()!
4921 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4922     np->show_helperpath = show;
4924     if (show) {
4925         SPCurve *helper_curve = np->curve->copy();
4926         helper_curve->transform(to_2geom(np->i2d));
4927         if (!np->helper_path) {
4928             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
4930             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4931             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);
4932             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4933             sp_canvas_item_move_to_z(np->helper_path, 0);
4934             sp_canvas_item_show(np->helper_path);
4935         } else {
4936             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4937         }
4938         helper_curve->unref();
4939     } else {
4940         if (np->helper_path) {
4941             GtkObject *temp = np->helper_path;
4942             np->helper_path = NULL;
4943             gtk_object_destroy(temp);
4944         }
4945     }
4948 /* sp_nodepath_make_straight_path:
4949  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4950  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4951  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4952  */
4953 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4954     np->straight_path = true;
4955     np->show_handles = false;
4956     g_message("add code to make the path straight.");
4957     // do sp_nodepath_convert_node_type on all nodes?
4958     // coding tip: search for this text : "Make selected segments lines"
4962 /*
4963   Local Variables:
4964   mode:c++
4965   c-file-style:"stroustrup"
4966   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4967   indent-tabs-mode:nil
4968   fill-column:99
4969   End:
4970 */
4971 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :