Code

fix bug when editing endnodes
[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 = curve->first_bpath();
184     gint length = curve->end;
185     if (length == 0) {
186         curve->unref();
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         curve->unref();
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 = curve->copy();
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     curve->unref();
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 = np->curve->copy();
269         helper_curve->transform(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         helper_curve->unref();
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         np->curve->unref();
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         curve->unref();
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     np->curve->unref();
537     np->curve = create_curve(np);
539     sp_nodepath_set_curve(np, np->curve);
541     if (np->show_helperpath) {
542         SPCurve * helper_curve = np->curve->copy();
543         helper_curve->transform(np->i2d );
544         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
545         helper_curve->unref();
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     np->curve->unref();
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 = np->curve->copy();
580         helper_curve->transform(np->i2d );
581         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
582         helper_curve->unref();
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     curve->unref();
658 /**
659  * Create curve from path.
660  */
661 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
663     SPCurve *curve = new SPCurve();
665     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
666        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
667         curve->moveto(sp->first->pos * np->d2i);
668        Inkscape::NodePath::Node *n = sp->first->n.other;
669         while (n) {
670             NR::Point const end_pt = n->pos * np->d2i;
671             switch (n->code) {
672                 case NR_LINETO:
673                     curve->lineto(end_pt);
674                     break;
675                 case NR_CURVETO:
676                     curve->curveto(n->p.other->n.pos * np->d2i,
677                                      n->p.pos * np->d2i,
678                                      end_pt);
679                     break;
680                 default:
681                     g_assert_not_reached();
682                     break;
683             }
684             if (n != sp->last) {
685                 n = n->n.other;
686             } else {
687                 n = NULL;
688             }
689         }
690         if (sp->closed) {
691             curve->closepath();
692         }
693     }
695     return curve;
698 /**
699  * Convert path type string to sodipodi:nodetypes style.
700  */
701 static gchar *create_typestr(Inkscape::NodePath::Path *np)
703     gchar *typestr = g_new(gchar, 32);
704     gint len = 32;
705     gint pos = 0;
707     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
708        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
710         if (pos >= len) {
711             typestr = g_renew(gchar, typestr, len + 32);
712             len += 32;
713         }
715         typestr[pos++] = 'c';
717        Inkscape::NodePath::Node *n;
718         n = sp->first->n.other;
719         while (n) {
720             gchar code;
722             switch (n->type) {
723                 case Inkscape::NodePath::NODE_CUSP:
724                     code = 'c';
725                     break;
726                 case Inkscape::NodePath::NODE_SMOOTH:
727                     code = 's';
728                     break;
729                 case Inkscape::NodePath::NODE_SYMM:
730                     code = 'z';
731                     break;
732                 default:
733                     g_assert_not_reached();
734                     code = '\0';
735                     break;
736             }
738             if (pos >= len) {
739                 typestr = g_renew(gchar, typestr, len + 32);
740                 len += 32;
741             }
743             typestr[pos++] = code;
745             if (n != sp->last) {
746                 n = n->n.other;
747             } else {
748                 n = NULL;
749             }
750         }
751     }
753     if (pos >= len) {
754         typestr = g_renew(gchar, typestr, len + 1);
755         len += 1;
756     }
758     typestr[pos++] = '\0';
760     return typestr;
763 /**
764  * Returns current path in context. // later eliminate this function at all!
765  */
766 static Inkscape::NodePath::Path *sp_nodepath_current()
768     if (!SP_ACTIVE_DESKTOP) {
769         return NULL;
770     }
772     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
774     if (!SP_IS_NODE_CONTEXT(event_context)) {
775         return NULL;
776     }
778     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
783 /**
784  \brief Fills node and handle positions for three nodes, splitting line
785   marked by end at distance t.
786  */
787 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
789     g_assert(new_path != NULL);
790     g_assert(end      != NULL);
792     g_assert(end->p.other == new_path);
793    Inkscape::NodePath::Node *start = new_path->p.other;
794     g_assert(start);
796     if (end->code == NR_LINETO) {
797         new_path->type =Inkscape::NodePath::NODE_CUSP;
798         new_path->code = NR_LINETO;
799         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
800     } else {
801         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
802         new_path->code = NR_CURVETO;
803         gdouble s      = 1 - t;
804         for (int dim = 0; dim < 2; dim++) {
805             NR::Coord const f000 = start->pos[dim];
806             NR::Coord const f001 = start->n.pos[dim];
807             NR::Coord const f011 = end->p.pos[dim];
808             NR::Coord const f111 = end->pos[dim];
809             NR::Coord const f00t = s * f000 + t * f001;
810             NR::Coord const f01t = s * f001 + t * f011;
811             NR::Coord const f11t = s * f011 + t * f111;
812             NR::Coord const f0tt = s * f00t + t * f01t;
813             NR::Coord const f1tt = s * f01t + t * f11t;
814             NR::Coord const fttt = s * f0tt + t * f1tt;
815             start->n.pos[dim]    = f00t;
816             new_path->p.pos[dim] = f0tt;
817             new_path->pos[dim]   = fttt;
818             new_path->n.pos[dim] = f1tt;
819             end->p.pos[dim]      = f11t;
820         }
821     }
824 /**
825  * Adds new node on direct line between two nodes, activates handles of all
826  * three nodes.
827  */
828 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
830     g_assert(end);
831     g_assert(end->subpath);
832     g_assert(g_list_find(end->subpath->nodes, end));
834    Inkscape::NodePath::Node *start = end->p.other;
835     g_assert( start->n.other == end );
836    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
837                                                end,
838                                                (NRPathcode)end->code == NR_LINETO?
839                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
840                                                (NRPathcode)end->code,
841                                                &start->pos, &start->pos, &start->n.pos);
842     sp_nodepath_line_midpoint(newnode, end, t);
844     sp_node_adjust_handles(start);
845     sp_node_update_handles(start);
846     sp_node_update_handles(newnode);
847     sp_node_adjust_handles(end);
848     sp_node_update_handles(end);
850     return newnode;
853 /**
854 \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
855 */
856 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
858     g_assert(node);
859     g_assert(node->subpath);
860     g_assert(g_list_find(node->subpath->nodes, node));
862    Inkscape::NodePath::SubPath *sp = node->subpath;
863     Inkscape::NodePath::Path *np    = sp->nodepath;
865     if (sp->closed) {
866         sp_nodepath_subpath_open(sp, node);
867         return sp->first;
868     } else {
869         // no break for end nodes
870         if (node == sp->first) return NULL;
871         if (node == sp->last ) return NULL;
873         // create a new subpath
874        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
876         // duplicate the break node as start of the new subpath
877        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
879         while (node->n.other) { // copy the remaining nodes into the new subpath
880            Inkscape::NodePath::Node *n  = node->n.other;
881            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);
882             if (n->selected) {
883                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
884             }
885             sp_nodepath_node_destroy(n); // remove the point on the original subpath
886         }
888         return newnode;
889     }
892 /**
893  * Duplicate node and connect to neighbours.
894  */
895 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
897     g_assert(node);
898     g_assert(node->subpath);
899     g_assert(g_list_find(node->subpath->nodes, node));
901    Inkscape::NodePath::SubPath *sp = node->subpath;
903     NRPathcode code = (NRPathcode) node->code;
904     if (code == NR_MOVETO) { // if node is the endnode,
905         node->code = NR_LINETO; // new one is inserted before it, so change that to line
906     }
908     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
910     if (!node->n.other || !node->p.other) // if node is an endnode, select it
911         return node;
912     else
913         return newnode; // otherwise select the newly created node
916 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
918     node->p.pos = (node->pos + (node->pos - node->n.pos));
921 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
923     node->n.pos = (node->pos + (node->pos - node->p.pos));
926 /**
927  * Change line type at node, with side effects on neighbours.
928  */
929 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
931     g_assert(end);
932     g_assert(end->subpath);
933     g_assert(end->p.other);
935     if (end->code == static_cast< guint > ( code ) )
936         return;
938    Inkscape::NodePath::Node *start = end->p.other;
940     end->code = code;
942     if (code == NR_LINETO) {
943         if (start->code == NR_LINETO) {
944             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
945         }
946         if (end->n.other) {
947             if (end->n.other->code == NR_LINETO) {
948                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
949             }
950         }
951     } else {
952         NR::Point delta = end->pos - start->pos;
953         start->n.pos = start->pos + delta / 3;
954         end->p.pos = end->pos - delta / 3;
955         sp_node_adjust_handle(start, 1);
956         sp_node_adjust_handle(end, -1);
957     }
959     sp_node_update_handles(start);
960     sp_node_update_handles(end);
963 /**
964  * Change node type, and its handles accordingly.
965  */
966 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
968     g_assert(node);
969     g_assert(node->subpath);
971     if ((node->p.other != NULL) && (node->n.other != NULL)) {
972         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
973             type =Inkscape::NodePath::NODE_CUSP;
974         }
975     }
977     node->type = type;
979     if (node->type == Inkscape::NodePath::NODE_CUSP) {
980         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
981         node->knot->setSize (node->selected? 11 : 9);
982         sp_knot_update_ctrl(node->knot);
983     } else {
984         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
985         node->knot->setSize (node->selected? 9 : 7);
986         sp_knot_update_ctrl(node->knot);
987     }
989     // if one of handles is mouseovered, preserve its position
990     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
991         sp_node_adjust_handle(node, 1);
992     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
993         sp_node_adjust_handle(node, -1);
994     } else {
995         sp_node_adjust_handles(node);
996     }
998     sp_node_update_handles(node);
1000     sp_nodepath_update_statusbar(node->subpath->nodepath);
1002     return node;
1005 bool
1006 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1008         Inkscape::NodePath::Node *othernode = side->other;
1009         if (!othernode)
1010             return false;
1011         NRPathcode const code = sp_node_path_code_from_side(node, side);
1012         if (code == NR_LINETO)
1013             return true;
1014         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1015         if (&node->p == side) {
1016             other_to_me = &othernode->n;
1017         } else if (&node->n == side) {
1018             other_to_me = &othernode->p;
1019         } 
1020         if (!other_to_me)
1021             return false;
1022         bool is_line = 
1023              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1024               NR::L2(node->pos - side->pos) < 1e-6);
1025         return is_line;
1028 /**
1029  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1030  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1031  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1032  * If already cusp and set to cusp, retracts handles.
1033 */
1034 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1036     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1038 /* 
1039   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1040  
1041         if (two_handles) {
1042             // do nothing, adjust_handles called via set_node_type will line them up
1043         } else if (one_handle) {
1044             if (opposite_to_handle_is_line) {
1045                 if (lined_up) {
1046                     // already half-smooth; pull opposite handle too making it fully smooth
1047                 } else {
1048                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1049                 }
1050             } else {
1051                 // pull opposite handle in line with the existing one
1052             }
1053         } else if (no_handles) {
1054             if (both_segments_are_lines OR both_segments_are_curves) {
1055                 //pull both handles
1056             } else {
1057                 // pull the handle opposite to line segment, making node half-smooth
1058             }
1059         }
1060 */
1061         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1062         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1063         bool p_is_line = sp_node_side_is_line(node, &node->p);
1064         bool n_is_line = sp_node_side_is_line(node, &node->n);
1066         if (p_has_handle && n_has_handle) {
1067             // do nothing, adjust_handles will line them up
1068         } else if (p_has_handle || n_has_handle) {
1069             if (p_has_handle && n_is_line) {
1070                 Radial line (node->n.other->pos - node->pos);
1071                 Radial handle (node->pos - node->p.pos);
1072                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1073                     // already half-smooth; pull opposite handle too making it fully smooth
1074                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1075                 } else {
1076                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1077                 }
1078             } else if (n_has_handle && p_is_line) {
1079                 Radial line (node->p.other->pos - node->pos);
1080                 Radial handle (node->pos - node->n.pos);
1081                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1082                     // already half-smooth; pull opposite handle too making it fully smooth
1083                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1084                 } else {
1085                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1086                 }
1087             } else if (p_has_handle && node->n.other) {
1088                 // pull n handle
1089                 node->n.other->code = NR_CURVETO;
1090                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1091                     NR::L2(node->p.pos - node->pos) :
1092                     NR::L2(node->n.other->pos - node->pos) / 3;
1093                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1094             } else if (n_has_handle && node->p.other) {
1095                 // pull p handle
1096                 node->code = NR_CURVETO;
1097                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1098                     NR::L2(node->n.pos - node->pos) :
1099                     NR::L2(node->p.other->pos - node->pos) / 3;
1100                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1101             }
1102         } else if (!p_has_handle && !n_has_handle) {
1103             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1104                 // no handles, but both segments are either lnes or curves:
1105                 //pull both handles
1107                 // convert both to curves:
1108                 node->code = NR_CURVETO;
1109                 node->n.other->code = NR_CURVETO;
1111                 NR::Point leg_prev = node->pos - node->p.other->pos;
1112                 NR::Point leg_next = node->pos - node->n.other->pos;
1114                 double norm_leg_prev = L2(leg_prev);
1115                 double norm_leg_next = L2(leg_next);
1117                 NR::Point delta;
1118                 if (norm_leg_next > 0.0) {
1119                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1120                     (&delta)->normalize();
1121                 }
1123                 if (type == Inkscape::NodePath::NODE_SYMM) {
1124                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1125                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1126                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1127                 } else {
1128                     // length of handle is proportional to distance to adjacent node
1129                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1130                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1131                 }
1133             } else {
1134                 // pull the handle opposite to line segment, making it half-smooth
1135                 if (p_is_line && node->n.other) {
1136                     if (type != Inkscape::NodePath::NODE_SYMM) {
1137                         // pull n handle
1138                         node->n.other->code = NR_CURVETO;
1139                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1140                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1141                     }
1142                 } else if (n_is_line && node->p.other) {
1143                     if (type != Inkscape::NodePath::NODE_SYMM) {
1144                         // pull p handle
1145                         node->code = NR_CURVETO;
1146                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1147                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1148                     }
1149                 }
1150             }
1151         }
1152     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1153         // cusping a cusp: retract nodes
1154         node->p.pos = node->pos;
1155         node->n.pos = node->pos;
1156     }
1158     sp_nodepath_set_node_type (node, type);
1161 /**
1162  * Move node to point, and adjust its and neighbouring handles.
1163  */
1164 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1166     NR::Point delta = p - node->pos;
1167     node->pos = p;
1169     node->p.pos += delta;
1170     node->n.pos += delta;
1172     Inkscape::NodePath::Node *node_p = NULL;
1173     Inkscape::NodePath::Node *node_n = NULL;
1175     if (node->p.other) {
1176         if (node->code == NR_LINETO) {
1177             sp_node_adjust_handle(node, 1);
1178             sp_node_adjust_handle(node->p.other, -1);
1179             node_p = node->p.other;
1180         }
1181     }
1182     if (node->n.other) {
1183         if (node->n.other->code == NR_LINETO) {
1184             sp_node_adjust_handle(node, -1);
1185             sp_node_adjust_handle(node->n.other, 1);
1186             node_n = node->n.other;
1187         }
1188     }
1190     // this function is only called from batch movers that will update display at the end
1191     // themselves, so here we just move all the knots without emitting move signals, for speed
1192     sp_node_update_handles(node, false);
1193     if (node_n) {
1194         sp_node_update_handles(node_n, false);
1195     }
1196     if (node_p) {
1197         sp_node_update_handles(node_p, false);
1198     }
1201 /**
1202  * Call sp_node_moveto() for node selection and handle possible snapping.
1203  */
1204 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1205                                             bool const snap = true)
1207     NR::Coord best = NR_HUGE;
1208     NR::Point delta(dx, dy);
1209     NR::Point best_pt = delta;
1210     Inkscape::SnappedPoint best_abs;
1212     
1213     if (snap) {    
1214         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1215          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1216          * must provide that information. */
1217           
1218         // Build a list of the unselected nodes to which the snapper should snap 
1219         std::vector<NR::Point> unselected_nodes;
1220         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1221             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1222             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1223                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1224                 if (!node->selected) {
1225                     unselected_nodes.push_back(node->pos);
1226                 }    
1227             }
1228         }        
1229         
1230         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1231         
1232         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1233             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1234             m.setup(nodepath->desktop, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1235             Inkscape::SnappedPoint s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta);            
1236             if (s.getDistance() < best) {
1237                 best = s.getDistance();
1238                 best_abs = s;
1239                 best_pt = s.getPoint() - n->pos;
1240             }
1241         }
1242                         
1243         if (best_abs.getSnapped()) {
1244             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1245         }
1246     }
1248     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1249         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1250         sp_node_moveto(n, n->pos + best_pt);
1251     }
1253     // do not update repr here so that node dragging is acceptably fast
1254     update_object(nodepath);
1257 /**
1258 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1259 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1260 near x = 0.
1261  */
1262 double
1263 sculpt_profile (double x, double alpha, guint profile)
1265     if (x >= 1)
1266         return 0;
1267     if (x <= 0)
1268         return 1;
1270     switch (profile) {
1271         case SCULPT_PROFILE_LINEAR:
1272         return 1 - x;
1273         case SCULPT_PROFILE_BELL:
1274         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1275         case SCULPT_PROFILE_ELLIPTIC:
1276         return sqrt(1 - x*x);
1277     }
1279     return 1;
1282 double
1283 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1285     // extremely primitive for now, don't have time to look for the real one
1286     double lower = NR::L2(b - a);
1287     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1288     return (lower + upper)/2;
1291 void
1292 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1294     n->pos = n->origin + delta;
1295     n->n.pos = n->n.origin + delta_n;
1296     n->p.pos = n->p.origin + delta_p;
1297     sp_node_adjust_handles(n);
1298     sp_node_update_handles(n, false);
1301 /**
1302  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1303  * on how far they are from the dragged node n.
1304  */
1305 static void
1306 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1308     g_assert (n);
1309     g_assert (nodepath);
1310     g_assert (n->subpath->nodepath == nodepath);
1312     double pressure = n->knot->pressure;
1313     if (pressure == 0)
1314         pressure = 0.5; // default
1315     pressure = CLAMP (pressure, 0.2, 0.8);
1317     // map pressure to alpha = 1/5 ... 5
1318     double alpha = 1 - 2 * fabs(pressure - 0.5);
1319     if (pressure > 0.5)
1320         alpha = 1/alpha;
1322     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1324     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1325         // Only one subpath has selected nodes:
1326         // use linear mode, where the distance from n to node being dragged is calculated along the path
1328         double n_sel_range = 0, p_sel_range = 0;
1329         guint n_nodes = 0, p_nodes = 0;
1330         guint n_sel_nodes = 0, p_sel_nodes = 0;
1332         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1333         {
1334             double n_range = 0, p_range = 0;
1335             bool n_going = true, p_going = true;
1336             Inkscape::NodePath::Node *n_node = n;
1337             Inkscape::NodePath::Node *p_node = n;
1338             do {
1339                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1340                 if (n_node && n_going)
1341                     n_node = n_node->n.other;
1342                 if (n_node == NULL) {
1343                     n_going = false;
1344                 } else {
1345                     n_nodes ++;
1346                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1347                     if (n_node->selected) {
1348                         n_sel_nodes ++;
1349                         n_sel_range = n_range;
1350                     }
1351                     if (n_node == p_node) {
1352                         n_going = false;
1353                         p_going = false;
1354                     }
1355                 }
1356                 if (p_node && p_going)
1357                     p_node = p_node->p.other;
1358                 if (p_node == NULL) {
1359                     p_going = false;
1360                 } else {
1361                     p_nodes ++;
1362                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1363                     if (p_node->selected) {
1364                         p_sel_nodes ++;
1365                         p_sel_range = p_range;
1366                     }
1367                     if (p_node == n_node) {
1368                         n_going = false;
1369                         p_going = false;
1370                     }
1371                 }
1372             } while (n_going || p_going);
1373         }
1375         // Second pass: actually move nodes in this subpath
1376         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1377         {
1378             double n_range = 0, p_range = 0;
1379             bool n_going = true, p_going = true;
1380             Inkscape::NodePath::Node *n_node = n;
1381             Inkscape::NodePath::Node *p_node = n;
1382             do {
1383                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1384                 if (n_node && n_going)
1385                     n_node = n_node->n.other;
1386                 if (n_node == NULL) {
1387                     n_going = false;
1388                 } else {
1389                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1390                     if (n_node->selected) {
1391                         sp_nodepath_move_node_and_handles (n_node,
1392                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1393                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1394                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1395                     }
1396                     if (n_node == p_node) {
1397                         n_going = false;
1398                         p_going = false;
1399                     }
1400                 }
1401                 if (p_node && p_going)
1402                     p_node = p_node->p.other;
1403                 if (p_node == NULL) {
1404                     p_going = false;
1405                 } else {
1406                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1407                     if (p_node->selected) {
1408                         sp_nodepath_move_node_and_handles (p_node,
1409                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1410                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1411                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1412                     }
1413                     if (p_node == n_node) {
1414                         n_going = false;
1415                         p_going = false;
1416                     }
1417                 }
1418             } while (n_going || p_going);
1419         }
1421     } else {
1422         // Multiple subpaths have selected nodes:
1423         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1424         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1425         // fix the pear-like shape when sculpting e.g. a ring
1427         // First pass: calculate range
1428         gdouble direct_range = 0;
1429         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1430             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1431             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1432                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1433                 if (node->selected) {
1434                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1435                 }
1436             }
1437         }
1439         // Second pass: actually move nodes
1440         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1441             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1442             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1443                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1444                 if (node->selected) {
1445                     if (direct_range > 1e-6) {
1446                         sp_nodepath_move_node_and_handles (node,
1447                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1448                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1449                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1450                     } else {
1451                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1452                     }
1454                 }
1455             }
1456         }
1457     }
1459     // do not update repr here so that node dragging is acceptably fast
1460     update_object(nodepath);
1464 /**
1465  * Move node selection to point, adjust its and neighbouring handles,
1466  * handle possible snapping, and commit the change with possible undo.
1467  */
1468 void
1469 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1471     if (!nodepath) return;
1473     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1475     if (dx == 0) {
1476         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1477     } else if (dy == 0) {
1478         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1479     } else {
1480         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1481     }
1484 /**
1485  * Move node selection off screen and commit the change.
1486  */
1487 void
1488 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1490     // borrowed from sp_selection_move_screen in selection-chemistry.c
1491     // we find out the current zoom factor and divide deltas by it
1492     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1494     gdouble zoom = desktop->current_zoom();
1495     gdouble zdx = dx / zoom;
1496     gdouble zdy = dy / zoom;
1498     if (!nodepath) return;
1500     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1502     if (dx == 0) {
1503         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1504     } else if (dy == 0) {
1505         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1506     } else {
1507         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1508     }
1511 /**
1512  * Move selected nodes to the absolute position given
1513  */
1514 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1516     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1517         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1518         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1519         sp_node_moveto(n, npos);
1520     }
1522     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1525 /**
1526  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1527  */
1528 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1530     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1531     g_return_val_if_fail(nodepath->selected, no_coord);
1533     // determine coordinate of first selected node
1534     GList *nsel = nodepath->selected;
1535     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1536     NR::Coord coord = n->pos[axis];
1537     bool coincide = true;
1539     // compare it to the coordinates of all the other selected nodes
1540     for (GList *l = nsel->next; l != NULL; l = l->next) {
1541         n = (Inkscape::NodePath::Node *) l->data;
1542         if (n->pos[axis] != coord) {
1543             coincide = false;
1544         }
1545     }
1546     if (coincide) {
1547         return coord;
1548     } else {
1549         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1550         // currently we return the coordinate of the bounding box midpoint because I don't know how
1551         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1552         return bbox.midpoint()[axis];
1553     }
1556 /** If they don't yet exist, creates knot and line for the given side of the node */
1557 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1559     if (!side->knot) {
1560         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"));
1562         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1563         side->knot->setSize (7);
1564         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1565         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1566         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1567         sp_knot_update_ctrl(side->knot);
1569         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1570         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1571         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1572         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1573         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1574         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1575     }
1577     if (!side->line) {
1578         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1579                                         SP_TYPE_CTRLLINE, NULL);
1580     }
1583 /**
1584  * Ensure the given handle of the node is visible/invisible, update its screen position
1585  */
1586 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1588     g_assert(node != NULL);
1590    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1591     NRPathcode code = sp_node_path_code_from_side(node, side);
1593     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1595     if (show_handle) {
1596         if (!side->knot) { // No handle knot at all
1597             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1598             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1599             side->knot->pos = side->pos;
1600             if (side->knot->item)
1601                 SP_CTRL(side->knot->item)->moveto(side->pos);
1602             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1603             sp_knot_show(side->knot);
1604         } else {
1605             if (side->knot->pos != side->pos) { // only if it's really moved
1606                 if (fire_move_signals) {
1607                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1608                 } else {
1609                     sp_knot_moveto(side->knot, &side->pos);
1610                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1611                 }
1612             }
1613             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1614                 sp_knot_show(side->knot);
1615             }
1616         }
1617         sp_canvas_item_show(side->line);
1618     } else {
1619         if (side->knot) {
1620             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1621                 sp_knot_hide(side->knot);
1622             }
1623         }
1624         if (side->line) {
1625             sp_canvas_item_hide(side->line);
1626         }
1627     }
1630 /**
1631  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1632  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1633  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1634  * updated; otherwise, just move the knots silently (used in batch moves).
1635  */
1636 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1638     g_assert(node != NULL);
1640     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1641         sp_knot_show(node->knot);
1642     }
1644     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1645         if (fire_move_signals)
1646             sp_knot_set_position(node->knot, &node->pos, 0);
1647         else
1648             sp_knot_moveto(node->knot, &node->pos);
1649     }
1651     gboolean show_handles = node->selected;
1652     if (node->p.other != NULL) {
1653         if (node->p.other->selected) show_handles = TRUE;
1654     }
1655     if (node->n.other != NULL) {
1656         if (node->n.other->selected) show_handles = TRUE;
1657     }
1659     if (node->subpath->nodepath->show_handles == false)
1660         show_handles = FALSE;
1662     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1663     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1666 /**
1667  * Call sp_node_update_handles() for all nodes on subpath.
1668  */
1669 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1671     g_assert(subpath != NULL);
1673     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1674         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1675     }
1678 /**
1679  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1680  */
1681 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1683     g_assert(nodepath != NULL);
1685     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1686         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1687     }
1690 void
1691 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1693     if (nodepath == NULL) return;
1695     nodepath->show_handles = show;
1696     sp_nodepath_update_handles(nodepath);
1699 /**
1700  * Adds all selected nodes in nodepath to list.
1701  */
1702 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1704     StlConv<Node *>::list(l, selected);
1705 /// \todo this adds a copying, rework when the selection becomes a stl list
1708 /**
1709  * Align selected nodes on the specified axis.
1710  */
1711 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1713     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1714         return;
1715     }
1717     if ( !nodepath->selected->next ) { // only one node selected
1718         return;
1719     }
1720    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1721     NR::Point dest(pNode->pos);
1722     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1723         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1724         if (pNode) {
1725             dest[axis] = pNode->pos[axis];
1726             sp_node_moveto(pNode, dest);
1727         }
1728     }
1730     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1733 /// Helper struct.
1734 struct NodeSort
1736    Inkscape::NodePath::Node *_node;
1737     NR::Coord _coord;
1738     /// \todo use vectorof pointers instead of calling copy ctor
1739     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1740         _node(node), _coord(node->pos[axis])
1741     {}
1743 };
1745 static bool operator<(NodeSort const &a, NodeSort const &b)
1747     return (a._coord < b._coord);
1750 /**
1751  * Distribute selected nodes on the specified axis.
1752  */
1753 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1755     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1756         return;
1757     }
1759     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1760         return;
1761     }
1763    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1764     std::vector<NodeSort> sorted;
1765     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1766         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1767         if (pNode) {
1768             NodeSort n(pNode, axis);
1769             sorted.push_back(n);
1770             //dest[axis] = pNode->pos[axis];
1771             //sp_node_moveto(pNode, dest);
1772         }
1773     }
1774     std::sort(sorted.begin(), sorted.end());
1775     unsigned int len = sorted.size();
1776     //overall bboxes span
1777     float dist = (sorted.back()._coord -
1778                   sorted.front()._coord);
1779     //new distance between each bbox
1780     float step = (dist) / (len - 1);
1781     float pos = sorted.front()._coord;
1782     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1783           it < sorted.end();
1784           it ++ )
1785     {
1786         NR::Point dest((*it)._node->pos);
1787         dest[axis] = pos;
1788         sp_node_moveto((*it)._node, dest);
1789         pos += step;
1790     }
1792     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1796 /**
1797  * Call sp_nodepath_line_add_node() for all selected segments.
1798  */
1799 void
1800 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1802     if (!nodepath) {
1803         return;
1804     }
1806     GList *nl = NULL;
1808     int n_added = 0;
1810     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1811        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1812         g_assert(t->selected);
1813         if (t->p.other && t->p.other->selected) {
1814             nl = g_list_prepend(nl, t);
1815         }
1816     }
1818     while (nl) {
1819        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1820        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1821        sp_nodepath_node_select(n, TRUE, FALSE);
1822        n_added ++;
1823        nl = g_list_remove(nl, t);
1824     }
1826     /** \todo fixme: adjust ? */
1827     sp_nodepath_update_handles(nodepath);
1829     if (n_added > 1) {
1830         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1831     } else if (n_added > 0) {
1832         sp_nodepath_update_repr(nodepath, _("Add node"));
1833     }
1835     sp_nodepath_update_statusbar(nodepath);
1838 /**
1839  * Select segment nearest to point
1840  */
1841 void
1842 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1844     if (!nodepath) {
1845         return;
1846     }
1848     sp_nodepath_ensure_livarot_path(nodepath);
1849     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1850     if (!maybe_position) {
1851         return;
1852     }
1853     Path::cut_position position = *maybe_position;
1855     //find segment to segment
1856     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1858     //fixme: this can return NULL, so check before proceeding.
1859     g_return_if_fail(e != NULL);
1861     gboolean force = FALSE;
1862     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1863         force = TRUE;
1864     }
1865     sp_nodepath_node_select(e, (gboolean) toggle, force);
1866     if (e->p.other)
1867         sp_nodepath_node_select(e->p.other, TRUE, force);
1869     sp_nodepath_update_handles(nodepath);
1871     sp_nodepath_update_statusbar(nodepath);
1874 /**
1875  * Add a node nearest to point
1876  */
1877 void
1878 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1880     if (!nodepath) {
1881         return;
1882     }
1884     sp_nodepath_ensure_livarot_path(nodepath);
1885     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1886     if (!maybe_position) {
1887         return;
1888     }
1889     Path::cut_position position = *maybe_position;
1891     //find segment to split
1892     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1894     //don't know why but t seems to flip for lines
1895     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1896         position.t = 1.0 - position.t;
1897     }
1898     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1899     sp_nodepath_node_select(n, FALSE, TRUE);
1901     /* fixme: adjust ? */
1902     sp_nodepath_update_handles(nodepath);
1904     sp_nodepath_update_repr(nodepath, _("Add node"));
1906     sp_nodepath_update_statusbar(nodepath);
1909 /*
1910  * Adjusts a segment so that t moves by a certain delta for dragging
1911  * converts lines to curves
1912  *
1913  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1914  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1915  */
1916 void
1917 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1919     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1921     //fixme: e and e->p can be NULL, so check for those before proceeding
1922     g_return_if_fail(e != NULL);
1923     g_return_if_fail(&e->p != NULL);
1925     /* feel good is an arbitrary parameter that distributes the delta between handles
1926      * if t of the drag point is less than 1/6 distance form the endpoint only
1927      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1928      */
1929     double feel_good;
1930     if (t <= 1.0 / 6.0)
1931         feel_good = 0;
1932     else if (t <= 0.5)
1933         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1934     else if (t <= 5.0 / 6.0)
1935         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1936     else
1937         feel_good = 1;
1939     //if we're dragging a line convert it to a curve
1940     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1941         sp_nodepath_set_line_type(e, NR_CURVETO);
1942     }
1944     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1945     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1946     e->p.other->n.pos += offsetcoord0;
1947     e->p.pos += offsetcoord1;
1949     // adjust handles of adjacent nodes where necessary
1950     sp_node_adjust_handle(e,1);
1951     sp_node_adjust_handle(e->p.other,-1);
1953     sp_nodepath_update_handles(e->subpath->nodepath);
1955     update_object(e->subpath->nodepath);
1957     sp_nodepath_update_statusbar(e->subpath->nodepath);
1961 /**
1962  * Call sp_nodepath_break() for all selected segments.
1963  */
1964 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1966     if (!nodepath) return;
1968     GList *temp = NULL;
1969     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1970        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1971        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1972         if (nn == NULL) continue; // no break, no new node
1973         temp = g_list_prepend(temp, nn);
1974     }
1976     if (temp) {
1977         sp_nodepath_deselect(nodepath);
1978     }
1979     for (GList *l = temp; l != NULL; l = l->next) {
1980         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1981     }
1983     sp_nodepath_update_handles(nodepath);
1985     sp_nodepath_update_repr(nodepath, _("Break path"));
1988 /**
1989  * Duplicate the selected node(s).
1990  */
1991 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1993     if (!nodepath) {
1994         return;
1995     }
1997     GList *temp = NULL;
1998     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1999        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2000        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2001         if (nn == NULL) continue; // could not duplicate
2002         temp = g_list_prepend(temp, nn);
2003     }
2005     if (temp) {
2006         sp_nodepath_deselect(nodepath);
2007     }
2008     for (GList *l = temp; l != NULL; l = l->next) {
2009         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2010     }
2012     sp_nodepath_update_handles(nodepath);
2014     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2017 /**
2018  *  Internal function to join two nodes by merging them into one.
2019  */
2020 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2022     /* a and b are endpoints */
2024     // if one of the two nodes is mouseovered, fix its position
2025     NR::Point c;
2026     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2027         c = a->pos;
2028     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2029         c = b->pos;
2030     } else {
2031         // otherwise, move joined node to the midpoint
2032         c = (a->pos + b->pos) / 2;
2033     }
2035     if (a->subpath == b->subpath) {
2036        Inkscape::NodePath::SubPath *sp = a->subpath;
2037         sp_nodepath_subpath_close(sp);
2038         sp_node_moveto (sp->first, c);
2040         sp_nodepath_update_handles(sp->nodepath);
2041         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2042         return;
2043     }
2045     /* a and b are separate subpaths */
2046     Inkscape::NodePath::SubPath *sa = a->subpath;
2047     Inkscape::NodePath::SubPath *sb = b->subpath;
2048     NR::Point p;
2049     Inkscape::NodePath::Node *n;
2050     NRPathcode code;
2051     if (a == sa->first) {
2052         // we will now reverse sa, so that a is its last node, not first, and drop that node
2053         p = sa->first->n.pos;
2054         code = (NRPathcode)sa->first->n.other->code;
2055         // create new subpath
2056        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2057        // create a first moveto node on it
2058         n = sa->last;
2059         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2060         n = n->p.other;
2061         if (n == sa->first) n = NULL;
2062         while (n) {
2063             // copy the rest of the nodes from sa to t, going backwards
2064             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2065             n = n->p.other;
2066             if (n == sa->first) n = NULL;
2067         }
2068         // replace sa with t
2069         sp_nodepath_subpath_destroy(sa);
2070         sa = t;
2071     } else if (a == sa->last) {
2072         // a is already last, just drop it
2073         p = sa->last->p.pos;
2074         code = (NRPathcode)sa->last->code;
2075         sp_nodepath_node_destroy(sa->last);
2076     } else {
2077         code = NR_END;
2078         g_assert_not_reached();
2079     }
2081     if (b == sb->first) {
2082         // copy all nodes from b to a, forward 
2083         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2084         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2085             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2086         }
2087     } else if (b == sb->last) {
2088         // copy all nodes from b to a, backward 
2089         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2090         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2091             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2092         }
2093     } else {
2094         g_assert_not_reached();
2095     }
2096     /* and now destroy sb */
2098     sp_nodepath_subpath_destroy(sb);
2100     sp_nodepath_update_handles(sa->nodepath);
2102     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2104     sp_nodepath_update_statusbar(nodepath);
2107 /**
2108  *  Internal function to join two nodes by adding a segment between them.
2109  */
2110 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2112     if (a->subpath == b->subpath) {
2113        Inkscape::NodePath::SubPath *sp = a->subpath;
2115         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2116         sp->closed = TRUE;
2118         sp->first->p.other = sp->last;
2119         sp->last->n.other  = sp->first;
2121         sp_node_handle_mirror_p_to_n(sp->last);
2122         sp_node_handle_mirror_n_to_p(sp->first);
2124         sp->first->code = sp->last->code;
2125         sp->first       = sp->last;
2127         sp_nodepath_update_handles(sp->nodepath);
2129         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2131         return;
2132     }
2134     /* a and b are separate subpaths */
2135     Inkscape::NodePath::SubPath *sa = a->subpath;
2136     Inkscape::NodePath::SubPath *sb = b->subpath;
2138     Inkscape::NodePath::Node *n;
2139     NR::Point p;
2140     NRPathcode code;
2141     if (a == sa->first) {
2142         code = (NRPathcode) sa->first->n.other->code;
2143        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2144         n = sa->last;
2145         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2146         for (n = n->p.other; n != NULL; n = n->p.other) {
2147             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2148         }
2149         sp_nodepath_subpath_destroy(sa);
2150         sa = t;
2151     } else if (a == sa->last) {
2152         code = (NRPathcode)sa->last->code;
2153     } else {
2154         code = NR_END;
2155         g_assert_not_reached();
2156     }
2158     if (b == sb->first) {
2159         n = sb->first;
2160         sp_node_handle_mirror_p_to_n(sa->last);
2161         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2162         sp_node_handle_mirror_n_to_p(sa->last);
2163         for (n = n->n.other; n != NULL; n = n->n.other) {
2164             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2165         }
2166     } else if (b == sb->last) {
2167         n = sb->last;
2168         sp_node_handle_mirror_p_to_n(sa->last);
2169         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2170         sp_node_handle_mirror_n_to_p(sa->last);
2171         for (n = n->p.other; n != NULL; n = n->p.other) {
2172             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2173         }
2174     } else {
2175         g_assert_not_reached();
2176     }
2177     /* and now destroy sb */
2179     sp_nodepath_subpath_destroy(sb);
2181     sp_nodepath_update_handles(sa->nodepath);
2183     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2186 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2188 /**
2189  * Internal function to handle joining two nodes.
2190  */
2191 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2193     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2195     if (g_list_length(nodepath->selected) != 2) {
2196         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2197         return;
2198     }
2200     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2201     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2203     g_assert(a != b);
2204     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2205         // someone tried to join an orphan node (i.e. a single-node subpath).
2206         // this is not worth an error message, just fail silently.
2207         return;
2208     }
2210     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2211         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2212         return;
2213     }
2215     switch(mode) {
2216         case NODE_JOIN_ENDPOINTS:
2217             do_node_selected_join(nodepath, a, b);
2218             break;
2219         case NODE_JOIN_SEGMENT:
2220             do_node_selected_join_segment(nodepath, a, b);
2221             break;
2222     }
2225 /**
2226  *  Join two nodes by merging them into one.
2227  */
2228 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2230     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2233 /**
2234  *  Join two nodes by adding a segment between them.
2235  */
2236 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2238     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2241 /**
2242  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2243  */
2244 void sp_node_delete_preserve(GList *nodes_to_delete)
2246     GSList *nodepaths = NULL;
2248     while (nodes_to_delete) {
2249         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2250         Inkscape::NodePath::SubPath *sp = node->subpath;
2251         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2252         Inkscape::NodePath::Node *sample_cursor = NULL;
2253         Inkscape::NodePath::Node *sample_end = NULL;
2254         Inkscape::NodePath::Node *delete_cursor = node;
2255         bool just_delete = false;
2257         //find the start of this contiguous selection
2258         //move left to the first node that is not selected
2259         //or the start of the non-closed path
2260         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2261             delete_cursor = curr;
2262         }
2264         //just delete at the beginning of an open path
2265         if (!delete_cursor->p.other) {
2266             sample_cursor = delete_cursor;
2267             just_delete = true;
2268         } else {
2269             sample_cursor = delete_cursor->p.other;
2270         }
2272         //calculate points for each segment
2273         int rate = 5;
2274         float period = 1.0 / rate;
2275         std::vector<NR::Point> data;
2276         if (!just_delete) {
2277             data.push_back(sample_cursor->pos);
2278             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2279                 //just delete at the end of an open path
2280                 if (!sp->closed && curr == sp->last) {
2281                     just_delete = true;
2282                     break;
2283                 }
2285                 //sample points on the contiguous selected segment
2286                 NR::Point *bez;
2287                 bez = new NR::Point [4];
2288                 bez[0] = curr->pos;
2289                 bez[1] = curr->n.pos;
2290                 bez[2] = curr->n.other->p.pos;
2291                 bez[3] = curr->n.other->pos;
2292                 for (int i=1; i<rate; i++) {
2293                     gdouble t = i * period;
2294                     NR::Point p = bezier_pt(3, bez, t);
2295                     data.push_back(p);
2296                 }
2297                 data.push_back(curr->n.other->pos);
2299                 sample_end = curr->n.other;
2300                 //break if we've come full circle or hit the end of the selection
2301                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2302                     break;
2303                 }
2304             }
2305         }
2307         if (!just_delete) {
2308             //calculate the best fitting single segment and adjust the endpoints
2309             NR::Point *adata;
2310             adata = new NR::Point [data.size()];
2311             copy(data.begin(), data.end(), adata);
2313             NR::Point *bez;
2314             bez = new NR::Point [4];
2315             //would decreasing error create a better fitting approximation?
2316             gdouble error = 1.0;
2317             gint ret;
2318             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2320             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2321             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2322             //the resulting nodes behave as expected.
2323             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2324                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2325             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2326                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2328             //adjust endpoints
2329             sample_cursor->n.pos = bez[1];
2330             sample_end->p.pos = bez[2];
2331         }
2333         //destroy this contiguous selection
2334         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2335             Inkscape::NodePath::Node *temp = delete_cursor;
2336             if (delete_cursor->n.other == delete_cursor) {
2337                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2338                 delete_cursor = NULL;
2339             } else {
2340                 delete_cursor = delete_cursor->n.other;
2341             }
2342             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2343             sp_nodepath_node_destroy(temp);
2344         }
2346         sp_nodepath_update_handles(nodepath);
2348         if (!g_slist_find(nodepaths, nodepath))
2349             nodepaths = g_slist_prepend (nodepaths, nodepath);
2350     }
2352     for (GSList *i = nodepaths; i; i = i->next) {
2353         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2354         // different nodepaths will give us one undo event per nodepath
2355         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2357         // if the entire nodepath is removed, delete the selected object.
2358         if (nodepath->subpaths == NULL ||
2359             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2360             //at least 2
2361             sp_nodepath_get_node_count(nodepath) < 2) {
2362             SPDocument *document = sp_desktop_document (nodepath->desktop);
2363             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2364             //delete this nodepath's object, not the entire selection! (though at this time, this
2365             //does not matter)
2366             sp_selection_delete();
2367             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2368                               _("Delete nodes"));
2369         } else {
2370             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2371             sp_nodepath_update_statusbar(nodepath);
2372         }
2373     }
2375     g_slist_free (nodepaths);
2378 /**
2379  * Delete one or more selected nodes.
2380  */
2381 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2383     if (!nodepath) return;
2384     if (!nodepath->selected) return;
2386     /** \todo fixme: do it the right way */
2387     while (nodepath->selected) {
2388        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2389         sp_nodepath_node_destroy(node);
2390     }
2393     //clean up the nodepath (such as for trivial subpaths)
2394     sp_nodepath_cleanup(nodepath);
2396     sp_nodepath_update_handles(nodepath);
2398     // if the entire nodepath is removed, delete the selected object.
2399     if (nodepath->subpaths == NULL ||
2400         sp_nodepath_get_node_count(nodepath) < 2) {
2401         SPDocument *document = sp_desktop_document (nodepath->desktop);
2402         sp_selection_delete();
2403         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2404                           _("Delete nodes"));
2405         return;
2406     }
2408     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2410     sp_nodepath_update_statusbar(nodepath);
2413 /**
2414  * Delete one or more segments between two selected nodes.
2415  * This is the code for 'split'.
2416  */
2417 void
2418 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2420    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2421    Inkscape::NodePath::Node *curr, *next;     //Iterators
2423     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2425     if (g_list_length(nodepath->selected) != 2) {
2426         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2427                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2428         return;
2429     }
2431     //Selected nodes, not inclusive
2432    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2433    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2435     if ( ( a==b)                       ||  //same node
2436          (a->subpath  != b->subpath )  ||  //not the same path
2437          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2438          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2439     {
2440         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2441                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2442         return;
2443     }
2445     //###########################################
2446     //# BEGIN EDITS
2447     //###########################################
2448     //##################################
2449     //# CLOSED PATH
2450     //##################################
2451     if (a->subpath->closed) {
2454         gboolean reversed = FALSE;
2456         //Since we can go in a circle, we need to find the shorter distance.
2457         //  a->b or b->a
2458         start = end = NULL;
2459         int distance    = 0;
2460         int minDistance = 0;
2461         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2462             if (curr==b) {
2463                 //printf("a to b:%d\n", distance);
2464                 start = a;//go from a to b
2465                 end   = b;
2466                 minDistance = distance;
2467                 //printf("A to B :\n");
2468                 break;
2469             }
2470             distance++;
2471         }
2473         //try again, the other direction
2474         distance = 0;
2475         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2476             if (curr==a) {
2477                 //printf("b to a:%d\n", distance);
2478                 if (distance < minDistance) {
2479                     start    = b;  //we go from b to a
2480                     end      = a;
2481                     reversed = TRUE;
2482                     //printf("B to A\n");
2483                 }
2484                 break;
2485             }
2486             distance++;
2487         }
2490         //Copy everything from 'end' to 'start' to a new subpath
2491        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2492         for (curr=end ; curr ; curr=curr->n.other) {
2493             NRPathcode code = (NRPathcode) curr->code;
2494             if (curr == end)
2495                 code = NR_MOVETO;
2496             sp_nodepath_node_new(t, NULL,
2497                                  (Inkscape::NodePath::NodeType)curr->type, code,
2498                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2499             if (curr == start)
2500                 break;
2501         }
2502         sp_nodepath_subpath_destroy(a->subpath);
2505     }
2509     //##################################
2510     //# OPEN PATH
2511     //##################################
2512     else {
2514         //We need to get the direction of the list between A and B
2515         //Can we walk from a to b?
2516         start = end = NULL;
2517         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2518             if (curr==b) {
2519                 start = a;  //did it!  we go from a to b
2520                 end   = b;
2521                 //printf("A to B\n");
2522                 break;
2523             }
2524         }
2525         if (!start) {//didn't work?  let's try the other direction
2526             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2527                 if (curr==a) {
2528                     start = b;  //did it!  we go from b to a
2529                     end   = a;
2530                     //printf("B to A\n");
2531                     break;
2532                 }
2533             }
2534         }
2535         if (!start) {
2536             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2537                                                      _("Cannot find path between nodes."));
2538             return;
2539         }
2543         //Copy everything after 'end' to a new subpath
2544        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2545         for (curr=end ; curr ; curr=curr->n.other) {
2546             NRPathcode code = (NRPathcode) curr->code;
2547             if (curr == end)
2548                 code = NR_MOVETO;
2549             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2550                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2551         }
2553         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2554         for (curr = start->n.other ; curr  ; curr=next) {
2555             next = curr->n.other;
2556             sp_nodepath_node_destroy(curr);
2557         }
2559     }
2560     //###########################################
2561     //# END EDITS
2562     //###########################################
2564     //clean up the nodepath (such as for trivial subpaths)
2565     sp_nodepath_cleanup(nodepath);
2567     sp_nodepath_update_handles(nodepath);
2569     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2571     sp_nodepath_update_statusbar(nodepath);
2574 /**
2575  * Call sp_nodepath_set_line() for all selected segments.
2576  */
2577 void
2578 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2580     if (nodepath == NULL) return;
2582     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2583        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2584         g_assert(n->selected);
2585         if (n->p.other && n->p.other->selected) {
2586             sp_nodepath_set_line_type(n, code);
2587         }
2588     }
2590     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2593 /**
2594  * Call sp_nodepath_convert_node_type() for all selected nodes.
2595  */
2596 void
2597 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2599     if (nodepath == NULL) return;
2601     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2603     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2604         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2605     }
2607     sp_nodepath_update_repr(nodepath, _("Change node type"));
2610 /**
2611  * Change select status of node, update its own and neighbour handles.
2612  */
2613 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2615     node->selected = selected;
2617     if (selected) {
2618         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2619         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2620         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2621         sp_knot_update_ctrl(node->knot);
2622     } else {
2623         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2624         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2625         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2626         sp_knot_update_ctrl(node->knot);
2627     }
2629     sp_node_update_handles(node);
2630     if (node->n.other) sp_node_update_handles(node->n.other);
2631     if (node->p.other) sp_node_update_handles(node->p.other);
2634 /**
2635 \brief Select a node
2636 \param node     The node to select
2637 \param incremental   If true, add to selection, otherwise deselect others
2638 \param override   If true, always select this node, otherwise toggle selected status
2639 */
2640 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2642     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2644     if (incremental) {
2645         if (override) {
2646             if (!g_list_find(nodepath->selected, node)) {
2647                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2648             }
2649             sp_node_set_selected(node, TRUE);
2650         } else { // toggle
2651             if (node->selected) {
2652                 g_assert(g_list_find(nodepath->selected, node));
2653                 nodepath->selected = g_list_remove(nodepath->selected, node);
2654             } else {
2655                 g_assert(!g_list_find(nodepath->selected, node));
2656                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2657             }
2658             sp_node_set_selected(node, !node->selected);
2659         }
2660     } else {
2661         sp_nodepath_deselect(nodepath);
2662         nodepath->selected = g_list_prepend(nodepath->selected, node);
2663         sp_node_set_selected(node, TRUE);
2664     }
2666     sp_nodepath_update_statusbar(nodepath);
2670 /**
2671 \brief Deselect all nodes in the nodepath
2672 */
2673 void
2674 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2676     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2678     while (nodepath->selected) {
2679         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2680         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2681     }
2682     sp_nodepath_update_statusbar(nodepath);
2685 /**
2686 \brief Select or invert selection of all nodes in the nodepath
2687 */
2688 void
2689 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2691     if (!nodepath) return;
2693     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2694        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2695         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2696            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2697            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2698         }
2699     }
2702 /**
2703  * If nothing selected, does the same as sp_nodepath_select_all();
2704  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2705  * (i.e., similar to "select all in layer", with the "selected" subpaths
2706  * being treated as "layers" in the path).
2707  */
2708 void
2709 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2711     if (!nodepath) return;
2713     if (g_list_length (nodepath->selected) == 0) {
2714         sp_nodepath_select_all (nodepath, invert);
2715         return;
2716     }
2718     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2719     GSList *subpaths = NULL;
2721     for (GList *l = copy; l != NULL; l = l->next) {
2722         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2723         Inkscape::NodePath::SubPath *subpath = n->subpath;
2724         if (!g_slist_find (subpaths, subpath))
2725             subpaths = g_slist_prepend (subpaths, subpath);
2726     }
2728     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2729         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2730         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2731             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2732             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2733         }
2734     }
2736     g_slist_free (subpaths);
2737     g_list_free (copy);
2740 /**
2741  * \brief Select the node after the last selected; if none is selected,
2742  * select the first within path.
2743  */
2744 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2746     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2748    Inkscape::NodePath::Node *last = NULL;
2749     if (nodepath->selected) {
2750         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2751            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2752             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2753             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2754                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2755                 if (node->selected) {
2756                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2757                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2758                             if (spl->next) { // there's a next subpath
2759                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2760                                 last = subpath_next->first;
2761                             } else if (spl->prev) { // there's a previous subpath
2762                                 last = NULL; // to be set later to the first node of first subpath
2763                             } else {
2764                                 last = node->n.other;
2765                             }
2766                         } else {
2767                             last = node->n.other;
2768                         }
2769                     } else {
2770                         if (node->n.other) {
2771                             last = node->n.other;
2772                         } else {
2773                             if (spl->next) { // there's a next subpath
2774                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2775                                 last = subpath_next->first;
2776                             } else if (spl->prev) { // there's a previous subpath
2777                                 last = NULL; // to be set later to the first node of first subpath
2778                             } else {
2779                                 last = (Inkscape::NodePath::Node *) subpath->first;
2780                             }
2781                         }
2782                     }
2783                 }
2784             }
2785         }
2786         sp_nodepath_deselect(nodepath);
2787     }
2789     if (last) { // there's at least one more node after selected
2790         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2791     } else { // no more nodes, select the first one in first subpath
2792        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2793         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2794     }
2797 /**
2798  * \brief Select the node before the first selected; if none is selected,
2799  * select the last within path
2800  */
2801 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2803     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2805    Inkscape::NodePath::Node *last = NULL;
2806     if (nodepath->selected) {
2807         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2808            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2809             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2810                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2811                 if (node->selected) {
2812                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2813                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2814                             if (spl->prev) { // there's a prev subpath
2815                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2816                                 last = subpath_prev->last;
2817                             } else if (spl->next) { // there's a next subpath
2818                                 last = NULL; // to be set later to the last node of last subpath
2819                             } else {
2820                                 last = node->p.other;
2821                             }
2822                         } else {
2823                             last = node->p.other;
2824                         }
2825                     } else {
2826                         if (node->p.other) {
2827                             last = node->p.other;
2828                         } else {
2829                             if (spl->prev) { // there's a prev subpath
2830                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2831                                 last = subpath_prev->last;
2832                             } else if (spl->next) { // there's a next subpath
2833                                 last = NULL; // to be set later to the last node of last subpath
2834                             } else {
2835                                 last = (Inkscape::NodePath::Node *) subpath->last;
2836                             }
2837                         }
2838                     }
2839                 }
2840             }
2841         }
2842         sp_nodepath_deselect(nodepath);
2843     }
2845     if (last) { // there's at least one more node before selected
2846         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2847     } else { // no more nodes, select the last one in last subpath
2848         GList *spl = g_list_last(nodepath->subpaths);
2849        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2850         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2851     }
2854 /**
2855  * \brief Select all nodes that are within the rectangle.
2856  */
2857 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2859     if (!incremental) {
2860         sp_nodepath_deselect(nodepath);
2861     }
2863     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2864        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2865         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2866            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2868             if (b.contains(node->pos)) {
2869                 sp_nodepath_node_select(node, TRUE, TRUE);
2870             }
2871         }
2872     }
2876 void
2877 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2879     g_assert (n);
2880     g_assert (nodepath);
2881     g_assert (n->subpath->nodepath == nodepath);
2883     if (g_list_length (nodepath->selected) == 0) {
2884         if (grow > 0) {
2885             sp_nodepath_node_select(n, TRUE, TRUE);
2886         }
2887         return;
2888     }
2890     if (g_list_length (nodepath->selected) == 1) {
2891         if (grow < 0) {
2892             sp_nodepath_deselect (nodepath);
2893             return;
2894         }
2895     }
2897         double n_sel_range = 0, p_sel_range = 0;
2898             Inkscape::NodePath::Node *farthest_n_node = n;
2899             Inkscape::NodePath::Node *farthest_p_node = n;
2901         // Calculate ranges
2902         {
2903             double n_range = 0, p_range = 0;
2904             bool n_going = true, p_going = true;
2905             Inkscape::NodePath::Node *n_node = n;
2906             Inkscape::NodePath::Node *p_node = n;
2907             do {
2908                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2909                 if (n_node && n_going)
2910                     n_node = n_node->n.other;
2911                 if (n_node == NULL) {
2912                     n_going = false;
2913                 } else {
2914                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2915                     if (n_node->selected) {
2916                         n_sel_range = n_range;
2917                         farthest_n_node = n_node;
2918                     }
2919                     if (n_node == p_node) {
2920                         n_going = false;
2921                         p_going = false;
2922                     }
2923                 }
2924                 if (p_node && p_going)
2925                     p_node = p_node->p.other;
2926                 if (p_node == NULL) {
2927                     p_going = false;
2928                 } else {
2929                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2930                     if (p_node->selected) {
2931                         p_sel_range = p_range;
2932                         farthest_p_node = p_node;
2933                     }
2934                     if (p_node == n_node) {
2935                         n_going = false;
2936                         p_going = false;
2937                     }
2938                 }
2939             } while (n_going || p_going);
2940         }
2942     if (grow > 0) {
2943         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2944                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2945         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2946                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2947         }
2948     } else {
2949         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2950                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2951         } else if (farthest_p_node && farthest_p_node->selected) {
2952                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2953         }
2954     }
2957 void
2958 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2960     g_assert (n);
2961     g_assert (nodepath);
2962     g_assert (n->subpath->nodepath == nodepath);
2964     if (g_list_length (nodepath->selected) == 0) {
2965         if (grow > 0) {
2966             sp_nodepath_node_select(n, TRUE, TRUE);
2967         }
2968         return;
2969     }
2971     if (g_list_length (nodepath->selected) == 1) {
2972         if (grow < 0) {
2973             sp_nodepath_deselect (nodepath);
2974             return;
2975         }
2976     }
2978     Inkscape::NodePath::Node *farthest_selected = NULL;
2979     double farthest_dist = 0;
2981     Inkscape::NodePath::Node *closest_unselected = NULL;
2982     double closest_dist = NR_HUGE;
2984     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2985        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2986         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2987            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2988            if (node == n)
2989                continue;
2990            if (node->selected) {
2991                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2992                    farthest_dist = NR::L2(node->pos - n->pos);
2993                    farthest_selected = node;
2994                }
2995            } else {
2996                if (NR::L2(node->pos - n->pos) < closest_dist) {
2997                    closest_dist = NR::L2(node->pos - n->pos);
2998                    closest_unselected = node;
2999                }
3000            }
3001         }
3002     }
3004     if (grow > 0) {
3005         if (closest_unselected) {
3006             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3007         }
3008     } else {
3009         if (farthest_selected) {
3010             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3011         }
3012     }
3016 /**
3017 \brief  Saves all nodes' and handles' current positions in their origin members
3018 */
3019 void
3020 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3022     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3023        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3024         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3025            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3026            n->origin = n->pos;
3027            n->p.origin = n->p.pos;
3028            n->n.origin = n->n.pos;
3029         }
3030     }
3033 /**
3034 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3035 */
3036 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3038     if (!nodepath->selected) {
3039         return NULL;
3040     }
3042     GList *r = NULL;
3043     guint i = 0;
3044     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3045        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3046         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3047            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3048             i++;
3049             if (node->selected) {
3050                 r = g_list_append(r, GINT_TO_POINTER(i));
3051             }
3052         }
3053     }
3054     return r;
3057 /**
3058 \brief  Restores selection by selecting nodes whose positions are in the list
3059 */
3060 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3062     sp_nodepath_deselect(nodepath);
3064     guint i = 0;
3065     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3066        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3067         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3068            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3069             i++;
3070             if (g_list_find(r, GINT_TO_POINTER(i))) {
3071                 sp_nodepath_node_select(node, TRUE, TRUE);
3072             }
3073         }
3074     }
3078 /**
3079 \brief Adjusts handle according to node type and line code.
3080 */
3081 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3083     g_assert(node);
3085    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3086    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3088    // nothing to do if we are an end node
3089     if (me->other == NULL) return;
3090     if (other->other == NULL) return;
3092     // nothing to do if we are a cusp node
3093     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3095     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3096     NRPathcode mecode;
3097     if (which_adjust == 1) {
3098         mecode = (NRPathcode)me->other->code;
3099     } else {
3100         mecode = (NRPathcode)node->code;
3101     }
3102     if (mecode == NR_LINETO) return;
3104     if (sp_node_side_is_line(node, other)) {
3105         // other is a line, and we are either smooth or symm
3106        Inkscape::NodePath::Node *othernode = other->other;
3107         double len = NR::L2(me->pos - node->pos);
3108         NR::Point delta = node->pos - othernode->pos;
3109         double linelen = NR::L2(delta);
3110         if (linelen < 1e-18)
3111             return;
3112         me->pos = node->pos + (len / linelen)*delta;
3113         return;
3114     }
3116     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3117         // symmetrize 
3118         me->pos = 2 * node->pos - other->pos;
3119         return;
3120     } else {
3121         // smoothify
3122         double len = NR::L2(me->pos - node->pos);
3123         NR::Point delta = other->pos - node->pos;
3124         double otherlen = NR::L2(delta);
3125         if (otherlen < 1e-18) return;
3126         me->pos = node->pos - (len / otherlen) * delta;
3127     }
3130 /**
3131  \brief Adjusts both handles according to node type and line code
3132  */
3133 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3135     g_assert(node);
3137     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3139     /* we are either smooth or symm */
3141     if (node->p.other == NULL) return;
3142     if (node->n.other == NULL) return;
3144     if (sp_node_side_is_line(node, &node->p)) {
3145         sp_node_adjust_handle(node, 1);
3146         return;
3147     }
3149     if (sp_node_side_is_line(node, &node->n)) {
3150         sp_node_adjust_handle(node, -1);
3151         return;
3152     }
3154     /* both are curves */
3155     NR::Point const delta( node->n.pos - node->p.pos );
3157     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3158         node->p.pos = node->pos - delta / 2;
3159         node->n.pos = node->pos + delta / 2;
3160         return;
3161     }
3163     /* We are smooth */
3164     double plen = NR::L2(node->p.pos - node->pos);
3165     if (plen < 1e-18) return;
3166     double nlen = NR::L2(node->n.pos - node->pos);
3167     if (nlen < 1e-18) return;
3168     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3169     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3172 /**
3173  * Node event callback.
3174  */
3175 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3177     gboolean ret = FALSE;
3178     switch (event->type) {
3179         case GDK_ENTER_NOTIFY:
3180             Inkscape::NodePath::Path::active_node = n;
3181             break;
3182         case GDK_LEAVE_NOTIFY:
3183             Inkscape::NodePath::Path::active_node = NULL;
3184             break;
3185         case GDK_SCROLL:
3186             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3187                 switch (event->scroll.direction) {
3188                     case GDK_SCROLL_UP:
3189                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3190                         break;
3191                     case GDK_SCROLL_DOWN:
3192                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3193                         break;
3194                     default:
3195                         break;
3196                 }
3197                 ret = TRUE;
3198             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3199                 switch (event->scroll.direction) {
3200                     case GDK_SCROLL_UP:
3201                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3202                         break;
3203                     case GDK_SCROLL_DOWN:
3204                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3205                         break;
3206                     default:
3207                         break;
3208                 }
3209                 ret = TRUE;
3210             }
3211             break;
3212         case GDK_KEY_PRESS:
3213             switch (get_group0_keyval (&event->key)) {
3214                 case GDK_space:
3215                     if (event->key.state & GDK_BUTTON1_MASK) {
3216                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3217                         stamp_repr(nodepath);
3218                         ret = TRUE;
3219                     }
3220                     break;
3221                 case GDK_Page_Up:
3222                     if (event->key.state & GDK_CONTROL_MASK) {
3223                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3224                     } else {
3225                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3226                     }
3227                     break;
3228                 case GDK_Page_Down:
3229                     if (event->key.state & GDK_CONTROL_MASK) {
3230                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3231                     } else {
3232                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3233                     }
3234                     break;
3235                 default:
3236                     break;
3237             }
3238             break;
3239         default:
3240             break;
3241     }
3243     return ret;
3246 /**
3247  * Handle keypress on node; directly called.
3248  */
3249 gboolean node_key(GdkEvent *event)
3251     Inkscape::NodePath::Path *np;
3253     // there is no way to verify nodes so set active_node to nil when deleting!!
3254     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3256     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3257         gint ret = FALSE;
3258         switch (get_group0_keyval (&event->key)) {
3259             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3260             case GDK_BackSpace:
3261                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3262                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3263                 sp_nodepath_update_repr(np, _("Delete node"));
3264                 Inkscape::NodePath::Path::active_node = NULL;
3265                 ret = TRUE;
3266                 break;
3267             case GDK_c:
3268                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3269                 ret = TRUE;
3270                 break;
3271             case GDK_s:
3272                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3273                 ret = TRUE;
3274                 break;
3275             case GDK_y:
3276                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3277                 ret = TRUE;
3278                 break;
3279             case GDK_b:
3280                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3281                 ret = TRUE;
3282                 break;
3283         }
3284         return ret;
3285     }
3286     return FALSE;
3289 /**
3290  * Mouseclick on node callback.
3291  */
3292 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3294    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3296     if (state & GDK_CONTROL_MASK) {
3297         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3299         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3300             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3301                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3302             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3303                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3304             } else {
3305                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3306             }
3307             sp_nodepath_update_repr(nodepath, _("Change node type"));
3308             sp_nodepath_update_statusbar(nodepath);
3310         } else { //ctrl+alt+click: delete node
3311             GList *node_to_delete = NULL;
3312             node_to_delete = g_list_append(node_to_delete, n);
3313             sp_node_delete_preserve(node_to_delete);
3314         }
3316     } else {
3317         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3318     }
3321 /**
3322  * Mouse grabbed node callback.
3323  */
3324 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3326    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3328     if (!n->selected) {
3329         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3330     }
3332     n->is_dragging = true;
3333     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3335     sp_nodepath_remember_origins (n->subpath->nodepath);
3338 /**
3339  * Mouse ungrabbed node callback.
3340  */
3341 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3343    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3345    n->dragging_out = NULL;
3346    n->is_dragging = false;
3347    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3349    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3352 /**
3353  * The point on a line, given by its angle, closest to the given point.
3354  * \param p  A point.
3355  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3356  * \param closest  Pointer to the point struct where the result is stored.
3357  * \todo FIXME: use dot product perhaps?
3358  */
3359 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3361     if (a == HUGE_VAL) { // vertical
3362         *closest = NR::Point(0, (*p)[NR::Y]);
3363     } else {
3364         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3365         (*closest)[NR::Y] = a * (*closest)[NR::X];
3366     }
3369 /**
3370  * Distance from the point to a line given by its angle.
3371  * \param p  A point.
3372  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3373  */
3374 static double point_line_distance(NR::Point *p, double a)
3376     NR::Point c;
3377     point_line_closest(p, a, &c);
3378     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]));
3381 /**
3382  * Callback for node "request" signal.
3383  * \todo fixme: This goes to "moved" event? (lauris)
3384  */
3385 static gboolean
3386 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3388     double yn, xn, yp, xp;
3389     double an, ap, na, pa;
3390     double d_an, d_ap, d_na, d_pa;
3391     gboolean collinear = FALSE;
3392     NR::Point c;
3393     NR::Point pr;
3395    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3397     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3399    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3400     if ( (!n->subpath->nodepath->straight_path) &&
3401          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3402            || n->dragging_out ) )
3403     {
3404        NR::Point mouse = (*p);
3406        if (!n->dragging_out) {
3407            // This is the first drag-out event; find out which handle to drag out
3408            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3409            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3411            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3412                return FALSE;
3414            Inkscape::NodePath::NodeSide *opposite;
3415            if (appr_p > appr_n) { // closer to p
3416                n->dragging_out = &n->p;
3417                opposite = &n->n;
3418                n->code = NR_CURVETO;
3419            } else if (appr_p < appr_n) { // closer to n
3420                n->dragging_out = &n->n;
3421                opposite = &n->p;
3422                n->n.other->code = NR_CURVETO;
3423            } else { // p and n nodes are the same
3424                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3425                    n->dragging_out = &n->p;
3426                    opposite = &n->n;
3427                    n->code = NR_CURVETO;
3428                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3429                    n->dragging_out = &n->n;
3430                    opposite = &n->p;
3431                    n->n.other->code = NR_CURVETO;
3432                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3433                    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);
3434                    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);
3435                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3436                        n->dragging_out = &n->n;
3437                        opposite = &n->p;
3438                        n->n.other->code = NR_CURVETO;
3439                    } else { // closer to other's n handle
3440                        n->dragging_out = &n->p;
3441                        opposite = &n->n;
3442                        n->code = NR_CURVETO;
3443                    }
3444                }
3445            }
3447            // if there's another handle, make sure the one we drag out starts parallel to it
3448            if (opposite->pos != n->pos) {
3449                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3450            }
3452            // knots might not be created yet!
3453            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3454            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3455        }
3457        // pass this on to the handle-moved callback
3458        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3459        sp_node_update_handles(n);
3460        return TRUE;
3461    }
3463     if (state & GDK_CONTROL_MASK) { // constrained motion
3465         // calculate relative distances of handles
3466         // n handle:
3467         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3468         xn = n->n.pos[NR::X] - n->pos[NR::X];
3469         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3470         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3471             if (n->n.other) { // if there is the next point
3472                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3473                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3474                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3475             }
3476         }
3477         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3478         if (yn < 0) { xn = -xn; yn = -yn; }
3480         // p handle:
3481         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3482         xp = n->p.pos[NR::X] - n->pos[NR::X];
3483         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3484         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3485             if (n->p.other) {
3486                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3487                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3488                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3489             }
3490         }
3491         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3492         if (yp < 0) { xp = -xp; yp = -yp; }
3494         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3495             // sliding on handles, only if at least one of the handles is non-vertical
3496             // (otherwise it's the same as ctrl+drag anyway)
3498             // calculate angles of the handles
3499             if (xn == 0) {
3500                 if (yn == 0) { // no handle, consider it the continuation of the other one
3501                     an = 0;
3502                     collinear = TRUE;
3503                 }
3504                 else an = 0; // vertical; set the angle to horizontal
3505             } else an = yn/xn;
3507             if (xp == 0) {
3508                 if (yp == 0) { // no handle, consider it the continuation of the other one
3509                     ap = an;
3510                 }
3511                 else ap = 0; // vertical; set the angle to horizontal
3512             } else  ap = yp/xp;
3514             if (collinear) an = ap;
3516             // angles of the perpendiculars; HUGE_VAL means vertical
3517             if (an == 0) na = HUGE_VAL; else na = -1/an;
3518             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3520             // mouse point relative to the node's original pos
3521             pr = (*p) - n->origin;
3523             // distances to the four lines (two handles and two perpendiculars)
3524             d_an = point_line_distance(&pr, an);
3525             d_na = point_line_distance(&pr, na);
3526             d_ap = point_line_distance(&pr, ap);
3527             d_pa = point_line_distance(&pr, pa);
3529             // find out which line is the closest, save its closest point in c
3530             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3531                 point_line_closest(&pr, an, &c);
3532             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3533                 point_line_closest(&pr, ap, &c);
3534             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3535                 point_line_closest(&pr, na, &c);
3536             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3537                 point_line_closest(&pr, pa, &c);
3538             }
3540             // move the node to the closest point
3541             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3542                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3543                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3545         } else {  // constraining to hor/vert
3547             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3548                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3549             } else { // snap to vert
3550                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3551             }
3552         }
3553     } else { // move freely
3554         if (n->is_dragging) {
3555             if (state & GDK_MOD1_MASK) { // sculpt
3556                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3557             } else {
3558                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3559                                             (*p)[NR::X] - n->pos[NR::X],
3560                                             (*p)[NR::Y] - n->pos[NR::Y],
3561                                             (state & GDK_SHIFT_MASK) == 0);
3562             }
3563         }
3564     }
3566     n->subpath->nodepath->desktop->scroll_to_point(p);
3568     return TRUE;
3571 /**
3572  * Node handle clicked callback.
3573  */
3574 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3576    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3578     if (state & GDK_CONTROL_MASK) { // "delete" handle
3579         if (n->p.knot == knot) {
3580             n->p.pos = n->pos;
3581         } else if (n->n.knot == knot) {
3582             n->n.pos = n->pos;
3583         }
3584         sp_node_update_handles(n);
3585         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3586         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3587         sp_nodepath_update_statusbar(nodepath);
3589     } else { // just select or add to selection, depending in Shift
3590         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3591     }
3594 /**
3595  * Node handle grabbed callback.
3596  */
3597 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3599    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3601     if (!n->selected) {
3602         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3603     }
3605     // remember the origin point of the handle
3606     if (n->p.knot == knot) {
3607         n->p.origin_radial = n->p.pos - n->pos;
3608     } else if (n->n.knot == knot) {
3609         n->n.origin_radial = n->n.pos - n->pos;
3610     } else {
3611         g_assert_not_reached();
3612     }
3614     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3617 /**
3618  * Node handle ungrabbed callback.
3619  */
3620 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3622    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3624     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3625     if (n->p.knot == knot) {
3626         n->p.origin_radial.a = 0;
3627         sp_knot_set_position(knot, &n->p.pos, state);
3628     } else if (n->n.knot == knot) {
3629         n->n.origin_radial.a = 0;
3630         sp_knot_set_position(knot, &n->n.pos, state);
3631     } else {
3632         g_assert_not_reached();
3633     }
3635     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3638 /**
3639  * Node handle "request" signal callback.
3640  */
3641 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3643     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3645     Inkscape::NodePath::NodeSide *me, *opposite;
3646     gint which;
3647     if (n->p.knot == knot) {
3648         me = &n->p;
3649         opposite = &n->n;
3650         which = -1;
3651     } else if (n->n.knot == knot) {
3652         me = &n->n;
3653         opposite = &n->p;
3654         which = 1;
3655     } else {
3656         me = opposite = NULL;
3657         which = 0;
3658         g_assert_not_reached();
3659     }
3661     SPDesktop *desktop = n->subpath->nodepath->desktop;
3662     SnapManager &m = desktop->namedview->snap_manager;
3663     m.setup(desktop, n->subpath->nodepath->item);
3664     Inkscape::SnappedPoint s ;
3666     Inkscape::NodePath::Node *othernode = opposite->other;
3667     if (othernode) {
3668         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3669             /* We are smooth node adjacent with line */
3670             NR::Point const delta = *p - n->pos;
3671             NR::Coord const len = NR::L2(delta);
3672             Inkscape::NodePath::Node *othernode = opposite->other;
3673             NR::Point const ndelta = n->pos - othernode->pos;
3674             NR::Coord const linelen = NR::L2(ndelta);
3675             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3676                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3677                 (*p) = n->pos + (scal / linelen) * ndelta;
3678             }
3679             s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta));
3680         } else {
3681             s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
3682         }
3683     } else {
3684         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p);
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 = curve_new->copy();
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 = SPCurve::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 = np->curve->copy();
4719         helper_curve->transform(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         helper_curve->unref();
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 :