Code

6b1c540574fe79251b327cc7d4f2104d674fd628
[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 "libnr/n-art-bpath.h"
25 #include "libnr/nr-path.h"
26 #include "helper/units.h"
27 #include "knot.h"
28 #include "inkscape.h"
29 #include "document.h"
30 #include "sp-namedview.h"
31 #include "desktop.h"
32 #include "desktop-handles.h"
33 #include "snap.h"
34 #include "message-stack.h"
35 #include "message-context.h"
36 #include "node-context.h"
37 #include "shape-editor.h"
38 #include "selection-chemistry.h"
39 #include "selection.h"
40 #include "xml/repr.h"
41 #include "prefs-utils.h"
42 #include "sp-metrics.h"
43 #include "sp-path.h"
44 #include "libnr/nr-matrix-ops.h"
45 #include "splivarot.h"
46 #include "svg/svg.h"
47 #include "verbs.h"
48 #include "display/bezier-utils.h"
49 #include <vector>
50 #include <algorithm>
51 #include <cstring>
52 #include <string>
53 #include "live_effects/lpeobject.h"
54 #include "live_effects/parameter/parameter.h"
55 #include "util/mathfns.h"
56 #include "display/snap-indicator.h"
57 #include "snapped-point.h"
59 class NR::Matrix;
61 /// \todo
62 /// evil evil evil. FIXME: conflict of two different Path classes!
63 /// There is a conflict in the namespace between two classes named Path.
64 /// #include "sp-flowtext.h"
65 /// #include "sp-flowregion.h"
67 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
68 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
69 GType sp_flowregion_get_type (void);
70 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
71 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
72 GType sp_flowtext_get_type (void);
73 // end evil workaround
75 #include "helper/stlport.h"
78 /// \todo fixme: Implement these via preferences */
80 #define NODE_FILL          0xbfbfbf00
81 #define NODE_STROKE        0x000000ff
82 #define NODE_FILL_HI       0xff000000
83 #define NODE_STROKE_HI     0x000000ff
84 #define NODE_FILL_SEL      0x0000ffff
85 #define NODE_STROKE_SEL    0x000000ff
86 #define NODE_FILL_SEL_HI   0xff000000
87 #define NODE_STROKE_SEL_HI 0x000000ff
88 #define KNOT_FILL          0xffffffff
89 #define KNOT_STROKE        0x000000ff
90 #define KNOT_FILL_HI       0xff000000
91 #define KNOT_STROKE_HI     0x000000ff
93 static GMemChunk *nodechunk = NULL;
95 /* Creation from object */
97 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
98 static gchar *parse_nodetypes(gchar const *types, gint length);
100 /* Object updating */
102 static void stamp_repr(Inkscape::NodePath::Path *np);
103 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
104 static gchar *create_typestr(Inkscape::NodePath::Path *np);
106 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
108 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
110 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
112 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
114 /* Adjust handle placement, if the node or the other handle is moved */
115 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
116 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
118 /* Node event callbacks */
119 static void node_clicked(SPKnot *knot, guint state, gpointer data);
120 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
121 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
122 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
124 /* Handle event callbacks */
125 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
126 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
127 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
128 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
129 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
130 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
132 /* Constructors and destructors */
134 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
135 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
136 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
137 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
138 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
139                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
140 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
142 /* Helpers */
144 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
145 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
146 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
148 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
149 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
151 // active_node indicates mouseover node
152 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
154 /**
155  * \brief Creates new nodepath from item
156  */
157 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
159     Inkscape::XML::Node *repr = object->repr;
161     /** \todo
162      * FIXME: remove this. We don't want to edit paths inside flowtext.
163      * Instead we will build our flowtext with cloned paths, so that the
164      * real paths are outside the flowtext and thus editable as usual.
165      */
166     if (SP_IS_FLOWTEXT(object)) {
167         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
168             if SP_IS_FLOWREGION(child) {
169                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
170                 if (grandchild && SP_IS_PATH(grandchild)) {
171                     object = SP_ITEM(grandchild);
172                     break;
173                 }
174             }
175         }
176     }
178     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
180     if (curve == NULL)
181         return NULL;
183     NArtBpath *bpath = sp_curve_first_bpath(curve);
184     gint length = curve->end;
185     if (length == 0) {
186         sp_curve_unref(curve);
187         return NULL; // prevent crash for one-node paths
188     }
190     //Create new nodepath
191     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
192     if (!np) {
193         sp_curve_unref(curve);
194         return NULL;
195     }
197     // Set defaults
198     np->desktop     = desktop;
199     np->object      = object;
200     np->subpaths    = NULL;
201     np->selected    = NULL;
202     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
203     np->livarot_path = NULL;
204     np->local_change = 0;
205     np->show_handles = show_handles;
206     np->helper_path = NULL;
207     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
208     np->helperpath_width = 1.0;
209     np->curve = sp_curve_copy(curve);
210     np->show_helperpath = prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1;
211     np->straight_path = false;
212     if (IS_LIVEPATHEFFECT(object) && item) {
213         np->item = item;
214     } else {
215         np->item = SP_ITEM(object);
216     }
218     // we need to update item's transform from the repr here,
219     // because they may be out of sync when we respond
220     // to a change in repr by regenerating nodepath     --bb
221     sp_object_read_attr(SP_OBJECT(np->item), "transform");
223     np->i2d  = sp_item_i2d_affine(np->item);
224     np->d2i  = np->i2d.inverse();
226     np->repr = repr;
227     if (repr_key_in) { // apparantly the object is an LPEObject
228         np->repr_key = g_strdup(repr_key_in);
229         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
230         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
231         if (lpeparam) {
232             lpeparam->param_setup_nodepath(np);
233         }
234     } else {
235         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
236         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
237             np->repr_key = g_strdup("inkscape:original-d");
239             LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(SP_LPE_ITEM(np->object));
240             if (lpeobj && lpeobj->lpe) {
241                 lpeobj->lpe->setup_nodepath(np);
242             }
243         } else {
244             np->repr_key = g_strdup("d");
245         }
246     }
248     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
249     gchar *typestr = parse_nodetypes(nodetypes, length);
251     // create the subpath(s) from the bpath
252     NArtBpath *b = bpath;
253     while (b->code != NR_END) {
254         b = subpath_from_bpath(np, b, typestr + (b - bpath));
255     }
257     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
258     np->subpaths = g_list_reverse(np->subpaths);
260     g_free(typestr);
261     sp_curve_unref(curve);
263     // create the livarot representation from the same item
264     sp_nodepath_ensure_livarot_path(np);
266     // Draw helper curve
267     if (np->show_helperpath) {
268         SPCurve *helper_curve = sp_curve_copy(np->curve);
269         sp_curve_transform(helper_curve, np->i2d );
270         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
271         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);
272         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
273         sp_canvas_item_move_to_z(np->helper_path, 0);
274         sp_canvas_item_show(np->helper_path);
275         sp_curve_unref(helper_curve);
276     }
278     return np;
281 /**
282  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
283  */
284 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
286     if (!np)  //soft fail, like delete
287         return;
289     while (np->subpaths) {
290         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
291     }
293     //Inform the ShapeEditor that made me, if any, that I am gone.
294     if (np->shape_editor)
295         np->shape_editor->nodepath_destroyed();
297     g_assert(!np->selected);
299     if (np->livarot_path) {
300         delete np->livarot_path;
301         np->livarot_path = NULL;
302     }
304     if (np->helper_path) {
305         GtkObject *temp = np->helper_path;
306         np->helper_path = NULL;
307         gtk_object_destroy(temp);
308     }
309     if (np->curve) {
310         sp_curve_unref(np->curve);
311         np->curve = NULL;
312     }
314     if (np->repr_key) {
315         g_free(np->repr_key);
316         np->repr_key = NULL;
317     }
318     if (np->repr_nodetypes_key) {
319         g_free(np->repr_nodetypes_key);
320         np->repr_nodetypes_key = NULL;
321     }
323     np->desktop = NULL;
325     g_free(np);
329 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
331     if (np && np->livarot_path == NULL) {
332         SPCurve *curve = create_curve(np);
333         NArtBpath *bpath = SP_CURVE_BPATH(curve);
334         np->livarot_path = bpath_to_Path(bpath);
336         if (np->livarot_path)
337             np->livarot_path->ConvertWithBackData(0.01);
339         sp_curve_unref(curve);
340     }
344 /**
345  *  Return the node count of a given NodeSubPath.
346  */
347 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
349     if (!subpath)
350         return 0;
351     gint nodeCount = g_list_length(subpath->nodes);
352     return nodeCount;
355 /**
356  *  Return the node count of a given NodePath.
357  */
358 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
360     if (!np)
361         return 0;
362     gint nodeCount = 0;
363     for (GList *item = np->subpaths ; item ; item=item->next) {
364        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
365         nodeCount += g_list_length(subpath->nodes);
366     }
367     return nodeCount;
370 /**
371  *  Return the subpath count of a given NodePath.
372  */
373 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
375     if (!np)
376         return 0;
377     return g_list_length (np->subpaths);
380 /**
381  *  Return the selected node count of a given NodePath.
382  */
383 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
385     if (!np)
386         return 0;
387     return g_list_length (np->selected);
390 /**
391  *  Return the number of subpaths where nodes are selected in a given NodePath.
392  */
393 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
395     if (!np)
396         return 0;
397     if (!np->selected)
398         return 0;
399     if (!np->selected->next)
400         return 1;
401     gint count = 0;
402     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
403         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
404         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
405             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
406             if (node->selected) {
407                 count ++;
408                 break;
409             }
410         }
411     }
412     return count;
415 /**
416  * Clean up a nodepath after editing.
417  *
418  * Currently we are deleting trivial subpaths.
419  */
420 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
422     GList *badSubPaths = NULL;
424     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
425     for (GList *l = nodepath->subpaths; l ; l=l->next) {
426        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
427        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
428             badSubPaths = g_list_append(badSubPaths, sp);
429     }
431     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
432     //also removes the subpath from nodepath->subpaths
433     for (GList *l = badSubPaths; l ; l=l->next) {
434        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
435         sp_nodepath_subpath_destroy(sp);
436     }
438     g_list_free(badSubPaths);
441 /**
442  * Create new nodepath from b, make it subpath of np.
443  * \param t The node type.
444  * \todo Fixme: t should be a proper type, rather than gchar
445  */
446 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
448     NR::Point ppos, pos, npos;
450     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
452     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
453     bool const closed = (b->code == NR_MOVETO);
455     pos = NR::Point(b->x3, b->y3) * np->i2d;
456     if (b[1].code == NR_CURVETO) {
457         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
458     } else {
459         npos = pos;
460     }
461     Inkscape::NodePath::Node *n;
462     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
463     g_assert(sp->first == n);
464     g_assert(sp->last  == n);
466     b++;
467     t++;
468     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
469         pos = NR::Point(b->x3, b->y3) * np->i2d;
470         if (b->code == NR_CURVETO) {
471             ppos = NR::Point(b->x2, b->y2) * np->i2d;
472         } else {
473             ppos = pos;
474         }
475         if (b[1].code == NR_CURVETO) {
476             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
477         } else {
478             npos = pos;
479         }
480         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
481         b++;
482         t++;
483     }
485     if (closed) sp_nodepath_subpath_close(sp);
487     return b;
490 /**
491  * Convert from sodipodi:nodetypes to new style type string.
492  */
493 static gchar *parse_nodetypes(gchar const *types, gint length)
495     g_assert(length > 0);
497     gchar *typestr = g_new(gchar, length + 1);
499     gint pos = 0;
501     if (types) {
502         for (gint i = 0; types[i] && ( i < length ); i++) {
503             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
504             if (types[i] != '\0') {
505                 switch (types[i]) {
506                     case 's':
507                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
508                         break;
509                     case 'z':
510                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
511                         break;
512                     case 'c':
513                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
514                         break;
515                     default:
516                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
517                         break;
518                 }
519             }
520         }
521     }
523     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
525     return typestr;
528 /**
529  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
530  * updated but repr is not (for speed). Used during curve and node drag.
531  */
532 static void update_object(Inkscape::NodePath::Path *np)
534     g_assert(np);
536     sp_curve_unref(np->curve);
537     np->curve = create_curve(np);
539     sp_nodepath_set_curve(np, np->curve);
541     if (np->show_helperpath) {
542         SPCurve * helper_curve = sp_curve_copy(np->curve);
543         sp_curve_transform(helper_curve, np->i2d );
544         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
545         sp_curve_unref(helper_curve);
546     }
549 /**
550  * Update XML path node with data from path object.
551  */
552 static void update_repr_internal(Inkscape::NodePath::Path *np)
554     g_assert(np);
556     Inkscape::XML::Node *repr = np->object->repr;
558     sp_curve_unref(np->curve);
559     np->curve = create_curve(np);
561     gchar *typestr = create_typestr(np);
562     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
564     // determine if path has an effect applied and write to correct "d" attribute.
565     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
566         np->local_change++;
567         repr->setAttribute(np->repr_key, svgpath);
568     }
570     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
571         np->local_change++;
572         repr->setAttribute(np->repr_nodetypes_key, typestr);
573     }
575     g_free(svgpath);
576     g_free(typestr);
578     if (np->show_helperpath) {
579         SPCurve * helper_curve = sp_curve_copy(np->curve);
580         sp_curve_transform(helper_curve, np->i2d );
581         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
582         sp_curve_unref(helper_curve);
583     }
584  }
586 /**
587  * Update XML path node with data from path object, commit changes forever.
588  */
589 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
591     //fixme: np can be NULL, so check before proceeding
592     g_return_if_fail(np != NULL);
594     if (np->livarot_path) {
595         delete np->livarot_path;
596         np->livarot_path = NULL;
597     }
599     update_repr_internal(np);
600     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
602     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
603                      annotation);
606 /**
607  * Update XML path node with data from path object, commit changes with undo.
608  */
609 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
611     if (np->livarot_path) {
612         delete np->livarot_path;
613         np->livarot_path = NULL;
614     }
616     update_repr_internal(np);
617     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
618                            annotation);
621 /**
622  * Make duplicate of path, replace corresponding XML node in tree, commit.
623  */
624 static void stamp_repr(Inkscape::NodePath::Path *np)
626     g_assert(np);
628     Inkscape::XML::Node *old_repr = np->object->repr;
629     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
631     // remember the position of the item
632     gint pos = old_repr->position();
633     // remember parent
634     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
636     SPCurve *curve = create_curve(np);
637     gchar *typestr = create_typestr(np);
639     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
641     new_repr->setAttribute(np->repr_key, svgpath);
642     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
644     // add the new repr to the parent
645     parent->appendChild(new_repr);
646     // move to the saved position
647     new_repr->setPosition(pos > 0 ? pos : 0);
649     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
650                      _("Stamp"));
652     Inkscape::GC::release(new_repr);
653     g_free(svgpath);
654     g_free(typestr);
655     sp_curve_unref(curve);
658 /**
659  * Create curve from path.
660  */
661 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
663     SPCurve *curve = sp_curve_new();
665     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
666        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
667         sp_curve_moveto(curve,
668                         sp->first->pos * np->d2i);
669        Inkscape::NodePath::Node *n = sp->first->n.other;
670         while (n) {
671             NR::Point const end_pt = n->pos * np->d2i;
672             switch (n->code) {
673                 case NR_LINETO:
674                     sp_curve_lineto(curve, end_pt);
675                     break;
676                 case NR_CURVETO:
677                     sp_curve_curveto(curve,
678                                      n->p.other->n.pos * np->d2i,
679                                      n->p.pos * np->d2i,
680                                      end_pt);
681                     break;
682                 default:
683                     g_assert_not_reached();
684                     break;
685             }
686             if (n != sp->last) {
687                 n = n->n.other;
688             } else {
689                 n = NULL;
690             }
691         }
692         if (sp->closed) {
693             sp_curve_closepath(curve);
694         }
695     }
697     return curve;
700 /**
701  * Convert path type string to sodipodi:nodetypes style.
702  */
703 static gchar *create_typestr(Inkscape::NodePath::Path *np)
705     gchar *typestr = g_new(gchar, 32);
706     gint len = 32;
707     gint pos = 0;
709     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
710        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
712         if (pos >= len) {
713             typestr = g_renew(gchar, typestr, len + 32);
714             len += 32;
715         }
717         typestr[pos++] = 'c';
719        Inkscape::NodePath::Node *n;
720         n = sp->first->n.other;
721         while (n) {
722             gchar code;
724             switch (n->type) {
725                 case Inkscape::NodePath::NODE_CUSP:
726                     code = 'c';
727                     break;
728                 case Inkscape::NodePath::NODE_SMOOTH:
729                     code = 's';
730                     break;
731                 case Inkscape::NodePath::NODE_SYMM:
732                     code = 'z';
733                     break;
734                 default:
735                     g_assert_not_reached();
736                     code = '\0';
737                     break;
738             }
740             if (pos >= len) {
741                 typestr = g_renew(gchar, typestr, len + 32);
742                 len += 32;
743             }
745             typestr[pos++] = code;
747             if (n != sp->last) {
748                 n = n->n.other;
749             } else {
750                 n = NULL;
751             }
752         }
753     }
755     if (pos >= len) {
756         typestr = g_renew(gchar, typestr, len + 1);
757         len += 1;
758     }
760     typestr[pos++] = '\0';
762     return typestr;
765 /**
766  * Returns current path in context. // later eliminate this function at all!
767  */
768 static Inkscape::NodePath::Path *sp_nodepath_current()
770     if (!SP_ACTIVE_DESKTOP) {
771         return NULL;
772     }
774     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
776     if (!SP_IS_NODE_CONTEXT(event_context)) {
777         return NULL;
778     }
780     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
785 /**
786  \brief Fills node and handle positions for three nodes, splitting line
787   marked by end at distance t.
788  */
789 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
791     g_assert(new_path != NULL);
792     g_assert(end      != NULL);
794     g_assert(end->p.other == new_path);
795    Inkscape::NodePath::Node *start = new_path->p.other;
796     g_assert(start);
798     if (end->code == NR_LINETO) {
799         new_path->type =Inkscape::NodePath::NODE_CUSP;
800         new_path->code = NR_LINETO;
801         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
802     } else {
803         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
804         new_path->code = NR_CURVETO;
805         gdouble s      = 1 - t;
806         for (int dim = 0; dim < 2; dim++) {
807             NR::Coord const f000 = start->pos[dim];
808             NR::Coord const f001 = start->n.pos[dim];
809             NR::Coord const f011 = end->p.pos[dim];
810             NR::Coord const f111 = end->pos[dim];
811             NR::Coord const f00t = s * f000 + t * f001;
812             NR::Coord const f01t = s * f001 + t * f011;
813             NR::Coord const f11t = s * f011 + t * f111;
814             NR::Coord const f0tt = s * f00t + t * f01t;
815             NR::Coord const f1tt = s * f01t + t * f11t;
816             NR::Coord const fttt = s * f0tt + t * f1tt;
817             start->n.pos[dim]    = f00t;
818             new_path->p.pos[dim] = f0tt;
819             new_path->pos[dim]   = fttt;
820             new_path->n.pos[dim] = f1tt;
821             end->p.pos[dim]      = f11t;
822         }
823     }
826 /**
827  * Adds new node on direct line between two nodes, activates handles of all
828  * three nodes.
829  */
830 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
832     g_assert(end);
833     g_assert(end->subpath);
834     g_assert(g_list_find(end->subpath->nodes, end));
836    Inkscape::NodePath::Node *start = end->p.other;
837     g_assert( start->n.other == end );
838    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
839                                                end,
840                                                (NRPathcode)end->code == NR_LINETO?
841                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
842                                                (NRPathcode)end->code,
843                                                &start->pos, &start->pos, &start->n.pos);
844     sp_nodepath_line_midpoint(newnode, end, t);
846     sp_node_adjust_handles(start);
847     sp_node_update_handles(start);
848     sp_node_update_handles(newnode);
849     sp_node_adjust_handles(end);
850     sp_node_update_handles(end);
852     return newnode;
855 /**
856 \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
857 */
858 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
860     g_assert(node);
861     g_assert(node->subpath);
862     g_assert(g_list_find(node->subpath->nodes, node));
864    Inkscape::NodePath::SubPath *sp = node->subpath;
865     Inkscape::NodePath::Path *np    = sp->nodepath;
867     if (sp->closed) {
868         sp_nodepath_subpath_open(sp, node);
869         return sp->first;
870     } else {
871         // no break for end nodes
872         if (node == sp->first) return NULL;
873         if (node == sp->last ) return NULL;
875         // create a new subpath
876        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
878         // duplicate the break node as start of the new subpath
879        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
881         while (node->n.other) { // copy the remaining nodes into the new subpath
882            Inkscape::NodePath::Node *n  = node->n.other;
883            Inkscape::NodePath::Node *nn = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
884             if (n->selected) {
885                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
886             }
887             sp_nodepath_node_destroy(n); // remove the point on the original subpath
888         }
890         return newnode;
891     }
894 /**
895  * Duplicate node and connect to neighbours.
896  */
897 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
899     g_assert(node);
900     g_assert(node->subpath);
901     g_assert(g_list_find(node->subpath->nodes, node));
903    Inkscape::NodePath::SubPath *sp = node->subpath;
905     NRPathcode code = (NRPathcode) node->code;
906     if (code == NR_MOVETO) { // if node is the endnode,
907         node->code = NR_LINETO; // new one is inserted before it, so change that to line
908     }
910     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
912     if (!node->n.other || !node->p.other) // if node is an endnode, select it
913         return node;
914     else
915         return newnode; // otherwise select the newly created node
918 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
920     node->p.pos = (node->pos + (node->pos - node->n.pos));
923 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
925     node->n.pos = (node->pos + (node->pos - node->p.pos));
928 /**
929  * Change line type at node, with side effects on neighbours.
930  */
931 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
933     g_assert(end);
934     g_assert(end->subpath);
935     g_assert(end->p.other);
937     if (end->code == static_cast< guint > ( code ) )
938         return;
940    Inkscape::NodePath::Node *start = end->p.other;
942     end->code = code;
944     if (code == NR_LINETO) {
945         if (start->code == NR_LINETO) {
946             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
947         }
948         if (end->n.other) {
949             if (end->n.other->code == NR_LINETO) {
950                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
951             }
952         }
953     } else {
954         NR::Point delta = end->pos - start->pos;
955         start->n.pos = start->pos + delta / 3;
956         end->p.pos = end->pos - delta / 3;
957         sp_node_adjust_handle(start, 1);
958         sp_node_adjust_handle(end, -1);
959     }
961     sp_node_update_handles(start);
962     sp_node_update_handles(end);
965 /**
966  * Change node type, and its handles accordingly.
967  */
968 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
970     g_assert(node);
971     g_assert(node->subpath);
973     if ((node->p.other != NULL) && (node->n.other != NULL)) {
974         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
975             type =Inkscape::NodePath::NODE_CUSP;
976         }
977     }
979     node->type = type;
981     if (node->type == Inkscape::NodePath::NODE_CUSP) {
982         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
983         node->knot->setSize (node->selected? 11 : 9);
984         sp_knot_update_ctrl(node->knot);
985     } else {
986         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
987         node->knot->setSize (node->selected? 9 : 7);
988         sp_knot_update_ctrl(node->knot);
989     }
991     // if one of handles is mouseovered, preserve its position
992     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
993         sp_node_adjust_handle(node, 1);
994     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
995         sp_node_adjust_handle(node, -1);
996     } else {
997         sp_node_adjust_handles(node);
998     }
1000     sp_node_update_handles(node);
1002     sp_nodepath_update_statusbar(node->subpath->nodepath);
1004     return node;
1007 bool
1008 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1010         Inkscape::NodePath::Node *othernode = side->other;
1011         if (!othernode)
1012             return false;
1013         NRPathcode const code = sp_node_path_code_from_side(node, side);
1014         if (code == NR_LINETO)
1015             return true;
1016         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1017         if (&node->p == side) {
1018             other_to_me = &othernode->n;
1019         } else if (&node->n == side) {
1020             other_to_me = &othernode->p;
1021         } 
1022         if (!other_to_me)
1023             return false;
1024         bool is_line = 
1025              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1026               NR::L2(node->pos - side->pos) < 1e-6);
1027         return is_line;
1030 /**
1031  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1032  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1033  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1034  * If already cusp and set to cusp, retracts handles.
1035 */
1036 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1038     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1040 /* 
1041   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1042  
1043         if (two_handles) {
1044             // do nothing, adjust_handles called via set_node_type will line them up
1045         } else if (one_handle) {
1046             if (opposite_to_handle_is_line) {
1047                 if (lined_up) {
1048                     // already half-smooth; pull opposite handle too making it fully smooth
1049                 } else {
1050                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1051                 }
1052             } else {
1053                 // pull opposite handle in line with the existing one
1054             }
1055         } else if (no_handles) {
1056             if (both_segments_are_lines OR both_segments_are_curves) {
1057                 //pull both handles
1058             } else {
1059                 // pull the handle opposite to line segment, making node half-smooth
1060             }
1061         }
1062 */
1063         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1064         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1065         bool p_is_line = sp_node_side_is_line(node, &node->p);
1066         bool n_is_line = sp_node_side_is_line(node, &node->n);
1068         if (p_has_handle && n_has_handle) {
1069             // do nothing, adjust_handles will line them up
1070         } else if (p_has_handle || n_has_handle) {
1071             if (p_has_handle && n_is_line) {
1072                 Radial line (node->n.other->pos - node->pos);
1073                 Radial handle (node->pos - node->p.pos);
1074                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1075                     // already half-smooth; pull opposite handle too making it fully smooth
1076                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1077                 } else {
1078                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1079                 }
1080             } else if (n_has_handle && p_is_line) {
1081                 Radial line (node->p.other->pos - node->pos);
1082                 Radial handle (node->pos - node->n.pos);
1083                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1084                     // already half-smooth; pull opposite handle too making it fully smooth
1085                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1086                 } else {
1087                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1088                 }
1089             } else if (p_has_handle && node->n.other) {
1090                 // pull n handle
1091                 node->n.other->code = NR_CURVETO;
1092                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1093                     NR::L2(node->p.pos - node->pos) :
1094                     NR::L2(node->n.other->pos - node->pos) / 3;
1095                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1096             } else if (n_has_handle && node->p.other) {
1097                 // pull p handle
1098                 node->code = NR_CURVETO;
1099                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1100                     NR::L2(node->n.pos - node->pos) :
1101                     NR::L2(node->p.other->pos - node->pos) / 3;
1102                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1103             }
1104         } else if (!p_has_handle && !n_has_handle) {
1105             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1106                 // no handles, but both segments are either lnes or curves:
1107                 //pull both handles
1109                 // convert both to curves:
1110                 node->code = NR_CURVETO;
1111                 node->n.other->code = NR_CURVETO;
1113                 NR::Point leg_prev = node->pos - node->p.other->pos;
1114                 NR::Point leg_next = node->pos - node->n.other->pos;
1116                 double norm_leg_prev = L2(leg_prev);
1117                 double norm_leg_next = L2(leg_next);
1119                 NR::Point delta;
1120                 if (norm_leg_next > 0.0) {
1121                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1122                     (&delta)->normalize();
1123                 }
1125                 if (type == Inkscape::NodePath::NODE_SYMM) {
1126                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1127                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1128                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1129                 } else {
1130                     // length of handle is proportional to distance to adjacent node
1131                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1132                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1133                 }
1135             } else {
1136                 // pull the handle opposite to line segment, making it half-smooth
1137                 if (p_is_line && node->n.other) {
1138                     if (type != Inkscape::NodePath::NODE_SYMM) {
1139                         // pull n handle
1140                         node->n.other->code = NR_CURVETO;
1141                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1142                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1143                     }
1144                 } else if (n_is_line && node->p.other) {
1145                     if (type != Inkscape::NodePath::NODE_SYMM) {
1146                         // pull p handle
1147                         node->code = NR_CURVETO;
1148                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1149                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1150                     }
1151                 }
1152             }
1153         }
1154     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1155         // cusping a cusp: retract nodes
1156         node->p.pos = node->pos;
1157         node->n.pos = node->pos;
1158     }
1160     sp_nodepath_set_node_type (node, type);
1163 /**
1164  * Move node to point, and adjust its and neighbouring handles.
1165  */
1166 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1168     NR::Point delta = p - node->pos;
1169     node->pos = p;
1171     node->p.pos += delta;
1172     node->n.pos += delta;
1174     Inkscape::NodePath::Node *node_p = NULL;
1175     Inkscape::NodePath::Node *node_n = NULL;
1177     if (node->p.other) {
1178         if (node->code == NR_LINETO) {
1179             sp_node_adjust_handle(node, 1);
1180             sp_node_adjust_handle(node->p.other, -1);
1181             node_p = node->p.other;
1182         }
1183     }
1184     if (node->n.other) {
1185         if (node->n.other->code == NR_LINETO) {
1186             sp_node_adjust_handle(node, -1);
1187             sp_node_adjust_handle(node->n.other, 1);
1188             node_n = node->n.other;
1189         }
1190     }
1192     // this function is only called from batch movers that will update display at the end
1193     // themselves, so here we just move all the knots without emitting move signals, for speed
1194     sp_node_update_handles(node, false);
1195     if (node_n) {
1196         sp_node_update_handles(node_n, false);
1197     }
1198     if (node_p) {
1199         sp_node_update_handles(node_p, false);
1200     }
1203 /**
1204  * Call sp_node_moveto() for node selection and handle possible snapping.
1205  */
1206 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1207                                             bool const snap = true)
1209     NR::Coord best = NR_HUGE;
1210     NR::Point delta(dx, dy);
1211     NR::Point best_pt = delta;
1212     Inkscape::SnappedPoint best_abs;
1214     
1215     if (snap) {    
1216         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1217          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1218          * must provide that information. */
1219           
1220         // Build a list of the unselected nodes to which the snapper should snap 
1221         std::vector<NR::Point> unselected_nodes;
1222         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1223             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1224             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1225                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1226                 if (!node->selected) {
1227                     unselected_nodes.push_back(node->pos);
1228                 }    
1229             }
1230         }        
1231         
1232         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1233         
1234         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1235             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1236             m.setup(nodepath->desktop, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1237             Inkscape::SnappedPoint s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);            
1238             if (s.getDistance() < best) {
1239                 best = s.getDistance();
1240                 best_abs = s;
1241                 best_pt = s.getPoint() - n->pos;
1242             }
1243         }
1244                         
1245         if (best_abs.getSnapped()) {
1246             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1247         }
1248     }
1250     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1251         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1252         sp_node_moveto(n, n->pos + best_pt);
1253     }
1255     // do not update repr here so that node dragging is acceptably fast
1256     update_object(nodepath);
1259 /**
1260 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1261 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1262 near x = 0.
1263  */
1264 double
1265 sculpt_profile (double x, double alpha, guint profile)
1267     if (x >= 1)
1268         return 0;
1269     if (x <= 0)
1270         return 1;
1272     switch (profile) {
1273         case SCULPT_PROFILE_LINEAR:
1274         return 1 - x;
1275         case SCULPT_PROFILE_BELL:
1276         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1277         case SCULPT_PROFILE_ELLIPTIC:
1278         return sqrt(1 - x*x);
1279     }
1281     return 1;
1284 double
1285 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1287     // extremely primitive for now, don't have time to look for the real one
1288     double lower = NR::L2(b - a);
1289     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1290     return (lower + upper)/2;
1293 void
1294 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1296     n->pos = n->origin + delta;
1297     n->n.pos = n->n.origin + delta_n;
1298     n->p.pos = n->p.origin + delta_p;
1299     sp_node_adjust_handles(n);
1300     sp_node_update_handles(n, false);
1303 /**
1304  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1305  * on how far they are from the dragged node n.
1306  */
1307 static void
1308 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1310     g_assert (n);
1311     g_assert (nodepath);
1312     g_assert (n->subpath->nodepath == nodepath);
1314     double pressure = n->knot->pressure;
1315     if (pressure == 0)
1316         pressure = 0.5; // default
1317     pressure = CLAMP (pressure, 0.2, 0.8);
1319     // map pressure to alpha = 1/5 ... 5
1320     double alpha = 1 - 2 * fabs(pressure - 0.5);
1321     if (pressure > 0.5)
1322         alpha = 1/alpha;
1324     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1326     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1327         // Only one subpath has selected nodes:
1328         // use linear mode, where the distance from n to node being dragged is calculated along the path
1330         double n_sel_range = 0, p_sel_range = 0;
1331         guint n_nodes = 0, p_nodes = 0;
1332         guint n_sel_nodes = 0, p_sel_nodes = 0;
1334         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1335         {
1336             double n_range = 0, p_range = 0;
1337             bool n_going = true, p_going = true;
1338             Inkscape::NodePath::Node *n_node = n;
1339             Inkscape::NodePath::Node *p_node = n;
1340             do {
1341                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1342                 if (n_node && n_going)
1343                     n_node = n_node->n.other;
1344                 if (n_node == NULL) {
1345                     n_going = false;
1346                 } else {
1347                     n_nodes ++;
1348                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1349                     if (n_node->selected) {
1350                         n_sel_nodes ++;
1351                         n_sel_range = n_range;
1352                     }
1353                     if (n_node == p_node) {
1354                         n_going = false;
1355                         p_going = false;
1356                     }
1357                 }
1358                 if (p_node && p_going)
1359                     p_node = p_node->p.other;
1360                 if (p_node == NULL) {
1361                     p_going = false;
1362                 } else {
1363                     p_nodes ++;
1364                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1365                     if (p_node->selected) {
1366                         p_sel_nodes ++;
1367                         p_sel_range = p_range;
1368                     }
1369                     if (p_node == n_node) {
1370                         n_going = false;
1371                         p_going = false;
1372                     }
1373                 }
1374             } while (n_going || p_going);
1375         }
1377         // Second pass: actually move nodes in this subpath
1378         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1379         {
1380             double n_range = 0, p_range = 0;
1381             bool n_going = true, p_going = true;
1382             Inkscape::NodePath::Node *n_node = n;
1383             Inkscape::NodePath::Node *p_node = n;
1384             do {
1385                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1386                 if (n_node && n_going)
1387                     n_node = n_node->n.other;
1388                 if (n_node == NULL) {
1389                     n_going = false;
1390                 } else {
1391                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1392                     if (n_node->selected) {
1393                         sp_nodepath_move_node_and_handles (n_node,
1394                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1395                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1396                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1397                     }
1398                     if (n_node == p_node) {
1399                         n_going = false;
1400                         p_going = false;
1401                     }
1402                 }
1403                 if (p_node && p_going)
1404                     p_node = p_node->p.other;
1405                 if (p_node == NULL) {
1406                     p_going = false;
1407                 } else {
1408                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1409                     if (p_node->selected) {
1410                         sp_nodepath_move_node_and_handles (p_node,
1411                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1412                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1413                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1414                     }
1415                     if (p_node == n_node) {
1416                         n_going = false;
1417                         p_going = false;
1418                     }
1419                 }
1420             } while (n_going || p_going);
1421         }
1423     } else {
1424         // Multiple subpaths have selected nodes:
1425         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1426         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1427         // fix the pear-like shape when sculpting e.g. a ring
1429         // First pass: calculate range
1430         gdouble direct_range = 0;
1431         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1432             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1433             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1434                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1435                 if (node->selected) {
1436                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1437                 }
1438             }
1439         }
1441         // Second pass: actually move nodes
1442         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1443             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1444             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1445                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1446                 if (node->selected) {
1447                     if (direct_range > 1e-6) {
1448                         sp_nodepath_move_node_and_handles (node,
1449                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1450                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1451                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1452                     } else {
1453                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1454                     }
1456                 }
1457             }
1458         }
1459     }
1461     // do not update repr here so that node dragging is acceptably fast
1462     update_object(nodepath);
1466 /**
1467  * Move node selection to point, adjust its and neighbouring handles,
1468  * handle possible snapping, and commit the change with possible undo.
1469  */
1470 void
1471 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1473     if (!nodepath) return;
1475     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1477     if (dx == 0) {
1478         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1479     } else if (dy == 0) {
1480         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1481     } else {
1482         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1483     }
1486 /**
1487  * Move node selection off screen and commit the change.
1488  */
1489 void
1490 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1492     // borrowed from sp_selection_move_screen in selection-chemistry.c
1493     // we find out the current zoom factor and divide deltas by it
1494     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1496     gdouble zoom = desktop->current_zoom();
1497     gdouble zdx = dx / zoom;
1498     gdouble zdy = dy / zoom;
1500     if (!nodepath) return;
1502     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1504     if (dx == 0) {
1505         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1506     } else if (dy == 0) {
1507         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1508     } else {
1509         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1510     }
1513 /**
1514  * Move selected nodes to the absolute position given
1515  */
1516 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1518     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1519         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1520         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1521         sp_node_moveto(n, npos);
1522     }
1524     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1527 /**
1528  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1529  */
1530 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1532     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1533     g_return_val_if_fail(nodepath->selected, no_coord);
1535     // determine coordinate of first selected node
1536     GList *nsel = nodepath->selected;
1537     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1538     NR::Coord coord = n->pos[axis];
1539     bool coincide = true;
1541     // compare it to the coordinates of all the other selected nodes
1542     for (GList *l = nsel->next; l != NULL; l = l->next) {
1543         n = (Inkscape::NodePath::Node *) l->data;
1544         if (n->pos[axis] != coord) {
1545             coincide = false;
1546         }
1547     }
1548     if (coincide) {
1549         return coord;
1550     } else {
1551         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1552         // currently we return the coordinate of the bounding box midpoint because I don't know how
1553         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1554         return bbox.midpoint()[axis];
1555     }
1558 /** If they don't yet exist, creates knot and line for the given side of the node */
1559 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1561     if (!side->knot) {
1562         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"));
1564         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1565         side->knot->setSize (7);
1566         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1567         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1568         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1569         sp_knot_update_ctrl(side->knot);
1571         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1572         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1573         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1574         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1575         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1576         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1577     }
1579     if (!side->line) {
1580         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1581                                         SP_TYPE_CTRLLINE, NULL);
1582     }
1585 /**
1586  * Ensure the given handle of the node is visible/invisible, update its screen position
1587  */
1588 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1590     g_assert(node != NULL);
1592    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1593     NRPathcode code = sp_node_path_code_from_side(node, side);
1595     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1597     if (show_handle) {
1598         if (!side->knot) { // No handle knot at all
1599             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1600             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1601             side->knot->pos = side->pos;
1602             if (side->knot->item)
1603                 SP_CTRL(side->knot->item)->moveto(side->pos);
1604             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1605             sp_knot_show(side->knot);
1606         } else {
1607             if (side->knot->pos != side->pos) { // only if it's really moved
1608                 if (fire_move_signals) {
1609                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1610                 } else {
1611                     sp_knot_moveto(side->knot, &side->pos);
1612                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1613                 }
1614             }
1615             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1616                 sp_knot_show(side->knot);
1617             }
1618         }
1619         sp_canvas_item_show(side->line);
1620     } else {
1621         if (side->knot) {
1622             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1623                 sp_knot_hide(side->knot);
1624             }
1625         }
1626         if (side->line) {
1627             sp_canvas_item_hide(side->line);
1628         }
1629     }
1632 /**
1633  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1634  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1635  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1636  * updated; otherwise, just move the knots silently (used in batch moves).
1637  */
1638 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1640     g_assert(node != NULL);
1642     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1643         sp_knot_show(node->knot);
1644     }
1646     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1647         if (fire_move_signals)
1648             sp_knot_set_position(node->knot, &node->pos, 0);
1649         else
1650             sp_knot_moveto(node->knot, &node->pos);
1651     }
1653     gboolean show_handles = node->selected;
1654     if (node->p.other != NULL) {
1655         if (node->p.other->selected) show_handles = TRUE;
1656     }
1657     if (node->n.other != NULL) {
1658         if (node->n.other->selected) show_handles = TRUE;
1659     }
1661     if (node->subpath->nodepath->show_handles == false)
1662         show_handles = FALSE;
1664     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1665     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1668 /**
1669  * Call sp_node_update_handles() for all nodes on subpath.
1670  */
1671 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1673     g_assert(subpath != NULL);
1675     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1676         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1677     }
1680 /**
1681  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1682  */
1683 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1685     g_assert(nodepath != NULL);
1687     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1688         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1689     }
1692 void
1693 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1695     if (nodepath == NULL) return;
1697     nodepath->show_handles = show;
1698     sp_nodepath_update_handles(nodepath);
1701 /**
1702  * Adds all selected nodes in nodepath to list.
1703  */
1704 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1706     StlConv<Node *>::list(l, selected);
1707 /// \todo this adds a copying, rework when the selection becomes a stl list
1710 /**
1711  * Align selected nodes on the specified axis.
1712  */
1713 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1715     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1716         return;
1717     }
1719     if ( !nodepath->selected->next ) { // only one node selected
1720         return;
1721     }
1722    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1723     NR::Point dest(pNode->pos);
1724     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1725         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1726         if (pNode) {
1727             dest[axis] = pNode->pos[axis];
1728             sp_node_moveto(pNode, dest);
1729         }
1730     }
1732     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1735 /// Helper struct.
1736 struct NodeSort
1738    Inkscape::NodePath::Node *_node;
1739     NR::Coord _coord;
1740     /// \todo use vectorof pointers instead of calling copy ctor
1741     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1742         _node(node), _coord(node->pos[axis])
1743     {}
1745 };
1747 static bool operator<(NodeSort const &a, NodeSort const &b)
1749     return (a._coord < b._coord);
1752 /**
1753  * Distribute selected nodes on the specified axis.
1754  */
1755 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1757     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1758         return;
1759     }
1761     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1762         return;
1763     }
1765    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1766     std::vector<NodeSort> sorted;
1767     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1768         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1769         if (pNode) {
1770             NodeSort n(pNode, axis);
1771             sorted.push_back(n);
1772             //dest[axis] = pNode->pos[axis];
1773             //sp_node_moveto(pNode, dest);
1774         }
1775     }
1776     std::sort(sorted.begin(), sorted.end());
1777     unsigned int len = sorted.size();
1778     //overall bboxes span
1779     float dist = (sorted.back()._coord -
1780                   sorted.front()._coord);
1781     //new distance between each bbox
1782     float step = (dist) / (len - 1);
1783     float pos = sorted.front()._coord;
1784     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1785           it < sorted.end();
1786           it ++ )
1787     {
1788         NR::Point dest((*it)._node->pos);
1789         dest[axis] = pos;
1790         sp_node_moveto((*it)._node, dest);
1791         pos += step;
1792     }
1794     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1798 /**
1799  * Call sp_nodepath_line_add_node() for all selected segments.
1800  */
1801 void
1802 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1804     if (!nodepath) {
1805         return;
1806     }
1808     GList *nl = NULL;
1810     int n_added = 0;
1812     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1813        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1814         g_assert(t->selected);
1815         if (t->p.other && t->p.other->selected) {
1816             nl = g_list_prepend(nl, t);
1817         }
1818     }
1820     while (nl) {
1821        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1822        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1823        sp_nodepath_node_select(n, TRUE, FALSE);
1824        n_added ++;
1825        nl = g_list_remove(nl, t);
1826     }
1828     /** \todo fixme: adjust ? */
1829     sp_nodepath_update_handles(nodepath);
1831     if (n_added > 1) {
1832         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1833     } else if (n_added > 0) {
1834         sp_nodepath_update_repr(nodepath, _("Add node"));
1835     }
1837     sp_nodepath_update_statusbar(nodepath);
1840 /**
1841  * Select segment nearest to point
1842  */
1843 void
1844 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1846     if (!nodepath) {
1847         return;
1848     }
1850     sp_nodepath_ensure_livarot_path(nodepath);
1851     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1852     if (!maybe_position) {
1853         return;
1854     }
1855     Path::cut_position position = *maybe_position;
1857     //find segment to segment
1858     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1860     //fixme: this can return NULL, so check before proceeding.
1861     g_return_if_fail(e != NULL);
1863     gboolean force = FALSE;
1864     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1865         force = TRUE;
1866     }
1867     sp_nodepath_node_select(e, (gboolean) toggle, force);
1868     if (e->p.other)
1869         sp_nodepath_node_select(e->p.other, TRUE, force);
1871     sp_nodepath_update_handles(nodepath);
1873     sp_nodepath_update_statusbar(nodepath);
1876 /**
1877  * Add a node nearest to point
1878  */
1879 void
1880 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1882     if (!nodepath) {
1883         return;
1884     }
1886     sp_nodepath_ensure_livarot_path(nodepath);
1887     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1888     if (!maybe_position) {
1889         return;
1890     }
1891     Path::cut_position position = *maybe_position;
1893     //find segment to split
1894     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1896     //don't know why but t seems to flip for lines
1897     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1898         position.t = 1.0 - position.t;
1899     }
1900     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1901     sp_nodepath_node_select(n, FALSE, TRUE);
1903     /* fixme: adjust ? */
1904     sp_nodepath_update_handles(nodepath);
1906     sp_nodepath_update_repr(nodepath, _("Add node"));
1908     sp_nodepath_update_statusbar(nodepath);
1911 /*
1912  * Adjusts a segment so that t moves by a certain delta for dragging
1913  * converts lines to curves
1914  *
1915  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1916  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1917  */
1918 void
1919 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1921     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1923     //fixme: e and e->p can be NULL, so check for those before proceeding
1924     g_return_if_fail(e != NULL);
1925     g_return_if_fail(&e->p != NULL);
1927     /* feel good is an arbitrary parameter that distributes the delta between handles
1928      * if t of the drag point is less than 1/6 distance form the endpoint only
1929      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1930      */
1931     double feel_good;
1932     if (t <= 1.0 / 6.0)
1933         feel_good = 0;
1934     else if (t <= 0.5)
1935         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1936     else if (t <= 5.0 / 6.0)
1937         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1938     else
1939         feel_good = 1;
1941     //if we're dragging a line convert it to a curve
1942     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1943         sp_nodepath_set_line_type(e, NR_CURVETO);
1944     }
1946     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1947     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1948     e->p.other->n.pos += offsetcoord0;
1949     e->p.pos += offsetcoord1;
1951     // adjust handles of adjacent nodes where necessary
1952     sp_node_adjust_handle(e,1);
1953     sp_node_adjust_handle(e->p.other,-1);
1955     sp_nodepath_update_handles(e->subpath->nodepath);
1957     update_object(e->subpath->nodepath);
1959     sp_nodepath_update_statusbar(e->subpath->nodepath);
1963 /**
1964  * Call sp_nodepath_break() for all selected segments.
1965  */
1966 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1968     if (!nodepath) return;
1970     GList *temp = NULL;
1971     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1972        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1973        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1974         if (nn == NULL) continue; // no break, no new node
1975         temp = g_list_prepend(temp, nn);
1976     }
1978     if (temp) {
1979         sp_nodepath_deselect(nodepath);
1980     }
1981     for (GList *l = temp; l != NULL; l = l->next) {
1982         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1983     }
1985     sp_nodepath_update_handles(nodepath);
1987     sp_nodepath_update_repr(nodepath, _("Break path"));
1990 /**
1991  * Duplicate the selected node(s).
1992  */
1993 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1995     if (!nodepath) {
1996         return;
1997     }
1999     GList *temp = NULL;
2000     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2001        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2002        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2003         if (nn == NULL) continue; // could not duplicate
2004         temp = g_list_prepend(temp, nn);
2005     }
2007     if (temp) {
2008         sp_nodepath_deselect(nodepath);
2009     }
2010     for (GList *l = temp; l != NULL; l = l->next) {
2011         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2012     }
2014     sp_nodepath_update_handles(nodepath);
2016     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2019 /**
2020  *  Internal function to join two nodes by merging them into one.
2021  */
2022 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2024     /* a and b are endpoints */
2026     // if one of the two nodes is mouseovered, fix its position
2027     NR::Point c;
2028     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2029         c = a->pos;
2030     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2031         c = b->pos;
2032     } else {
2033         // otherwise, move joined node to the midpoint
2034         c = (a->pos + b->pos) / 2;
2035     }
2037     if (a->subpath == b->subpath) {
2038        Inkscape::NodePath::SubPath *sp = a->subpath;
2039         sp_nodepath_subpath_close(sp);
2040         sp_node_moveto (sp->first, c);
2042         sp_nodepath_update_handles(sp->nodepath);
2043         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2044         return;
2045     }
2047     /* a and b are separate subpaths */
2048     Inkscape::NodePath::SubPath *sa = a->subpath;
2049     Inkscape::NodePath::SubPath *sb = b->subpath;
2050     NR::Point p;
2051     Inkscape::NodePath::Node *n;
2052     NRPathcode code;
2053     if (a == sa->first) {
2054         // we will now reverse sa, so that a is its last node, not first, and drop that node
2055         p = sa->first->n.pos;
2056         code = (NRPathcode)sa->first->n.other->code;
2057         // create new subpath
2058        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2059        // create a first moveto node on it
2060         n = sa->last;
2061         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2062         n = n->p.other;
2063         if (n == sa->first) n = NULL;
2064         while (n) {
2065             // copy the rest of the nodes from sa to t, going backwards
2066             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2067             n = n->p.other;
2068             if (n == sa->first) n = NULL;
2069         }
2070         // replace sa with t
2071         sp_nodepath_subpath_destroy(sa);
2072         sa = t;
2073     } else if (a == sa->last) {
2074         // a is already last, just drop it
2075         p = sa->last->p.pos;
2076         code = (NRPathcode)sa->last->code;
2077         sp_nodepath_node_destroy(sa->last);
2078     } else {
2079         code = NR_END;
2080         g_assert_not_reached();
2081     }
2083     if (b == sb->first) {
2084         // copy all nodes from b to a, forward 
2085         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2086         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2087             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2088         }
2089     } else if (b == sb->last) {
2090         // copy all nodes from b to a, backward 
2091         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2092         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2093             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2094         }
2095     } else {
2096         g_assert_not_reached();
2097     }
2098     /* and now destroy sb */
2100     sp_nodepath_subpath_destroy(sb);
2102     sp_nodepath_update_handles(sa->nodepath);
2104     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2106     sp_nodepath_update_statusbar(nodepath);
2109 /**
2110  *  Internal function to join two nodes by adding a segment between them.
2111  */
2112 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2114     if (a->subpath == b->subpath) {
2115        Inkscape::NodePath::SubPath *sp = a->subpath;
2117         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2118         sp->closed = TRUE;
2120         sp->first->p.other = sp->last;
2121         sp->last->n.other  = sp->first;
2123         sp_node_handle_mirror_p_to_n(sp->last);
2124         sp_node_handle_mirror_n_to_p(sp->first);
2126         sp->first->code = sp->last->code;
2127         sp->first       = sp->last;
2129         sp_nodepath_update_handles(sp->nodepath);
2131         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2133         return;
2134     }
2136     /* a and b are separate subpaths */
2137     Inkscape::NodePath::SubPath *sa = a->subpath;
2138     Inkscape::NodePath::SubPath *sb = b->subpath;
2140     Inkscape::NodePath::Node *n;
2141     NR::Point p;
2142     NRPathcode code;
2143     if (a == sa->first) {
2144         code = (NRPathcode) sa->first->n.other->code;
2145        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2146         n = sa->last;
2147         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2148         for (n = n->p.other; n != NULL; n = n->p.other) {
2149             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2150         }
2151         sp_nodepath_subpath_destroy(sa);
2152         sa = t;
2153     } else if (a == sa->last) {
2154         code = (NRPathcode)sa->last->code;
2155     } else {
2156         code = NR_END;
2157         g_assert_not_reached();
2158     }
2160     if (b == sb->first) {
2161         n = sb->first;
2162         sp_node_handle_mirror_p_to_n(sa->last);
2163         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2164         sp_node_handle_mirror_n_to_p(sa->last);
2165         for (n = n->n.other; n != NULL; n = n->n.other) {
2166             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2167         }
2168     } else if (b == sb->last) {
2169         n = sb->last;
2170         sp_node_handle_mirror_p_to_n(sa->last);
2171         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2172         sp_node_handle_mirror_n_to_p(sa->last);
2173         for (n = n->p.other; n != NULL; n = n->p.other) {
2174             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2175         }
2176     } else {
2177         g_assert_not_reached();
2178     }
2179     /* and now destroy sb */
2181     sp_nodepath_subpath_destroy(sb);
2183     sp_nodepath_update_handles(sa->nodepath);
2185     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2188 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2190 /**
2191  * Internal function to handle joining two nodes.
2192  */
2193 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2195     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2197     if (g_list_length(nodepath->selected) != 2) {
2198         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2199         return;
2200     }
2202     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2203     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2205     g_assert(a != b);
2206     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2207         // someone tried to join an orphan node (i.e. a single-node subpath).
2208         // this is not worth an error message, just fail silently.
2209         return;
2210     }
2212     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2213         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2214         return;
2215     }
2217     switch(mode) {
2218         case NODE_JOIN_ENDPOINTS:
2219             do_node_selected_join(nodepath, a, b);
2220             break;
2221         case NODE_JOIN_SEGMENT:
2222             do_node_selected_join_segment(nodepath, a, b);
2223             break;
2224     }
2227 /**
2228  *  Join two nodes by merging them into one.
2229  */
2230 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2232     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2235 /**
2236  *  Join two nodes by adding a segment between them.
2237  */
2238 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2240     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2243 /**
2244  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2245  */
2246 void sp_node_delete_preserve(GList *nodes_to_delete)
2248     GSList *nodepaths = NULL;
2250     while (nodes_to_delete) {
2251         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2252         Inkscape::NodePath::SubPath *sp = node->subpath;
2253         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2254         Inkscape::NodePath::Node *sample_cursor = NULL;
2255         Inkscape::NodePath::Node *sample_end = NULL;
2256         Inkscape::NodePath::Node *delete_cursor = node;
2257         bool just_delete = false;
2259         //find the start of this contiguous selection
2260         //move left to the first node that is not selected
2261         //or the start of the non-closed path
2262         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2263             delete_cursor = curr;
2264         }
2266         //just delete at the beginning of an open path
2267         if (!delete_cursor->p.other) {
2268             sample_cursor = delete_cursor;
2269             just_delete = true;
2270         } else {
2271             sample_cursor = delete_cursor->p.other;
2272         }
2274         //calculate points for each segment
2275         int rate = 5;
2276         float period = 1.0 / rate;
2277         std::vector<NR::Point> data;
2278         if (!just_delete) {
2279             data.push_back(sample_cursor->pos);
2280             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2281                 //just delete at the end of an open path
2282                 if (!sp->closed && curr == sp->last) {
2283                     just_delete = true;
2284                     break;
2285                 }
2287                 //sample points on the contiguous selected segment
2288                 NR::Point *bez;
2289                 bez = new NR::Point [4];
2290                 bez[0] = curr->pos;
2291                 bez[1] = curr->n.pos;
2292                 bez[2] = curr->n.other->p.pos;
2293                 bez[3] = curr->n.other->pos;
2294                 for (int i=1; i<rate; i++) {
2295                     gdouble t = i * period;
2296                     NR::Point p = bezier_pt(3, bez, t);
2297                     data.push_back(p);
2298                 }
2299                 data.push_back(curr->n.other->pos);
2301                 sample_end = curr->n.other;
2302                 //break if we've come full circle or hit the end of the selection
2303                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2304                     break;
2305                 }
2306             }
2307         }
2309         if (!just_delete) {
2310             //calculate the best fitting single segment and adjust the endpoints
2311             NR::Point *adata;
2312             adata = new NR::Point [data.size()];
2313             copy(data.begin(), data.end(), adata);
2315             NR::Point *bez;
2316             bez = new NR::Point [4];
2317             //would decreasing error create a better fitting approximation?
2318             gdouble error = 1.0;
2319             gint ret;
2320             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2322             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2323             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2324             //the resulting nodes behave as expected.
2325             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2326                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2327             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2328                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2330             //adjust endpoints
2331             sample_cursor->n.pos = bez[1];
2332             sample_end->p.pos = bez[2];
2333         }
2335         //destroy this contiguous selection
2336         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2337             Inkscape::NodePath::Node *temp = delete_cursor;
2338             if (delete_cursor->n.other == delete_cursor) {
2339                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2340                 delete_cursor = NULL;
2341             } else {
2342                 delete_cursor = delete_cursor->n.other;
2343             }
2344             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2345             sp_nodepath_node_destroy(temp);
2346         }
2348         sp_nodepath_update_handles(nodepath);
2350         if (!g_slist_find(nodepaths, nodepath))
2351             nodepaths = g_slist_prepend (nodepaths, nodepath);
2352     }
2354     for (GSList *i = nodepaths; i; i = i->next) {
2355         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2356         // different nodepaths will give us one undo event per nodepath
2357         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2359         // if the entire nodepath is removed, delete the selected object.
2360         if (nodepath->subpaths == NULL ||
2361             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2362             //at least 2
2363             sp_nodepath_get_node_count(nodepath) < 2) {
2364             SPDocument *document = sp_desktop_document (nodepath->desktop);
2365             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2366             //delete this nodepath's object, not the entire selection! (though at this time, this
2367             //does not matter)
2368             sp_selection_delete();
2369             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2370                               _("Delete nodes"));
2371         } else {
2372             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2373             sp_nodepath_update_statusbar(nodepath);
2374         }
2375     }
2377     g_slist_free (nodepaths);
2380 /**
2381  * Delete one or more selected nodes.
2382  */
2383 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2385     if (!nodepath) return;
2386     if (!nodepath->selected) return;
2388     /** \todo fixme: do it the right way */
2389     while (nodepath->selected) {
2390        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2391         sp_nodepath_node_destroy(node);
2392     }
2395     //clean up the nodepath (such as for trivial subpaths)
2396     sp_nodepath_cleanup(nodepath);
2398     sp_nodepath_update_handles(nodepath);
2400     // if the entire nodepath is removed, delete the selected object.
2401     if (nodepath->subpaths == NULL ||
2402         sp_nodepath_get_node_count(nodepath) < 2) {
2403         SPDocument *document = sp_desktop_document (nodepath->desktop);
2404         sp_selection_delete();
2405         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2406                           _("Delete nodes"));
2407         return;
2408     }
2410     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2412     sp_nodepath_update_statusbar(nodepath);
2415 /**
2416  * Delete one or more segments between two selected nodes.
2417  * This is the code for 'split'.
2418  */
2419 void
2420 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2422    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2423    Inkscape::NodePath::Node *curr, *next;     //Iterators
2425     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2427     if (g_list_length(nodepath->selected) != 2) {
2428         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2429                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2430         return;
2431     }
2433     //Selected nodes, not inclusive
2434    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2435    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2437     if ( ( a==b)                       ||  //same node
2438          (a->subpath  != b->subpath )  ||  //not the same path
2439          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2440          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2441     {
2442         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2443                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2444         return;
2445     }
2447     //###########################################
2448     //# BEGIN EDITS
2449     //###########################################
2450     //##################################
2451     //# CLOSED PATH
2452     //##################################
2453     if (a->subpath->closed) {
2456         gboolean reversed = FALSE;
2458         //Since we can go in a circle, we need to find the shorter distance.
2459         //  a->b or b->a
2460         start = end = NULL;
2461         int distance    = 0;
2462         int minDistance = 0;
2463         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2464             if (curr==b) {
2465                 //printf("a to b:%d\n", distance);
2466                 start = a;//go from a to b
2467                 end   = b;
2468                 minDistance = distance;
2469                 //printf("A to B :\n");
2470                 break;
2471             }
2472             distance++;
2473         }
2475         //try again, the other direction
2476         distance = 0;
2477         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2478             if (curr==a) {
2479                 //printf("b to a:%d\n", distance);
2480                 if (distance < minDistance) {
2481                     start    = b;  //we go from b to a
2482                     end      = a;
2483                     reversed = TRUE;
2484                     //printf("B to A\n");
2485                 }
2486                 break;
2487             }
2488             distance++;
2489         }
2492         //Copy everything from 'end' to 'start' to a new subpath
2493        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2494         for (curr=end ; curr ; curr=curr->n.other) {
2495             NRPathcode code = (NRPathcode) curr->code;
2496             if (curr == end)
2497                 code = NR_MOVETO;
2498             sp_nodepath_node_new(t, NULL,
2499                                  (Inkscape::NodePath::NodeType)curr->type, code,
2500                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2501             if (curr == start)
2502                 break;
2503         }
2504         sp_nodepath_subpath_destroy(a->subpath);
2507     }
2511     //##################################
2512     //# OPEN PATH
2513     //##################################
2514     else {
2516         //We need to get the direction of the list between A and B
2517         //Can we walk from a to b?
2518         start = end = NULL;
2519         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2520             if (curr==b) {
2521                 start = a;  //did it!  we go from a to b
2522                 end   = b;
2523                 //printf("A to B\n");
2524                 break;
2525             }
2526         }
2527         if (!start) {//didn't work?  let's try the other direction
2528             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2529                 if (curr==a) {
2530                     start = b;  //did it!  we go from b to a
2531                     end   = a;
2532                     //printf("B to A\n");
2533                     break;
2534                 }
2535             }
2536         }
2537         if (!start) {
2538             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2539                                                      _("Cannot find path between nodes."));
2540             return;
2541         }
2545         //Copy everything after 'end' to a new subpath
2546        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2547         for (curr=end ; curr ; curr=curr->n.other) {
2548             NRPathcode code = (NRPathcode) curr->code;
2549             if (curr == end)
2550                 code = NR_MOVETO;
2551             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2552                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2553         }
2555         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2556         for (curr = start->n.other ; curr  ; curr=next) {
2557             next = curr->n.other;
2558             sp_nodepath_node_destroy(curr);
2559         }
2561     }
2562     //###########################################
2563     //# END EDITS
2564     //###########################################
2566     //clean up the nodepath (such as for trivial subpaths)
2567     sp_nodepath_cleanup(nodepath);
2569     sp_nodepath_update_handles(nodepath);
2571     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2573     sp_nodepath_update_statusbar(nodepath);
2576 /**
2577  * Call sp_nodepath_set_line() for all selected segments.
2578  */
2579 void
2580 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2582     if (nodepath == NULL) return;
2584     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2585        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2586         g_assert(n->selected);
2587         if (n->p.other && n->p.other->selected) {
2588             sp_nodepath_set_line_type(n, code);
2589         }
2590     }
2592     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2595 /**
2596  * Call sp_nodepath_convert_node_type() for all selected nodes.
2597  */
2598 void
2599 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2601     if (nodepath == NULL) return;
2603     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2605     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2606         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2607     }
2609     sp_nodepath_update_repr(nodepath, _("Change node type"));
2612 /**
2613  * Change select status of node, update its own and neighbour handles.
2614  */
2615 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2617     node->selected = selected;
2619     if (selected) {
2620         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2621         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2622         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2623         sp_knot_update_ctrl(node->knot);
2624     } else {
2625         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2626         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2627         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2628         sp_knot_update_ctrl(node->knot);
2629     }
2631     sp_node_update_handles(node);
2632     if (node->n.other) sp_node_update_handles(node->n.other);
2633     if (node->p.other) sp_node_update_handles(node->p.other);
2636 /**
2637 \brief Select a node
2638 \param node     The node to select
2639 \param incremental   If true, add to selection, otherwise deselect others
2640 \param override   If true, always select this node, otherwise toggle selected status
2641 */
2642 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2644     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2646     if (incremental) {
2647         if (override) {
2648             if (!g_list_find(nodepath->selected, node)) {
2649                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2650             }
2651             sp_node_set_selected(node, TRUE);
2652         } else { // toggle
2653             if (node->selected) {
2654                 g_assert(g_list_find(nodepath->selected, node));
2655                 nodepath->selected = g_list_remove(nodepath->selected, node);
2656             } else {
2657                 g_assert(!g_list_find(nodepath->selected, node));
2658                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2659             }
2660             sp_node_set_selected(node, !node->selected);
2661         }
2662     } else {
2663         sp_nodepath_deselect(nodepath);
2664         nodepath->selected = g_list_prepend(nodepath->selected, node);
2665         sp_node_set_selected(node, TRUE);
2666     }
2668     sp_nodepath_update_statusbar(nodepath);
2672 /**
2673 \brief Deselect all nodes in the nodepath
2674 */
2675 void
2676 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2678     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2680     while (nodepath->selected) {
2681         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2682         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2683     }
2684     sp_nodepath_update_statusbar(nodepath);
2687 /**
2688 \brief Select or invert selection of all nodes in the nodepath
2689 */
2690 void
2691 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2693     if (!nodepath) return;
2695     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2696        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2697         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2698            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2699            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2700         }
2701     }
2704 /**
2705  * If nothing selected, does the same as sp_nodepath_select_all();
2706  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2707  * (i.e., similar to "select all in layer", with the "selected" subpaths
2708  * being treated as "layers" in the path).
2709  */
2710 void
2711 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2713     if (!nodepath) return;
2715     if (g_list_length (nodepath->selected) == 0) {
2716         sp_nodepath_select_all (nodepath, invert);
2717         return;
2718     }
2720     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2721     GSList *subpaths = NULL;
2723     for (GList *l = copy; l != NULL; l = l->next) {
2724         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2725         Inkscape::NodePath::SubPath *subpath = n->subpath;
2726         if (!g_slist_find (subpaths, subpath))
2727             subpaths = g_slist_prepend (subpaths, subpath);
2728     }
2730     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2731         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2732         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2733             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2734             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2735         }
2736     }
2738     g_slist_free (subpaths);
2739     g_list_free (copy);
2742 /**
2743  * \brief Select the node after the last selected; if none is selected,
2744  * select the first within path.
2745  */
2746 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2748     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2750    Inkscape::NodePath::Node *last = NULL;
2751     if (nodepath->selected) {
2752         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2753            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2754             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2755             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2756                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2757                 if (node->selected) {
2758                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2759                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2760                             if (spl->next) { // there's a next subpath
2761                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2762                                 last = subpath_next->first;
2763                             } else if (spl->prev) { // there's a previous subpath
2764                                 last = NULL; // to be set later to the first node of first subpath
2765                             } else {
2766                                 last = node->n.other;
2767                             }
2768                         } else {
2769                             last = node->n.other;
2770                         }
2771                     } else {
2772                         if (node->n.other) {
2773                             last = node->n.other;
2774                         } else {
2775                             if (spl->next) { // there's a next subpath
2776                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2777                                 last = subpath_next->first;
2778                             } else if (spl->prev) { // there's a previous subpath
2779                                 last = NULL; // to be set later to the first node of first subpath
2780                             } else {
2781                                 last = (Inkscape::NodePath::Node *) subpath->first;
2782                             }
2783                         }
2784                     }
2785                 }
2786             }
2787         }
2788         sp_nodepath_deselect(nodepath);
2789     }
2791     if (last) { // there's at least one more node after selected
2792         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2793     } else { // no more nodes, select the first one in first subpath
2794        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2795         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2796     }
2799 /**
2800  * \brief Select the node before the first selected; if none is selected,
2801  * select the last within path
2802  */
2803 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2805     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2807    Inkscape::NodePath::Node *last = NULL;
2808     if (nodepath->selected) {
2809         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2810            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2811             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2812                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2813                 if (node->selected) {
2814                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2815                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2816                             if (spl->prev) { // there's a prev subpath
2817                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2818                                 last = subpath_prev->last;
2819                             } else if (spl->next) { // there's a next subpath
2820                                 last = NULL; // to be set later to the last node of last subpath
2821                             } else {
2822                                 last = node->p.other;
2823                             }
2824                         } else {
2825                             last = node->p.other;
2826                         }
2827                     } else {
2828                         if (node->p.other) {
2829                             last = node->p.other;
2830                         } else {
2831                             if (spl->prev) { // there's a prev subpath
2832                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2833                                 last = subpath_prev->last;
2834                             } else if (spl->next) { // there's a next subpath
2835                                 last = NULL; // to be set later to the last node of last subpath
2836                             } else {
2837                                 last = (Inkscape::NodePath::Node *) subpath->last;
2838                             }
2839                         }
2840                     }
2841                 }
2842             }
2843         }
2844         sp_nodepath_deselect(nodepath);
2845     }
2847     if (last) { // there's at least one more node before selected
2848         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2849     } else { // no more nodes, select the last one in last subpath
2850         GList *spl = g_list_last(nodepath->subpaths);
2851        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2852         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2853     }
2856 /**
2857  * \brief Select all nodes that are within the rectangle.
2858  */
2859 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2861     if (!incremental) {
2862         sp_nodepath_deselect(nodepath);
2863     }
2865     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2866        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2867         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2868            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2870             if (b.contains(node->pos)) {
2871                 sp_nodepath_node_select(node, TRUE, TRUE);
2872             }
2873         }
2874     }
2878 void
2879 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2881     g_assert (n);
2882     g_assert (nodepath);
2883     g_assert (n->subpath->nodepath == nodepath);
2885     if (g_list_length (nodepath->selected) == 0) {
2886         if (grow > 0) {
2887             sp_nodepath_node_select(n, TRUE, TRUE);
2888         }
2889         return;
2890     }
2892     if (g_list_length (nodepath->selected) == 1) {
2893         if (grow < 0) {
2894             sp_nodepath_deselect (nodepath);
2895             return;
2896         }
2897     }
2899         double n_sel_range = 0, p_sel_range = 0;
2900             Inkscape::NodePath::Node *farthest_n_node = n;
2901             Inkscape::NodePath::Node *farthest_p_node = n;
2903         // Calculate ranges
2904         {
2905             double n_range = 0, p_range = 0;
2906             bool n_going = true, p_going = true;
2907             Inkscape::NodePath::Node *n_node = n;
2908             Inkscape::NodePath::Node *p_node = n;
2909             do {
2910                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2911                 if (n_node && n_going)
2912                     n_node = n_node->n.other;
2913                 if (n_node == NULL) {
2914                     n_going = false;
2915                 } else {
2916                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2917                     if (n_node->selected) {
2918                         n_sel_range = n_range;
2919                         farthest_n_node = n_node;
2920                     }
2921                     if (n_node == p_node) {
2922                         n_going = false;
2923                         p_going = false;
2924                     }
2925                 }
2926                 if (p_node && p_going)
2927                     p_node = p_node->p.other;
2928                 if (p_node == NULL) {
2929                     p_going = false;
2930                 } else {
2931                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2932                     if (p_node->selected) {
2933                         p_sel_range = p_range;
2934                         farthest_p_node = p_node;
2935                     }
2936                     if (p_node == n_node) {
2937                         n_going = false;
2938                         p_going = false;
2939                     }
2940                 }
2941             } while (n_going || p_going);
2942         }
2944     if (grow > 0) {
2945         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2946                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2947         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2948                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2949         }
2950     } else {
2951         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2952                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2953         } else if (farthest_p_node && farthest_p_node->selected) {
2954                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2955         }
2956     }
2959 void
2960 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2962     g_assert (n);
2963     g_assert (nodepath);
2964     g_assert (n->subpath->nodepath == nodepath);
2966     if (g_list_length (nodepath->selected) == 0) {
2967         if (grow > 0) {
2968             sp_nodepath_node_select(n, TRUE, TRUE);
2969         }
2970         return;
2971     }
2973     if (g_list_length (nodepath->selected) == 1) {
2974         if (grow < 0) {
2975             sp_nodepath_deselect (nodepath);
2976             return;
2977         }
2978     }
2980     Inkscape::NodePath::Node *farthest_selected = NULL;
2981     double farthest_dist = 0;
2983     Inkscape::NodePath::Node *closest_unselected = NULL;
2984     double closest_dist = NR_HUGE;
2986     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2987        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2988         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2989            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2990            if (node == n)
2991                continue;
2992            if (node->selected) {
2993                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2994                    farthest_dist = NR::L2(node->pos - n->pos);
2995                    farthest_selected = node;
2996                }
2997            } else {
2998                if (NR::L2(node->pos - n->pos) < closest_dist) {
2999                    closest_dist = NR::L2(node->pos - n->pos);
3000                    closest_unselected = node;
3001                }
3002            }
3003         }
3004     }
3006     if (grow > 0) {
3007         if (closest_unselected) {
3008             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3009         }
3010     } else {
3011         if (farthest_selected) {
3012             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3013         }
3014     }
3018 /**
3019 \brief  Saves all nodes' and handles' current positions in their origin members
3020 */
3021 void
3022 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3024     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3025        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3026         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3027            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3028            n->origin = n->pos;
3029            n->p.origin = n->p.pos;
3030            n->n.origin = n->n.pos;
3031         }
3032     }
3035 /**
3036 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3037 */
3038 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3040     if (!nodepath->selected) {
3041         return NULL;
3042     }
3044     GList *r = NULL;
3045     guint i = 0;
3046     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3047        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3048         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3049            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3050             i++;
3051             if (node->selected) {
3052                 r = g_list_append(r, GINT_TO_POINTER(i));
3053             }
3054         }
3055     }
3056     return r;
3059 /**
3060 \brief  Restores selection by selecting nodes whose positions are in the list
3061 */
3062 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3064     sp_nodepath_deselect(nodepath);
3066     guint i = 0;
3067     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3068        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3069         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3070            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3071             i++;
3072             if (g_list_find(r, GINT_TO_POINTER(i))) {
3073                 sp_nodepath_node_select(node, TRUE, TRUE);
3074             }
3075         }
3076     }
3080 /**
3081 \brief Adjusts handle according to node type and line code.
3082 */
3083 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3085     g_assert(node);
3087    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3088    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3090    // nothing to do if we are an end node
3091     if (me->other == NULL) return;
3092     if (other->other == NULL) return;
3094     // nothing to do if we are a cusp node
3095     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3097     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3098     NRPathcode mecode;
3099     if (which_adjust == 1) {
3100         mecode = (NRPathcode)me->other->code;
3101     } else {
3102         mecode = (NRPathcode)node->code;
3103     }
3104     if (mecode == NR_LINETO) return;
3106     if (sp_node_side_is_line(node, other)) {
3107         // other is a line, and we are either smooth or symm
3108        Inkscape::NodePath::Node *othernode = other->other;
3109         double len = NR::L2(me->pos - node->pos);
3110         NR::Point delta = node->pos - othernode->pos;
3111         double linelen = NR::L2(delta);
3112         if (linelen < 1e-18)
3113             return;
3114         me->pos = node->pos + (len / linelen)*delta;
3115         return;
3116     }
3118     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3119         // symmetrize 
3120         me->pos = 2 * node->pos - other->pos;
3121         return;
3122     } else {
3123         // smoothify
3124         double len = NR::L2(me->pos - node->pos);
3125         NR::Point delta = other->pos - node->pos;
3126         double otherlen = NR::L2(delta);
3127         if (otherlen < 1e-18) return;
3128         me->pos = node->pos - (len / otherlen) * delta;
3129     }
3132 /**
3133  \brief Adjusts both handles according to node type and line code
3134  */
3135 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3137     g_assert(node);
3139     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3141     /* we are either smooth or symm */
3143     if (node->p.other == NULL) return;
3144     if (node->n.other == NULL) return;
3146     if (sp_node_side_is_line(node, &node->p)) {
3147         sp_node_adjust_handle(node, 1);
3148         return;
3149     }
3151     if (sp_node_side_is_line(node, &node->n)) {
3152         sp_node_adjust_handle(node, -1);
3153         return;
3154     }
3156     /* both are curves */
3157     NR::Point const delta( node->n.pos - node->p.pos );
3159     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3160         node->p.pos = node->pos - delta / 2;
3161         node->n.pos = node->pos + delta / 2;
3162         return;
3163     }
3165     /* We are smooth */
3166     double plen = NR::L2(node->p.pos - node->pos);
3167     if (plen < 1e-18) return;
3168     double nlen = NR::L2(node->n.pos - node->pos);
3169     if (nlen < 1e-18) return;
3170     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3171     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3174 /**
3175  * Node event callback.
3176  */
3177 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3179     gboolean ret = FALSE;
3180     switch (event->type) {
3181         case GDK_ENTER_NOTIFY:
3182             Inkscape::NodePath::Path::active_node = n;
3183             break;
3184         case GDK_LEAVE_NOTIFY:
3185             Inkscape::NodePath::Path::active_node = NULL;
3186             break;
3187         case GDK_SCROLL:
3188             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3189                 switch (event->scroll.direction) {
3190                     case GDK_SCROLL_UP:
3191                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3192                         break;
3193                     case GDK_SCROLL_DOWN:
3194                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3195                         break;
3196                     default:
3197                         break;
3198                 }
3199                 ret = TRUE;
3200             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3201                 switch (event->scroll.direction) {
3202                     case GDK_SCROLL_UP:
3203                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3204                         break;
3205                     case GDK_SCROLL_DOWN:
3206                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3207                         break;
3208                     default:
3209                         break;
3210                 }
3211                 ret = TRUE;
3212             }
3213             break;
3214         case GDK_KEY_PRESS:
3215             switch (get_group0_keyval (&event->key)) {
3216                 case GDK_space:
3217                     if (event->key.state & GDK_BUTTON1_MASK) {
3218                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3219                         stamp_repr(nodepath);
3220                         ret = TRUE;
3221                     }
3222                     break;
3223                 case GDK_Page_Up:
3224                     if (event->key.state & GDK_CONTROL_MASK) {
3225                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3226                     } else {
3227                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3228                     }
3229                     break;
3230                 case GDK_Page_Down:
3231                     if (event->key.state & GDK_CONTROL_MASK) {
3232                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3233                     } else {
3234                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3235                     }
3236                     break;
3237                 default:
3238                     break;
3239             }
3240             break;
3241         default:
3242             break;
3243     }
3245     return ret;
3248 /**
3249  * Handle keypress on node; directly called.
3250  */
3251 gboolean node_key(GdkEvent *event)
3253     Inkscape::NodePath::Path *np;
3255     // there is no way to verify nodes so set active_node to nil when deleting!!
3256     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3258     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3259         gint ret = FALSE;
3260         switch (get_group0_keyval (&event->key)) {
3261             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3262             case GDK_BackSpace:
3263                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3264                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3265                 sp_nodepath_update_repr(np, _("Delete node"));
3266                 Inkscape::NodePath::Path::active_node = NULL;
3267                 ret = TRUE;
3268                 break;
3269             case GDK_c:
3270                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3271                 ret = TRUE;
3272                 break;
3273             case GDK_s:
3274                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3275                 ret = TRUE;
3276                 break;
3277             case GDK_y:
3278                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3279                 ret = TRUE;
3280                 break;
3281             case GDK_b:
3282                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3283                 ret = TRUE;
3284                 break;
3285         }
3286         return ret;
3287     }
3288     return FALSE;
3291 /**
3292  * Mouseclick on node callback.
3293  */
3294 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3296    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3298     if (state & GDK_CONTROL_MASK) {
3299         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3301         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3302             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3303                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3304             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3305                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3306             } else {
3307                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3308             }
3309             sp_nodepath_update_repr(nodepath, _("Change node type"));
3310             sp_nodepath_update_statusbar(nodepath);
3312         } else { //ctrl+alt+click: delete node
3313             GList *node_to_delete = NULL;
3314             node_to_delete = g_list_append(node_to_delete, n);
3315             sp_node_delete_preserve(node_to_delete);
3316         }
3318     } else {
3319         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3320     }
3323 /**
3324  * Mouse grabbed node callback.
3325  */
3326 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3328    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3330     if (!n->selected) {
3331         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3332     }
3334     n->is_dragging = true;
3335     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3337     sp_nodepath_remember_origins (n->subpath->nodepath);
3340 /**
3341  * Mouse ungrabbed node callback.
3342  */
3343 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3345    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3347    n->dragging_out = NULL;
3348    n->is_dragging = false;
3349    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3351    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3354 /**
3355  * The point on a line, given by its angle, closest to the given point.
3356  * \param p  A point.
3357  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3358  * \param closest  Pointer to the point struct where the result is stored.
3359  * \todo FIXME: use dot product perhaps?
3360  */
3361 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3363     if (a == HUGE_VAL) { // vertical
3364         *closest = NR::Point(0, (*p)[NR::Y]);
3365     } else {
3366         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3367         (*closest)[NR::Y] = a * (*closest)[NR::X];
3368     }
3371 /**
3372  * Distance from the point to a line given by its angle.
3373  * \param p  A point.
3374  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3375  */
3376 static double point_line_distance(NR::Point *p, double a)
3378     NR::Point c;
3379     point_line_closest(p, a, &c);
3380     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]));
3383 /**
3384  * Callback for node "request" signal.
3385  * \todo fixme: This goes to "moved" event? (lauris)
3386  */
3387 static gboolean
3388 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3390     double yn, xn, yp, xp;
3391     double an, ap, na, pa;
3392     double d_an, d_ap, d_na, d_pa;
3393     gboolean collinear = FALSE;
3394     NR::Point c;
3395     NR::Point pr;
3397    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3399     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3401    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3402     if ( (!n->subpath->nodepath->straight_path) &&
3403          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3404            || n->dragging_out ) )
3405     {
3406        NR::Point mouse = (*p);
3408        if (!n->dragging_out) {
3409            // This is the first drag-out event; find out which handle to drag out
3410            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3411            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3413            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3414                return FALSE;
3416            Inkscape::NodePath::NodeSide *opposite;
3417            if (appr_p > appr_n) { // closer to p
3418                n->dragging_out = &n->p;
3419                opposite = &n->n;
3420                n->code = NR_CURVETO;
3421            } else if (appr_p < appr_n) { // closer to n
3422                n->dragging_out = &n->n;
3423                opposite = &n->p;
3424                n->n.other->code = NR_CURVETO;
3425            } else { // p and n nodes are the same
3426                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3427                    n->dragging_out = &n->p;
3428                    opposite = &n->n;
3429                    n->code = NR_CURVETO;
3430                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3431                    n->dragging_out = &n->n;
3432                    opposite = &n->p;
3433                    n->n.other->code = NR_CURVETO;
3434                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3435                    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);
3436                    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);
3437                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3438                        n->dragging_out = &n->n;
3439                        opposite = &n->p;
3440                        n->n.other->code = NR_CURVETO;
3441                    } else { // closer to other's n handle
3442                        n->dragging_out = &n->p;
3443                        opposite = &n->n;
3444                        n->code = NR_CURVETO;
3445                    }
3446                }
3447            }
3449            // if there's another handle, make sure the one we drag out starts parallel to it
3450            if (opposite->pos != n->pos) {
3451                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3452            }
3454            // knots might not be created yet!
3455            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3456            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3457        }
3459        // pass this on to the handle-moved callback
3460        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3461        sp_node_update_handles(n);
3462        return TRUE;
3463    }
3465     if (state & GDK_CONTROL_MASK) { // constrained motion
3467         // calculate relative distances of handles
3468         // n handle:
3469         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3470         xn = n->n.pos[NR::X] - n->pos[NR::X];
3471         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3472         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3473             if (n->n.other) { // if there is the next point
3474                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3475                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3476                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3477             }
3478         }
3479         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3480         if (yn < 0) { xn = -xn; yn = -yn; }
3482         // p handle:
3483         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3484         xp = n->p.pos[NR::X] - n->pos[NR::X];
3485         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3486         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3487             if (n->p.other) {
3488                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3489                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3490                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3491             }
3492         }
3493         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3494         if (yp < 0) { xp = -xp; yp = -yp; }
3496         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3497             // sliding on handles, only if at least one of the handles is non-vertical
3498             // (otherwise it's the same as ctrl+drag anyway)
3500             // calculate angles of the handles
3501             if (xn == 0) {
3502                 if (yn == 0) { // no handle, consider it the continuation of the other one
3503                     an = 0;
3504                     collinear = TRUE;
3505                 }
3506                 else an = 0; // vertical; set the angle to horizontal
3507             } else an = yn/xn;
3509             if (xp == 0) {
3510                 if (yp == 0) { // no handle, consider it the continuation of the other one
3511                     ap = an;
3512                 }
3513                 else ap = 0; // vertical; set the angle to horizontal
3514             } else  ap = yp/xp;
3516             if (collinear) an = ap;
3518             // angles of the perpendiculars; HUGE_VAL means vertical
3519             if (an == 0) na = HUGE_VAL; else na = -1/an;
3520             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3522             // mouse point relative to the node's original pos
3523             pr = (*p) - n->origin;
3525             // distances to the four lines (two handles and two perpendiculars)
3526             d_an = point_line_distance(&pr, an);
3527             d_na = point_line_distance(&pr, na);
3528             d_ap = point_line_distance(&pr, ap);
3529             d_pa = point_line_distance(&pr, pa);
3531             // find out which line is the closest, save its closest point in c
3532             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3533                 point_line_closest(&pr, an, &c);
3534             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3535                 point_line_closest(&pr, ap, &c);
3536             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3537                 point_line_closest(&pr, na, &c);
3538             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3539                 point_line_closest(&pr, pa, &c);
3540             }
3542             // move the node to the closest point
3543             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3544                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3545                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3547         } else {  // constraining to hor/vert
3549             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3550                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3551             } else { // snap to vert
3552                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3553             }
3554         }
3555     } else { // move freely
3556         if (n->is_dragging) {
3557             if (state & GDK_MOD1_MASK) { // sculpt
3558                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3559             } else {
3560                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3561                                             (*p)[NR::X] - n->pos[NR::X],
3562                                             (*p)[NR::Y] - n->pos[NR::Y],
3563                                             (state & GDK_SHIFT_MASK) == 0);
3564             }
3565         }
3566     }
3568     n->subpath->nodepath->desktop->scroll_to_point(p);
3570     return TRUE;
3573 /**
3574  * Node handle clicked callback.
3575  */
3576 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3578    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3580     if (state & GDK_CONTROL_MASK) { // "delete" handle
3581         if (n->p.knot == knot) {
3582             n->p.pos = n->pos;
3583         } else if (n->n.knot == knot) {
3584             n->n.pos = n->pos;
3585         }
3586         sp_node_update_handles(n);
3587         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3588         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3589         sp_nodepath_update_statusbar(nodepath);
3591     } else { // just select or add to selection, depending in Shift
3592         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3593     }
3596 /**
3597  * Node handle grabbed callback.
3598  */
3599 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3601    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3603     if (!n->selected) {
3604         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3605     }
3607     // remember the origin point of the handle
3608     if (n->p.knot == knot) {
3609         n->p.origin_radial = n->p.pos - n->pos;
3610     } else if (n->n.knot == knot) {
3611         n->n.origin_radial = n->n.pos - n->pos;
3612     } else {
3613         g_assert_not_reached();
3614     }
3616     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3619 /**
3620  * Node handle ungrabbed callback.
3621  */
3622 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3624    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3626     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3627     if (n->p.knot == knot) {
3628         n->p.origin_radial.a = 0;
3629         sp_knot_set_position(knot, &n->p.pos, state);
3630     } else if (n->n.knot == knot) {
3631         n->n.origin_radial.a = 0;
3632         sp_knot_set_position(knot, &n->n.pos, state);
3633     } else {
3634         g_assert_not_reached();
3635     }
3637     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3640 /**
3641  * Node handle "request" signal callback.
3642  */
3643 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3645     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3647     Inkscape::NodePath::NodeSide *me, *opposite;
3648     gint which;
3649     if (n->p.knot == knot) {
3650         me = &n->p;
3651         opposite = &n->n;
3652         which = -1;
3653     } else if (n->n.knot == knot) {
3654         me = &n->n;
3655         opposite = &n->p;
3656         which = 1;
3657     } else {
3658         me = opposite = NULL;
3659         which = 0;
3660         g_assert_not_reached();
3661     }
3663     SPDesktop *desktop = n->subpath->nodepath->desktop;
3664     SnapManager &m = desktop->namedview->snap_manager;
3665     m.setup(desktop, n->subpath->nodepath->item);
3666     Inkscape::SnappedPoint s ;
3668     Inkscape::NodePath::Node *othernode = opposite->other;
3669     if (othernode) {
3670         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3671             /* We are smooth node adjacent with line */
3672             NR::Point const delta = *p - n->pos;
3673             NR::Coord const len = NR::L2(delta);
3674             Inkscape::NodePath::Node *othernode = opposite->other;
3675             NR::Point const ndelta = n->pos - othernode->pos;
3676             NR::Coord const linelen = NR::L2(ndelta);
3677             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3678                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3679                 (*p) = n->pos + (scal / linelen) * ndelta;
3680             }
3681             s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3682         } else {
3683             s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3684         }
3685     }
3686     
3687     *p = s.getPoint();
3689     sp_node_adjust_handle(n, -which);
3691     return FALSE;
3694 /**
3695  * Node handle moved callback.
3696  */
3697 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3699    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3701    Inkscape::NodePath::NodeSide *me;
3702    Inkscape::NodePath::NodeSide *other;
3703     if (n->p.knot == knot) {
3704         me = &n->p;
3705         other = &n->n;
3706     } else if (n->n.knot == knot) {
3707         me = &n->n;
3708         other = &n->p;
3709     } else {
3710         me = NULL;
3711         other = NULL;
3712         g_assert_not_reached();
3713     }
3715     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3716     Radial rme(me->pos - n->pos);
3717     Radial rother(other->pos - n->pos);
3718     Radial rnew(*p - n->pos);
3720     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3721         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3722         /* 0 interpreted as "no snapping". */
3724         // 1. Snap to the closest PI/snaps angle, starting from zero.
3725         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3727         // 2. Snap to the original angle, its opposite and perpendiculars
3728         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3729             /* The closest PI/2 angle, starting from original angle */
3730             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3732             // Snap to the closest.
3733             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3734                        ? a_snapped
3735                        : a_ortho );
3736         }
3738         // 3. Snap to the angle of the opposite line, if any
3739         Inkscape::NodePath::Node *othernode = other->other;
3740         if (othernode) {
3741             NR::Point other_to_snap(0,0);
3742             if (sp_node_side_is_line(n, other)) {
3743                 other_to_snap = othernode->pos - n->pos;
3744             } else {
3745                 other_to_snap = other->pos - n->pos;
3746             }
3747             if (NR::L2(other_to_snap) > 1e-3) {
3748                 Radial rother_to_snap(other_to_snap);
3749                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3750                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3752                 // Snap to the closest.
3753                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3754                        ? a_snapped
3755                        : a_oppo );
3756             }
3757         }
3759         rnew.a = a_snapped;
3760     }
3762     if (state & GDK_MOD1_MASK) {
3763         // lock handle length
3764         rnew.r = me->origin_radial.r;
3765     }
3767     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3768         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3769         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3770         rother.a += rnew.a - rme.a;
3771         other->pos = NR::Point(rother) + n->pos;
3772         if (other->knot) {
3773             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3774             sp_knot_moveto(other->knot, &other->pos);
3775         }
3776     }
3778     me->pos = NR::Point(rnew) + n->pos;
3779     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3781     // move knot, but without emitting the signal:
3782     // we cannot emit a "moved" signal because we're now processing it
3783     sp_knot_moveto(me->knot, &(me->pos));
3785     update_object(n->subpath->nodepath);
3787     /* status text */
3788     SPDesktop *desktop = n->subpath->nodepath->desktop;
3789     if (!desktop) return;
3790     SPEventContext *ec = desktop->event_context;
3791     if (!ec) return;
3792     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3793     if (!mc) return;
3795     double degrees = 180 / M_PI * rnew.a;
3796     if (degrees > 180) degrees -= 360;
3797     if (degrees < -180) degrees += 360;
3798     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3799         degrees = angle_to_compass (degrees);
3801     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3803     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3804          _("<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);
3806     g_string_free(length, TRUE);
3809 /**
3810  * Node handle event callback.
3811  */
3812 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3814     gboolean ret = FALSE;
3815     switch (event->type) {
3816         case GDK_KEY_PRESS:
3817             switch (get_group0_keyval (&event->key)) {
3818                 case GDK_space:
3819                     if (event->key.state & GDK_BUTTON1_MASK) {
3820                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3821                         stamp_repr(nodepath);
3822                         ret = TRUE;
3823                     }
3824                     break;
3825                 default:
3826                     break;
3827             }
3828             break;
3829         case GDK_ENTER_NOTIFY:
3830             // we use an experimentally determined threshold that seems to work fine
3831             if (NR::L2(n->pos - knot->pos) < 0.75)
3832                 Inkscape::NodePath::Path::active_node = n;
3833             break;
3834         case GDK_LEAVE_NOTIFY:
3835             // we use an experimentally determined threshold that seems to work fine
3836             if (NR::L2(n->pos - knot->pos) < 0.75)
3837                 Inkscape::NodePath::Path::active_node = NULL;
3838             break;
3839         default:
3840             break;
3841     }
3843     return ret;
3846 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3847                                  Radial &rme, Radial &rother, gboolean const both)
3849     rme.a += angle;
3850     if ( both
3851          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3852          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3853     {
3854         rother.a += angle;
3855     }
3858 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3859                                         Radial &rme, Radial &rother, gboolean const both)
3861     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3863     gdouble r;
3864     if ( both
3865          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3866          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3867     {
3868         r = MAX(rme.r, rother.r);
3869     } else {
3870         r = rme.r;
3871     }
3873     gdouble const weird_angle = atan2(norm_angle, r);
3874 /* Bulia says norm_angle is just the visible distance that the
3875  * object's end must travel on the screen.  Left as 'angle' for want of
3876  * a better name.*/
3878     rme.a += weird_angle;
3879     if ( both
3880          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3881          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3882     {
3883         rother.a += weird_angle;
3884     }
3887 /**
3888  * Rotate one node.
3889  */
3890 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3892     Inkscape::NodePath::NodeSide *me, *other;
3893     bool both = false;
3895     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3896     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3898     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3899         me = &(n->p);
3900         other = &(n->n);
3901     } else if (!n->p.other) {
3902         me = &(n->n);
3903         other = &(n->p);
3904     } else {
3905         if (which > 0) { // right handle
3906             if (xn > xp) {
3907                 me = &(n->n);
3908                 other = &(n->p);
3909             } else {
3910                 me = &(n->p);
3911                 other = &(n->n);
3912             }
3913         } else if (which < 0){ // left handle
3914             if (xn <= xp) {
3915                 me = &(n->n);
3916                 other = &(n->p);
3917             } else {
3918                 me = &(n->p);
3919                 other = &(n->n);
3920             }
3921         } else { // both handles
3922             me = &(n->n);
3923             other = &(n->p);
3924             both = true;
3925         }
3926     }
3928     Radial rme(me->pos - n->pos);
3929     Radial rother(other->pos - n->pos);
3931     if (screen) {
3932         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3933     } else {
3934         node_rotate_one_internal (*n, angle, rme, rother, both);
3935     }
3937     me->pos = n->pos + NR::Point(rme);
3939     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3940         other->pos =  n->pos + NR::Point(rother);
3941     }
3943     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3944     // so here we just move all the knots without emitting move signals, for speed
3945     sp_node_update_handles(n, false);
3948 /**
3949  * Rotate selected nodes.
3950  */
3951 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3953     if (!nodepath || !nodepath->selected) return;
3955     if (g_list_length(nodepath->selected) == 1) {
3956        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3957         node_rotate_one (n, angle, which, screen);
3958     } else {
3959        // rotate as an object:
3961         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3962         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3963         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3964             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3965             box.expandTo (n->pos); // contain all selected nodes
3966         }
3968         gdouble rot;
3969         if (screen) {
3970             gdouble const zoom = nodepath->desktop->current_zoom();
3971             gdouble const zmove = angle / zoom;
3972             gdouble const r = NR::L2(box.max() - box.midpoint());
3973             rot = atan2(zmove, r);
3974         } else {
3975             rot = angle;
3976         }
3978         NR::Point rot_center;
3979         if (Inkscape::NodePath::Path::active_node == NULL)
3980             rot_center = box.midpoint();
3981         else
3982             rot_center = Inkscape::NodePath::Path::active_node->pos;
3984         NR::Matrix t =
3985             NR::Matrix (NR::translate(-rot_center)) *
3986             NR::Matrix (NR::rotate(rot)) *
3987             NR::Matrix (NR::translate(rot_center));
3989         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3990             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3991             n->pos *= t;
3992             n->n.pos *= t;
3993             n->p.pos *= t;
3994             sp_node_update_handles(n, false);
3995         }
3996     }
3998     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4001 /**
4002  * Scale one node.
4003  */
4004 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4006     bool both = false;
4007     Inkscape::NodePath::NodeSide *me, *other;
4009     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4010     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4012     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4013         me = &(n->p);
4014         other = &(n->n);
4015         n->code = NR_CURVETO;
4016     } else if (!n->p.other) {
4017         me = &(n->n);
4018         other = &(n->p);
4019         if (n->n.other)
4020             n->n.other->code = NR_CURVETO;
4021     } else {
4022         if (which > 0) { // right handle
4023             if (xn > xp) {
4024                 me = &(n->n);
4025                 other = &(n->p);
4026                 if (n->n.other)
4027                     n->n.other->code = NR_CURVETO;
4028             } else {
4029                 me = &(n->p);
4030                 other = &(n->n);
4031                 n->code = NR_CURVETO;
4032             }
4033         } else if (which < 0){ // left handle
4034             if (xn <= xp) {
4035                 me = &(n->n);
4036                 other = &(n->p);
4037                 if (n->n.other)
4038                     n->n.other->code = NR_CURVETO;
4039             } else {
4040                 me = &(n->p);
4041                 other = &(n->n);
4042                 n->code = NR_CURVETO;
4043             }
4044         } else { // both handles
4045             me = &(n->n);
4046             other = &(n->p);
4047             both = true;
4048             n->code = NR_CURVETO;
4049             if (n->n.other)
4050                 n->n.other->code = NR_CURVETO;
4051         }
4052     }
4054     Radial rme(me->pos - n->pos);
4055     Radial rother(other->pos - n->pos);
4057     rme.r += grow;
4058     if (rme.r < 0) rme.r = 0;
4059     if (rme.a == HUGE_VAL) {
4060         if (me->other) { // if direction is unknown, initialize it towards the next node
4061             Radial rme_next(me->other->pos - n->pos);
4062             rme.a = rme_next.a;
4063         } else { // if there's no next, initialize to 0
4064             rme.a = 0;
4065         }
4066     }
4067     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4068         rother.r += grow;
4069         if (rother.r < 0) rother.r = 0;
4070         if (rother.a == HUGE_VAL) {
4071             rother.a = rme.a + M_PI;
4072         }
4073     }
4075     me->pos = n->pos + NR::Point(rme);
4077     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4078         other->pos = n->pos + NR::Point(rother);
4079     }
4081     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4082     // so here we just move all the knots without emitting move signals, for speed
4083     sp_node_update_handles(n, false);
4086 /**
4087  * Scale selected nodes.
4088  */
4089 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4091     if (!nodepath || !nodepath->selected) return;
4093     if (g_list_length(nodepath->selected) == 1) {
4094         // scale handles of the single selected node
4095         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4096         node_scale_one (n, grow, which);
4097     } else {
4098         // scale nodes as an "object":
4100         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4101         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4102         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4103             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4104             box.expandTo (n->pos); // contain all selected nodes
4105         }
4107         double scale = (box.maxExtent() + grow)/box.maxExtent();
4109         NR::Point scale_center;
4110         if (Inkscape::NodePath::Path::active_node == NULL)
4111             scale_center = box.midpoint();
4112         else
4113             scale_center = Inkscape::NodePath::Path::active_node->pos;
4115         NR::Matrix t =
4116             NR::Matrix (NR::translate(-scale_center)) *
4117             NR::Matrix (NR::scale(scale, scale)) *
4118             NR::Matrix (NR::translate(scale_center));
4120         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4121             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4122             n->pos *= t;
4123             n->n.pos *= t;
4124             n->p.pos *= t;
4125             sp_node_update_handles(n, false);
4126         }
4127     }
4129     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4132 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4134     if (!nodepath) return;
4135     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4138 /**
4139  * Flip selected nodes horizontally/vertically.
4140  */
4141 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4143     if (!nodepath || !nodepath->selected) return;
4145     if (g_list_length(nodepath->selected) == 1 && !center) {
4146         // flip handles of the single selected node
4147         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4148         double temp = n->p.pos[axis];
4149         n->p.pos[axis] = n->n.pos[axis];
4150         n->n.pos[axis] = temp;
4151         sp_node_update_handles(n, false);
4152     } else {
4153         // scale nodes as an "object":
4155         NR::Rect box = sp_node_selected_bbox (nodepath);
4156         if (!center) {
4157             center = box.midpoint();
4158         }
4159         NR::Matrix t =
4160             NR::Matrix (NR::translate(- *center)) *
4161             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4162             NR::Matrix (NR::translate(*center));
4164         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4165             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4166             n->pos *= t;
4167             n->n.pos *= t;
4168             n->p.pos *= t;
4169             sp_node_update_handles(n, false);
4170         }
4171     }
4173     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4176 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4178     g_assert (nodepath->selected);
4180     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4181     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4182     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4183         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4184         box.expandTo (n->pos); // contain all selected nodes
4185     }
4186     return box;
4189 //-----------------------------------------------
4190 /**
4191  * Return new subpath under given nodepath.
4192  */
4193 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4195     g_assert(nodepath);
4196     g_assert(nodepath->desktop);
4198    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4200     s->nodepath = nodepath;
4201     s->closed = FALSE;
4202     s->nodes = NULL;
4203     s->first = NULL;
4204     s->last = NULL;
4206     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4207     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4208     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4210     return s;
4213 /**
4214  * Destroy nodes in subpath, then subpath itself.
4215  */
4216 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4218     g_assert(subpath);
4219     g_assert(subpath->nodepath);
4220     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4222     while (subpath->nodes) {
4223         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4224     }
4226     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4228     g_free(subpath);
4231 /**
4232  * Link head to tail in subpath.
4233  */
4234 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4236     g_assert(!sp->closed);
4237     g_assert(sp->last != sp->first);
4238     g_assert(sp->first->code == NR_MOVETO);
4240     sp->closed = TRUE;
4242     //Link the head to the tail
4243     sp->first->p.other = sp->last;
4244     sp->last->n.other  = sp->first;
4245     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4246     sp->first          = sp->last;
4248     //Remove the extra end node
4249     sp_nodepath_node_destroy(sp->last->n.other);
4252 /**
4253  * Open closed (loopy) subpath at node.
4254  */
4255 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4257     g_assert(sp->closed);
4258     g_assert(n->subpath == sp);
4259     g_assert(sp->first == sp->last);
4261     /* We create new startpoint, current node will become last one */
4263    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4264                                                 &n->pos, &n->pos, &n->n.pos);
4267     sp->closed        = FALSE;
4269     //Unlink to make a head and tail
4270     sp->first         = new_path;
4271     sp->last          = n;
4272     n->n.other        = NULL;
4273     new_path->p.other = NULL;
4276 /**
4277  * Return new node in subpath with given properties.
4278  * \param pos Position of node.
4279  * \param ppos Handle position in previous direction
4280  * \param npos Handle position in previous direction
4281  */
4282 Inkscape::NodePath::Node *
4283 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)
4285     g_assert(sp);
4286     g_assert(sp->nodepath);
4287     g_assert(sp->nodepath->desktop);
4289     if (nodechunk == NULL)
4290         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4292     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4294     n->subpath  = sp;
4296     if (type != Inkscape::NodePath::NODE_NONE) {
4297         // use the type from sodipodi:nodetypes
4298         n->type = type;
4299     } else {
4300         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4301             // points are (almost) collinear
4302             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4303                 // endnode, or a node with a retracted handle
4304                 n->type = Inkscape::NodePath::NODE_CUSP;
4305             } else {
4306                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4307             }
4308         } else {
4309             n->type = Inkscape::NodePath::NODE_CUSP;
4310         }
4311     }
4313     n->code     = code;
4314     n->selected = FALSE;
4315     n->pos      = *pos;
4316     n->p.pos    = *ppos;
4317     n->n.pos    = *npos;
4319     n->dragging_out = NULL;
4321     Inkscape::NodePath::Node *prev;
4322     if (next) {
4323         //g_assert(g_list_find(sp->nodes, next));
4324         prev = next->p.other;
4325     } else {
4326         prev = sp->last;
4327     }
4329     if (prev)
4330         prev->n.other = n;
4331     else
4332         sp->first = n;
4334     if (next)
4335         next->p.other = n;
4336     else
4337         sp->last = n;
4339     n->p.other = prev;
4340     n->n.other = next;
4342     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"));
4343     sp_knot_set_position(n->knot, pos, 0);
4345     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4346     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4347     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4348     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4349     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4350     sp_knot_update_ctrl(n->knot);
4352     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4353     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4354     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4355     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4356     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4357     sp_knot_show(n->knot);
4359     // We only create handle knots and lines on demand
4360     n->p.knot = NULL;
4361     n->p.line = NULL;
4362     n->n.knot = NULL;
4363     n->n.line = NULL;
4365     sp->nodes = g_list_prepend(sp->nodes, n);
4367     return n;
4370 /**
4371  * Destroy node and its knots, link neighbors in subpath.
4372  */
4373 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4375     g_assert(node);
4376     g_assert(node->subpath);
4377     g_assert(SP_IS_KNOT(node->knot));
4379    Inkscape::NodePath::SubPath *sp = node->subpath;
4381     if (node->selected) { // first, deselect
4382         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4383         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4384     }
4386     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4388     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4389     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4390     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4391     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4392     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4393     g_object_unref(G_OBJECT(node->knot));
4395     if (node->p.knot) {
4396         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4397         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4398         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4399         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4400         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4401         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4402         g_object_unref(G_OBJECT(node->p.knot));
4403         node->p.knot = NULL;
4404     }
4406     if (node->n.knot) {
4407         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4408         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4409         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4410         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4411         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4412         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4413         g_object_unref(G_OBJECT(node->n.knot));
4414         node->n.knot = NULL;
4415     }
4417     if (node->p.line)
4418         gtk_object_destroy(GTK_OBJECT(node->p.line));
4419     if (node->n.line)
4420         gtk_object_destroy(GTK_OBJECT(node->n.line));
4422     if (sp->nodes) { // there are others nodes on the subpath
4423         if (sp->closed) {
4424             if (sp->first == node) {
4425                 g_assert(sp->last == node);
4426                 sp->first = node->n.other;
4427                 sp->last = sp->first;
4428             }
4429             node->p.other->n.other = node->n.other;
4430             node->n.other->p.other = node->p.other;
4431         } else {
4432             if (sp->first == node) {
4433                 sp->first = node->n.other;
4434                 sp->first->code = NR_MOVETO;
4435             }
4436             if (sp->last == node) sp->last = node->p.other;
4437             if (node->p.other) node->p.other->n.other = node->n.other;
4438             if (node->n.other) node->n.other->p.other = node->p.other;
4439         }
4440     } else { // this was the last node on subpath
4441         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4442     }
4444     g_mem_chunk_free(nodechunk, node);
4447 /**
4448  * Returns one of the node's two sides.
4449  * \param which Indicates which side.
4450  * \return Pointer to previous node side if which==-1, next if which==1.
4451  */
4452 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4454     g_assert(node);
4456     switch (which) {
4457         case -1:
4458             return &node->p;
4459         case 1:
4460             return &node->n;
4461         default:
4462             break;
4463     }
4465     g_assert_not_reached();
4467     return NULL;
4470 /**
4471  * Return the other side of the node, given one of its sides.
4472  */
4473 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4475     g_assert(node);
4477     if (me == &node->p) return &node->n;
4478     if (me == &node->n) return &node->p;
4480     g_assert_not_reached();
4482     return NULL;
4485 /**
4486  * Return NRPathcode on the given side of the node.
4487  */
4488 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4490     g_assert(node);
4492     if (me == &node->p) {
4493         if (node->p.other) return (NRPathcode)node->code;
4494         return NR_MOVETO;
4495     }
4497     if (me == &node->n) {
4498         if (node->n.other) return (NRPathcode)node->n.other->code;
4499         return NR_MOVETO;
4500     }
4502     g_assert_not_reached();
4504     return NR_END;
4507 /**
4508  * Return node with the given index
4509  */
4510 Inkscape::NodePath::Node *
4511 sp_nodepath_get_node_by_index(int index)
4513     Inkscape::NodePath::Node *e = NULL;
4515     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4516     if (!nodepath) {
4517         return e;
4518     }
4520     //find segment
4521     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4523         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4524         int n = g_list_length(sp->nodes);
4525         if (sp->closed) {
4526             n++;
4527         }
4529         //if the piece belongs to this subpath grab it
4530         //otherwise move onto the next subpath
4531         if (index < n) {
4532             e = sp->first;
4533             for (int i = 0; i < index; ++i) {
4534                 e = e->n.other;
4535             }
4536             break;
4537         } else {
4538             if (sp->closed) {
4539                 index -= (n+1);
4540             } else {
4541                 index -= n;
4542             }
4543         }
4544     }
4546     return e;
4549 /**
4550  * Returns plain text meaning of node type.
4551  */
4552 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4554     unsigned retracted = 0;
4555     bool endnode = false;
4557     for (int which = -1; which <= 1; which += 2) {
4558         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4559         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4560             retracted ++;
4561         if (!side->other)
4562             endnode = true;
4563     }
4565     if (retracted == 0) {
4566         if (endnode) {
4567                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4568                 return _("end node");
4569         } else {
4570             switch (node->type) {
4571                 case Inkscape::NodePath::NODE_CUSP:
4572                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4573                     return _("cusp");
4574                 case Inkscape::NodePath::NODE_SMOOTH:
4575                     // TRANSLATORS: "smooth" is an adjective here
4576                     return _("smooth");
4577                 case Inkscape::NodePath::NODE_SYMM:
4578                     return _("symmetric");
4579             }
4580         }
4581     } else if (retracted == 1) {
4582         if (endnode) {
4583             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4584             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4585         } else {
4586             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4587         }
4588     } else {
4589         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4590     }
4592     return NULL;
4595 /**
4596  * Handles content of statusbar as long as node tool is active.
4597  */
4598 void
4599 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4601     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");
4602     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4604     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4605     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4606     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4607     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4609     SPDesktop *desktop = NULL;
4610     if (nodepath) {
4611         desktop = nodepath->desktop;
4612     } else {
4613         desktop = SP_ACTIVE_DESKTOP;
4614     }
4616     SPEventContext *ec = desktop->event_context;
4617     if (!ec) return;
4618     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4619     if (!mc) return;
4621     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4623     if (selected_nodes == 0) {
4624         Inkscape::Selection *sel = desktop->selection;
4625         if (!sel || sel->isEmpty()) {
4626             mc->setF(Inkscape::NORMAL_MESSAGE,
4627                      _("Select a single object to edit its nodes or handles."));
4628         } else {
4629             if (nodepath) {
4630             mc->setF(Inkscape::NORMAL_MESSAGE,
4631                      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.",
4632                               "<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.",
4633                               total_nodes),
4634                      total_nodes);
4635             } else {
4636                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4637                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4638                 } else {
4639                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4640                 }
4641             }
4642         }
4643     } else if (nodepath && selected_nodes == 1) {
4644         mc->setF(Inkscape::NORMAL_MESSAGE,
4645                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4646                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4647                           total_nodes),
4648                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4649     } else {
4650         if (selected_subpaths > 1) {
4651             mc->setF(Inkscape::NORMAL_MESSAGE,
4652                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4653                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4654                               total_nodes),
4655                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4656         } else {
4657             mc->setF(Inkscape::NORMAL_MESSAGE,
4658                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4659                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4660                               total_nodes),
4661                      selected_nodes, total_nodes, when_selected);
4662         }
4663     }
4666 /*
4667  * returns a *copy* of the curve of that object.
4668  */
4669 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4670     if (!object)
4671         return NULL;
4673     SPCurve *curve = NULL;
4674     if (SP_IS_PATH(object)) {
4675         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4676         curve = sp_curve_copy(curve_new);
4677     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4678         const gchar *svgd = object->repr->attribute(key);
4679         if (svgd) {
4680             NArtBpath *bpath = sp_svg_read_path(svgd);
4681             SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
4682             if (curve_new) {
4683                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4684             } else {
4685                 g_free(bpath);
4686             }
4687         }
4688     }
4690     return curve;
4693 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4694     if (!np || !np->object || !curve)
4695         return;
4697     if (SP_IS_PATH(np->object)) {
4698         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4699             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4700         } else {
4701             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4702         }
4703     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4704         // FIXME: this writing to string and then reading from string is bound to be slow.
4705         // create a method to convert from curve directly to 2geom...
4706         gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
4707         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4708         g_free(svgpath);
4710         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4711     }
4714 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4715     np->show_helperpath = show;
4717     if (show) {
4718         SPCurve *helper_curve = sp_curve_copy(np->curve);
4719         sp_curve_transform(helper_curve, np->i2d );
4720         if (!np->helper_path) {
4721             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4722             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);
4723             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4724             sp_canvas_item_move_to_z(np->helper_path, 0);
4725             sp_canvas_item_show(np->helper_path);
4726         } else {
4727             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4728         }
4729         sp_curve_unref(helper_curve);
4730     } else {
4731         if (np->helper_path) {
4732             GtkObject *temp = np->helper_path;
4733             np->helper_path = NULL;
4734             gtk_object_destroy(temp);
4735         }
4736     }
4739 /* sp_nodepath_make_straight_path:
4740  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4741  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4742  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4743  */
4744 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4745     np->straight_path = true;
4746     np->show_handles = false;
4747     g_message("add code to make the path straight.");
4748     // do sp_nodepath_convert_node_type on all nodes?
4749     // coding tip: search for this text : "Make selected segments lines"
4753 /*
4754   Local Variables:
4755   mode:c++
4756   c-file-style:"stroustrup"
4757   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4758   indent-tabs-mode:nil
4759   fill-column:99
4760   End:
4761 */
4762 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :