Code

fix new deletion behavior when deleting up to next-to-last node in subpath
[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/curve.h"
19 #include "display/sp-ctrlline.h"
20 #include "display/sodipodi-ctrl.h"
21 #include <glibmm/i18n.h>
22 #include "libnr/n-art-bpath.h"
23 #include "helper/units.h"
24 #include "knot.h"
25 #include "inkscape.h"
26 #include "document.h"
27 #include "sp-namedview.h"
28 #include "desktop.h"
29 #include "desktop-handles.h"
30 #include "snap.h"
31 #include "message-stack.h"
32 #include "message-context.h"
33 #include "node-context.h"
34 #include "selection-chemistry.h"
35 #include "selection.h"
36 #include "xml/repr.h"
37 #include "prefs-utils.h"
38 #include "sp-metrics.h"
39 #include "sp-path.h"
40 #include "libnr/nr-matrix-ops.h"
41 #include "splivarot.h"
42 #include "svg/svg.h"
43 #include "verbs.h"
44 #include "display/bezier-utils.h"
45 #include <vector>
46 #include <algorithm>
48 class NR::Matrix;
50 /// \todo
51 /// evil evil evil. FIXME: conflict of two different Path classes!
52 /// There is a conflict in the namespace between two classes named Path.
53 /// #include "sp-flowtext.h"
54 /// #include "sp-flowregion.h"
56 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
57 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
58 GType sp_flowregion_get_type (void);
59 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
60 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
61 GType sp_flowtext_get_type (void);
62 // end evil workaround
64 #include "helper/stlport.h"
67 /// \todo fixme: Implement these via preferences */
69 #define NODE_FILL          0xbfbfbf00
70 #define NODE_STROKE        0x000000ff
71 #define NODE_FILL_HI       0xff000000
72 #define NODE_STROKE_HI     0x000000ff
73 #define NODE_FILL_SEL      0x0000ffff
74 #define NODE_STROKE_SEL    0x000000ff
75 #define NODE_FILL_SEL_HI   0xff000000
76 #define NODE_STROKE_SEL_HI 0x000000ff
77 #define KNOT_FILL          0xffffffff
78 #define KNOT_STROKE        0x000000ff
79 #define KNOT_FILL_HI       0xff000000
80 #define KNOT_STROKE_HI     0x000000ff
82 static GMemChunk *nodechunk = NULL;
84 /* Creation from object */
86 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
87 static gchar *parse_nodetypes(gchar const *types, gint length);
89 /* Object updating */
91 static void stamp_repr(Inkscape::NodePath::Path *np);
92 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
93 static gchar *create_typestr(Inkscape::NodePath::Path *np);
95 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
97 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
99 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
101 /* Adjust handle placement, if the node or the other handle is moved */
102 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
103 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
105 /* Node event callbacks */
106 static void node_clicked(SPKnot *knot, guint state, gpointer data);
107 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
108 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
109 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
111 /* Handle event callbacks */
112 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
113 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
114 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
115 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
116 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
117 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
119 /* Constructors and destructors */
121 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
122 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
123 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
124 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
125 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
126                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
127 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
129 /* Helpers */
131 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
132 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
133 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
135 // active_node indicates mouseover node
136 static Inkscape::NodePath::Node *active_node = NULL;
138 /**
139  * \brief Creates new nodepath from item
140  */
141 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool show_handles)
143     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
145     /** \todo
146      * FIXME: remove this. We don't want to edit paths inside flowtext.
147      * Instead we will build our flowtext with cloned paths, so that the
148      * real paths are outside the flowtext and thus editable as usual.
149      */
150     if (SP_IS_FLOWTEXT(item)) {
151         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
152             if SP_IS_FLOWREGION(child) {
153                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
154                 if (grandchild && SP_IS_PATH(grandchild)) {
155                     item = SP_ITEM(grandchild);
156                     break;
157                 }
158             }
159         }
160     }
162     if (!SP_IS_PATH(item))
163         return NULL;
164     SPPath *path = SP_PATH(item);
165     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
166     if (curve == NULL)
167         return NULL;
169     NArtBpath *bpath = sp_curve_first_bpath(curve);
170     gint length = curve->end;
171     if (length == 0)
172         return NULL; // prevent crash for one-node paths
174     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
175     gchar *typestr = parse_nodetypes(nodetypes, length);
177     //Create new nodepath
178     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
179     if (!np)
180         return NULL;
182     // Set defaults
183     np->desktop     = desktop;
184     np->path        = path;
185     np->subpaths    = NULL;
186     np->selected    = NULL;
187     np->nodeContext = NULL; //Let the context that makes this set it
188     np->livarot_path = NULL;
189     np->local_change = 0;
190     np->show_handles = show_handles;
192     // we need to update item's transform from the repr here,
193     // because they may be out of sync when we respond
194     // to a change in repr by regenerating nodepath     --bb
195     sp_object_read_attr(SP_OBJECT(item), "transform");
197     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
198     np->d2i  = np->i2d.inverse();
199     np->repr = repr;
201     // create the subpath(s) from the bpath
202     NArtBpath *b = bpath;
203     while (b->code != NR_END) {
204         b = subpath_from_bpath(np, b, typestr + (b - bpath));
205     }
207     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
208     np->subpaths = g_list_reverse(np->subpaths);
210     g_free(typestr);
211     sp_curve_unref(curve);
213     // create the livarot representation from the same item
214     np->livarot_path = Path_for_item(item, true, true);
215     if (np->livarot_path)
216         np->livarot_path->ConvertWithBackData(0.01);
218     return np;
221 /**
222  * Destroys nodepath's subpaths, then itself, also tell context about it.
223  */
224 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
226     if (!np)  //soft fail, like delete
227         return;
229     while (np->subpaths) {
230         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
231     }
233     //Inform the context that made me, if any, that I am gone.
234     if (np->nodeContext)
235         np->nodeContext->nodepath = NULL;
237     g_assert(!np->selected);
239     if (np->livarot_path) {
240         delete np->livarot_path;
241         np->livarot_path = NULL;
242     }
244     np->desktop = NULL;
246     g_free(np);
250 /**
251  *  Return the node count of a given NodeSubPath.
252  */
253 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
255     if (!subpath)
256         return 0;
257     gint nodeCount = g_list_length(subpath->nodes);
258     return nodeCount;
261 /**
262  *  Return the node count of a given NodePath.
263  */
264 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
266     if (!np)
267         return 0;
268     gint nodeCount = 0;
269     for (GList *item = np->subpaths ; item ; item=item->next) {
270        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
271         nodeCount += g_list_length(subpath->nodes);
272     }
273     return nodeCount;
276 /**
277  *  Return the subpath count of a given NodePath.
278  */
279 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
281     if (!np)
282         return 0;
283     return g_list_length (np->subpaths);
286 /**
287  *  Return the selected node count of a given NodePath.
288  */
289 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
291     if (!np)
292         return 0;
293     return g_list_length (np->selected);
296 /**
297  *  Return the number of subpaths where nodes are selected in a given NodePath.
298  */
299 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
301     if (!np)
302         return 0;
303     if (!np->selected)
304         return 0;
305     if (!np->selected->next)
306         return 1;
307     gint count = 0;
308     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
309         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
310         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
311             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
312             if (node->selected) {
313                 count ++;
314                 break;
315             }
316         }
317     }
318     return count;
320  
321 /**
322  * Clean up a nodepath after editing.
323  *
324  * Currently we are deleting trivial subpaths.
325  */
326 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
328     GList *badSubPaths = NULL;
330     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
331     for (GList *l = nodepath->subpaths; l ; l=l->next) {
332        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
333        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
334             badSubPaths = g_list_append(badSubPaths, sp);
335     }
337     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
338     //also removes the subpath from nodepath->subpaths
339     for (GList *l = badSubPaths; l ; l=l->next) {
340        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
341         sp_nodepath_subpath_destroy(sp);
342     }
344     g_list_free(badSubPaths);
347 /**
348  * Create new nodepath from b, make it subpath of np.
349  * \param t The node type.
350  * \todo Fixme: t should be a proper type, rather than gchar
351  */
352 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
354     NR::Point ppos, pos, npos;
356     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
358     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
359     bool const closed = (b->code == NR_MOVETO);
361     pos = NR::Point(b->x3, b->y3) * np->i2d;
362     if (b[1].code == NR_CURVETO) {
363         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
364     } else {
365         npos = pos;
366     }
367     Inkscape::NodePath::Node *n;
368     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
369     g_assert(sp->first == n);
370     g_assert(sp->last  == n);
372     b++;
373     t++;
374     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
375         pos = NR::Point(b->x3, b->y3) * np->i2d;
376         if (b->code == NR_CURVETO) {
377             ppos = NR::Point(b->x2, b->y2) * np->i2d;
378         } else {
379             ppos = pos;
380         }
381         if (b[1].code == NR_CURVETO) {
382             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
383         } else {
384             npos = pos;
385         }
386         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
387         b++;
388         t++;
389     }
391     if (closed) sp_nodepath_subpath_close(sp);
393     return b;
396 /**
397  * Convert from sodipodi:nodetypes to new style type string.
398  */
399 static gchar *parse_nodetypes(gchar const *types, gint length)
401     g_assert(length > 0);
403     gchar *typestr = g_new(gchar, length + 1);
405     gint pos = 0;
407     if (types) {
408         for (gint i = 0; types[i] && ( i < length ); i++) {
409             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
410             if (types[i] != '\0') {
411                 switch (types[i]) {
412                     case 's':
413                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
414                         break;
415                     case 'z':
416                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
417                         break;
418                     case 'c':
419                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
420                         break;
421                     default:
422                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
423                         break;
424                 }
425             }
426         }
427     }
429     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
431     return typestr;
434 /**
435  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
436  * updated but repr is not (for speed). Used during curve and node drag.
437  */
438 static void update_object(Inkscape::NodePath::Path *np)
440     g_assert(np);
442     SPCurve *curve = create_curve(np);
444     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
446     sp_curve_unref(curve);
449 /**
450  * Update XML path node with data from path object.
451  */
452 static void update_repr_internal(Inkscape::NodePath::Path *np)
454     g_assert(np);
456     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
458     SPCurve *curve = create_curve(np);
459     gchar *typestr = create_typestr(np);
460     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
462     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
463         np->local_change++;
464         repr->setAttribute("d", svgpath);
465     }
467     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
468         np->local_change++;
469         repr->setAttribute("sodipodi:nodetypes", typestr);
470     }
472     g_free(svgpath);
473     g_free(typestr);
474     sp_curve_unref(curve);
477 /**
478  * Update XML path node with data from path object, commit changes forever.
479  */
480 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
482     update_repr_internal(np);
483     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
484                      annotation);
486     if (np->livarot_path) {
487         delete np->livarot_path;
488         np->livarot_path = NULL;
489     }
491     if (np->path && SP_IS_ITEM(np->path)) {
492         np->livarot_path = Path_for_item (np->path, true, true);
493         if (np->livarot_path)
494             np->livarot_path->ConvertWithBackData(0.01);
495     }
498 /**
499  * Update XML path node with data from path object, commit changes with undo.
500  */
501 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
503     update_repr_internal(np);
504     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
505                            annotation);
507     if (np->livarot_path) {
508         delete np->livarot_path;
509         np->livarot_path = NULL;
510     }
512     if (np->path && SP_IS_ITEM(np->path)) {
513         np->livarot_path = Path_for_item (np->path, true, true);
514         if (np->livarot_path)
515             np->livarot_path->ConvertWithBackData(0.01);
516     }
519 /**
520  * Make duplicate of path, replace corresponding XML node in tree, commit.
521  */
522 static void stamp_repr(Inkscape::NodePath::Path *np)
524     g_assert(np);
526     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
527     Inkscape::XML::Node *new_repr = old_repr->duplicate();
529     // remember the position of the item
530     gint pos = old_repr->position();
531     // remember parent
532     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
534     SPCurve *curve = create_curve(np);
535     gchar *typestr = create_typestr(np);
537     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
539     new_repr->setAttribute("d", svgpath);
540     new_repr->setAttribute("sodipodi:nodetypes", typestr);
542     // add the new repr to the parent
543     parent->appendChild(new_repr);
544     // move to the saved position
545     new_repr->setPosition(pos > 0 ? pos : 0);
547     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
548                      _("Stamp"));
550     Inkscape::GC::release(new_repr);
551     g_free(svgpath);
552     g_free(typestr);
553     sp_curve_unref(curve);
556 /**
557  * Create curve from path.
558  */
559 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
561     SPCurve *curve = sp_curve_new();
563     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
564        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
565         sp_curve_moveto(curve,
566                         sp->first->pos * np->d2i);
567        Inkscape::NodePath::Node *n = sp->first->n.other;
568         while (n) {
569             NR::Point const end_pt = n->pos * np->d2i;
570             switch (n->code) {
571                 case NR_LINETO:
572                     sp_curve_lineto(curve, end_pt);
573                     break;
574                 case NR_CURVETO:
575                     sp_curve_curveto(curve,
576                                      n->p.other->n.pos * np->d2i,
577                                      n->p.pos * np->d2i,
578                                      end_pt);
579                     break;
580                 default:
581                     g_assert_not_reached();
582                     break;
583             }
584             if (n != sp->last) {
585                 n = n->n.other;
586             } else {
587                 n = NULL;
588             }
589         }
590         if (sp->closed) {
591             sp_curve_closepath(curve);
592         }
593     }
595     return curve;
598 /**
599  * Convert path type string to sodipodi:nodetypes style.
600  */
601 static gchar *create_typestr(Inkscape::NodePath::Path *np)
603     gchar *typestr = g_new(gchar, 32);
604     gint len = 32;
605     gint pos = 0;
607     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
608        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
610         if (pos >= len) {
611             typestr = g_renew(gchar, typestr, len + 32);
612             len += 32;
613         }
615         typestr[pos++] = 'c';
617        Inkscape::NodePath::Node *n;
618         n = sp->first->n.other;
619         while (n) {
620             gchar code;
622             switch (n->type) {
623                 case Inkscape::NodePath::NODE_CUSP:
624                     code = 'c';
625                     break;
626                 case Inkscape::NodePath::NODE_SMOOTH:
627                     code = 's';
628                     break;
629                 case Inkscape::NodePath::NODE_SYMM:
630                     code = 'z';
631                     break;
632                 default:
633                     g_assert_not_reached();
634                     code = '\0';
635                     break;
636             }
638             if (pos >= len) {
639                 typestr = g_renew(gchar, typestr, len + 32);
640                 len += 32;
641             }
643             typestr[pos++] = code;
645             if (n != sp->last) {
646                 n = n->n.other;
647             } else {
648                 n = NULL;
649             }
650         }
651     }
653     if (pos >= len) {
654         typestr = g_renew(gchar, typestr, len + 1);
655         len += 1;
656     }
658     typestr[pos++] = '\0';
660     return typestr;
663 /**
664  * Returns current path in context.
665  */
666 static Inkscape::NodePath::Path *sp_nodepath_current()
668     if (!SP_ACTIVE_DESKTOP) {
669         return NULL;
670     }
672     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
674     if (!SP_IS_NODE_CONTEXT(event_context)) {
675         return NULL;
676     }
678     return SP_NODE_CONTEXT(event_context)->nodepath;
683 /**
684  \brief Fills node and handle positions for three nodes, splitting line
685   marked by end at distance t.
686  */
687 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
689     g_assert(new_path != NULL);
690     g_assert(end      != NULL);
692     g_assert(end->p.other == new_path);
693    Inkscape::NodePath::Node *start = new_path->p.other;
694     g_assert(start);
696     if (end->code == NR_LINETO) {
697         new_path->type =Inkscape::NodePath::NODE_CUSP;
698         new_path->code = NR_LINETO;
699         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
700     } else {
701         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
702         new_path->code = NR_CURVETO;
703         gdouble s      = 1 - t;
704         for (int dim = 0; dim < 2; dim++) {
705             NR::Coord const f000 = start->pos[dim];
706             NR::Coord const f001 = start->n.pos[dim];
707             NR::Coord const f011 = end->p.pos[dim];
708             NR::Coord const f111 = end->pos[dim];
709             NR::Coord const f00t = s * f000 + t * f001;
710             NR::Coord const f01t = s * f001 + t * f011;
711             NR::Coord const f11t = s * f011 + t * f111;
712             NR::Coord const f0tt = s * f00t + t * f01t;
713             NR::Coord const f1tt = s * f01t + t * f11t;
714             NR::Coord const fttt = s * f0tt + t * f1tt;
715             start->n.pos[dim]    = f00t;
716             new_path->p.pos[dim] = f0tt;
717             new_path->pos[dim]   = fttt;
718             new_path->n.pos[dim] = f1tt;
719             end->p.pos[dim]      = f11t;
720         }
721     }
724 /**
725  * Adds new node on direct line between two nodes, activates handles of all
726  * three nodes.
727  */
728 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
730     g_assert(end);
731     g_assert(end->subpath);
732     g_assert(g_list_find(end->subpath->nodes, end));
734    Inkscape::NodePath::Node *start = end->p.other;
735     g_assert( start->n.other == end );
736    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
737                                                end,
738                                               Inkscape::NodePath::NODE_SMOOTH,
739                                                (NRPathcode)end->code,
740                                                &start->pos, &start->pos, &start->n.pos);
741     sp_nodepath_line_midpoint(newnode, end, t);
743     sp_node_update_handles(start);
744     sp_node_update_handles(newnode);
745     sp_node_update_handles(end);
747     return newnode;
750 /**
751 \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
752 */
753 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
755     g_assert(node);
756     g_assert(node->subpath);
757     g_assert(g_list_find(node->subpath->nodes, node));
759    Inkscape::NodePath::SubPath *sp = node->subpath;
760     Inkscape::NodePath::Path *np    = sp->nodepath;
762     if (sp->closed) {
763         sp_nodepath_subpath_open(sp, node);
764         return sp->first;
765     } else {
766         // no break for end nodes
767         if (node == sp->first) return NULL;
768         if (node == sp->last ) return NULL;
770         // create a new subpath
771        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
773         // duplicate the break node as start of the new subpath
774        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
776         while (node->n.other) { // copy the remaining nodes into the new subpath
777            Inkscape::NodePath::Node *n  = node->n.other;
778            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);
779             if (n->selected) {
780                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
781             }
782             sp_nodepath_node_destroy(n); // remove the point on the original subpath
783         }
785         return newnode;
786     }
789 /**
790  * Duplicate node and connect to neighbours.
791  */
792 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
794     g_assert(node);
795     g_assert(node->subpath);
796     g_assert(g_list_find(node->subpath->nodes, node));
798    Inkscape::NodePath::SubPath *sp = node->subpath;
800     NRPathcode code = (NRPathcode) node->code;
801     if (code == NR_MOVETO) { // if node is the endnode,
802         node->code = NR_LINETO; // new one is inserted before it, so change that to line
803     }
805     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
807     if (!node->n.other || !node->p.other) // if node is an endnode, select it
808         return node;
809     else
810         return newnode; // otherwise select the newly created node
813 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
815     node->p.pos = (node->pos + (node->pos - node->n.pos));
818 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
820     node->n.pos = (node->pos + (node->pos - node->p.pos));
823 /**
824  * Change line type at node, with side effects on neighbours.
825  */
826 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
828     g_assert(end);
829     g_assert(end->subpath);
830     g_assert(end->p.other);
832     if (end->code == static_cast< guint > ( code ) )
833         return;
835    Inkscape::NodePath::Node *start = end->p.other;
837     end->code = code;
839     if (code == NR_LINETO) {
840         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
841         if (end->n.other) {
842             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
843         }
844         sp_node_adjust_handle(start, -1);
845         sp_node_adjust_handle(end, 1);
846     } else {
847         NR::Point delta = end->pos - start->pos;
848         start->n.pos = start->pos + delta / 3;
849         end->p.pos = end->pos - delta / 3;
850         sp_node_adjust_handle(start, 1);
851         sp_node_adjust_handle(end, -1);
852     }
854     sp_node_update_handles(start);
855     sp_node_update_handles(end);
858 /**
859  * Change node type, and its handles accordingly.
860  */
861 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
863     g_assert(node);
864     g_assert(node->subpath);
866     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
867         return node;
869     if ((node->p.other != NULL) && (node->n.other != NULL)) {
870         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
871             type =Inkscape::NodePath::NODE_CUSP;
872         }
873     }
875     node->type = type;
877     if (node->type == Inkscape::NodePath::NODE_CUSP) {
878         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
879         node->knot->setSize (node->selected? 11 : 9);
880         sp_knot_update_ctrl(node->knot);
881     } else {
882         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
883         node->knot->setSize (node->selected? 9 : 7);
884         sp_knot_update_ctrl(node->knot);
885     }
887     // if one of handles is mouseovered, preserve its position
888     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
889         sp_node_adjust_handle(node, 1);
890     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
891         sp_node_adjust_handle(node, -1);
892     } else {
893         sp_node_adjust_handles(node);
894     }
896     sp_node_update_handles(node);
898     sp_nodepath_update_statusbar(node->subpath->nodepath);
900     return node;
903 /**
904  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
905  * adjacent segments from lines to curves.
906 */
907 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
909     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
910         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
911             // convert adjacent segment BEFORE to curve
912             node->code = NR_CURVETO;
913             NR::Point delta;
914             if (node->n.other != NULL)
915                 delta = node->n.other->pos - node->p.other->pos;
916             else
917                 delta = node->pos - node->p.other->pos;
918             node->p.pos = node->pos - delta / 4;
919             sp_node_update_handles(node);
920         }
922         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
923             // convert adjacent segment AFTER to curve
924             node->n.other->code = NR_CURVETO;
925             NR::Point delta;
926             if (node->p.other != NULL)
927                 delta = node->p.other->pos - node->n.other->pos;
928             else
929                 delta = node->pos - node->n.other->pos;
930             node->n.pos = node->pos - delta / 4;
931             sp_node_update_handles(node);
932         }
933     }
935     sp_nodepath_set_node_type (node, type);
938 /**
939  * Move node to point, and adjust its and neighbouring handles.
940  */
941 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
943     NR::Point delta = p - node->pos;
944     node->pos = p;
946     node->p.pos += delta;
947     node->n.pos += delta;
949     if (node->p.other) {
950         if (node->code == NR_LINETO) {
951             sp_node_adjust_handle(node, 1);
952             sp_node_adjust_handle(node->p.other, -1);
953         }
954     }
955     if (node->n.other) {
956         if (node->n.other->code == NR_LINETO) {
957             sp_node_adjust_handle(node, -1);
958             sp_node_adjust_handle(node->n.other, 1);
959         }
960     }
962     // this function is only called from batch movers that will update display at the end
963     // themselves, so here we just move all the knots without emitting move signals, for speed
964     sp_node_update_handles(node, false);
967 /**
968  * Call sp_node_moveto() for node selection and handle possible snapping.
969  */
970 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
971                                             bool const snap = true)
973     NR::Coord best = NR_HUGE;
974     NR::Point delta(dx, dy);
975     NR::Point best_pt = delta;
977     if (snap) {
978         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
979         
980         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
981             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
982             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
983             if (s.getDistance() < best) {
984                 best = s.getDistance();
985                 best_pt = s.getPoint() - n->pos;
986             }
987         }
988     }
990     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
991        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
992         sp_node_moveto(n, n->pos + best_pt);
993     }
995     // do not update repr here so that node dragging is acceptably fast
996     update_object(nodepath);
999 /**
1000 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1001 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1002 near x = 0.
1003  */
1004 double
1005 sculpt_profile (double x, double alpha, guint profile)
1007     if (x >= 1)
1008         return 0;
1009     if (x <= 0)
1010         return 1;
1012     switch (profile) {
1013         case SCULPT_PROFILE_LINEAR:
1014         return 1 - x;
1015         case SCULPT_PROFILE_BELL:
1016         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1017         case SCULPT_PROFILE_ELLIPTIC:
1018         return sqrt(1 - x*x);
1019     }
1021     return 1;
1024 double
1025 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1027     // extremely primitive for now, don't have time to look for the real one
1028     double lower = NR::L2(b - a);
1029     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1030     return (lower + upper)/2;
1033 void
1034 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1036     n->pos = n->origin + delta;
1037     n->n.pos = n->n.origin + delta_n;
1038     n->p.pos = n->p.origin + delta_p;
1039     sp_node_adjust_handles(n);
1040     sp_node_update_handles(n, false);
1043 /**
1044  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1045  * on how far they are from the dragged node n.
1046  */
1047 static void 
1048 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1050     g_assert (n);
1051     g_assert (nodepath);
1052     g_assert (n->subpath->nodepath == nodepath);
1054     double pressure = n->knot->pressure;
1055     if (pressure == 0)
1056         pressure = 0.5; // default
1057     pressure = CLAMP (pressure, 0.2, 0.8);
1059     // map pressure to alpha = 1/5 ... 5
1060     double alpha = 1 - 2 * fabs(pressure - 0.5);
1061     if (pressure > 0.5)
1062         alpha = 1/alpha;
1064     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1066     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1067         // Only one subpath has selected nodes:
1068         // use linear mode, where the distance from n to node being dragged is calculated along the path
1070         double n_sel_range = 0, p_sel_range = 0;
1071         guint n_nodes = 0, p_nodes = 0;
1072         guint n_sel_nodes = 0, p_sel_nodes = 0;
1074         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1075         {
1076             double n_range = 0, p_range = 0;
1077             bool n_going = true, p_going = true;
1078             Inkscape::NodePath::Node *n_node = n;
1079             Inkscape::NodePath::Node *p_node = n;
1080             do {
1081                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1082                 if (n_node && n_going)
1083                     n_node = n_node->n.other;
1084                 if (n_node == NULL) {
1085                     n_going = false;
1086                 } else {
1087                     n_nodes ++;
1088                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1089                     if (n_node->selected) {
1090                         n_sel_nodes ++;
1091                         n_sel_range = n_range;
1092                     }
1093                     if (n_node == p_node) {
1094                         n_going = false;
1095                         p_going = false;
1096                     }
1097                 }
1098                 if (p_node && p_going)
1099                     p_node = p_node->p.other;
1100                 if (p_node == NULL) {
1101                     p_going = false;
1102                 } else {
1103                     p_nodes ++;
1104                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1105                     if (p_node->selected) {
1106                         p_sel_nodes ++;
1107                         p_sel_range = p_range;
1108                     }
1109                     if (p_node == n_node) {
1110                         n_going = false;
1111                         p_going = false;
1112                     }
1113                 }
1114             } while (n_going || p_going);
1115         }
1117         // Second pass: actually move nodes in this subpath
1118         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1119         {
1120             double n_range = 0, p_range = 0;
1121             bool n_going = true, p_going = true;
1122             Inkscape::NodePath::Node *n_node = n;
1123             Inkscape::NodePath::Node *p_node = n;
1124             do {
1125                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1126                 if (n_node && n_going)
1127                     n_node = n_node->n.other;
1128                 if (n_node == NULL) {
1129                     n_going = false;
1130                 } else {
1131                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1132                     if (n_node->selected) {
1133                         sp_nodepath_move_node_and_handles (n_node, 
1134                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1135                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1136                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1137                     }
1138                     if (n_node == p_node) {
1139                         n_going = false;
1140                         p_going = false;
1141                     }
1142                 }
1143                 if (p_node && p_going)
1144                     p_node = p_node->p.other;
1145                 if (p_node == NULL) {
1146                     p_going = false;
1147                 } else {
1148                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1149                     if (p_node->selected) {
1150                         sp_nodepath_move_node_and_handles (p_node, 
1151                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1152                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1153                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1154                     }
1155                     if (p_node == n_node) {
1156                         n_going = false;
1157                         p_going = false;
1158                     }
1159                 }
1160             } while (n_going || p_going);
1161         }
1163     } else {
1164         // Multiple subpaths have selected nodes:
1165         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1166         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1167         // fix the pear-like shape when sculpting e.g. a ring
1169         // First pass: calculate range
1170         gdouble direct_range = 0;
1171         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1172             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1173             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1174                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1175                 if (node->selected) {
1176                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1177                 }
1178             }
1179         }
1181         // Second pass: actually move nodes
1182         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1183             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1184             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1185                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1186                 if (node->selected) {
1187                     if (direct_range > 1e-6) {
1188                         sp_nodepath_move_node_and_handles (node,
1189                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1190                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1191                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1192                     } else {
1193                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1194                     }
1196                 }
1197             }
1198         }
1199     }
1201     // do not update repr here so that node dragging is acceptably fast
1202     update_object(nodepath);
1206 /**
1207  * Move node selection to point, adjust its and neighbouring handles,
1208  * handle possible snapping, and commit the change with possible undo.
1209  */
1210 void
1211 sp_node_selected_move(gdouble dx, gdouble dy)
1213     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1214     if (!nodepath) return;
1216     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1218     if (dx == 0) {
1219         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1220     } else if (dy == 0) {
1221         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1222     } else {
1223         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1224     }
1227 /**
1228  * Move node selection off screen and commit the change.
1229  */
1230 void
1231 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1233     // borrowed from sp_selection_move_screen in selection-chemistry.c
1234     // we find out the current zoom factor and divide deltas by it
1235     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1237     gdouble zoom = desktop->current_zoom();
1238     gdouble zdx = dx / zoom;
1239     gdouble zdy = dy / zoom;
1241     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1242     if (!nodepath) return;
1244     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1246     if (dx == 0) {
1247         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1248     } else if (dy == 0) {
1249         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1250     } else {
1251         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1252     }
1255 /** If they don't yet exist, creates knot and line for the given side of the node */
1256 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1258     if (!side->knot) {
1259         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"));
1261         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1262         side->knot->setSize (7);
1263         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1264         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1265         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1266         sp_knot_update_ctrl(side->knot);
1268         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1269         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1270         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1271         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1272         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1273         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1274     }
1276     if (!side->line) {
1277         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1278                                         SP_TYPE_CTRLLINE, NULL);
1279     }
1282 /**
1283  * Ensure the given handle of the node is visible/invisible, update its screen position
1284  */
1285 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1287     g_assert(node != NULL);
1289    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1290     NRPathcode code = sp_node_path_code_from_side(node, side);
1292     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1294     if (show_handle) {
1295         if (!side->knot) { // No handle knot at all
1296             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1297             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1298             side->knot->pos = side->pos;
1299             if (side->knot->item) 
1300                 SP_CTRL(side->knot->item)->moveto(side->pos);
1301             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1302             sp_knot_show(side->knot);
1303         } else {
1304             if (side->knot->pos != side->pos) { // only if it's really moved
1305                 if (fire_move_signals) {
1306                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1307                 } else {
1308                     sp_knot_moveto(side->knot, &side->pos);
1309                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1310                 }
1311             }
1312             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1313                 sp_knot_show(side->knot);
1314             }
1315         }
1316         sp_canvas_item_show(side->line);
1317     } else {
1318         if (side->knot) {
1319             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1320                 sp_knot_hide(side->knot);
1321             }
1322         }
1323         if (side->line) {
1324             sp_canvas_item_hide(side->line);
1325         }
1326     }
1329 /**
1330  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1331  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1332  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1333  * updated; otherwise, just move the knots silently (used in batch moves).
1334  */
1335 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1337     g_assert(node != NULL);
1339     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1340         sp_knot_show(node->knot);
1341     }
1343     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1344         if (fire_move_signals)
1345             sp_knot_set_position(node->knot, &node->pos, 0);
1346         else 
1347             sp_knot_moveto(node->knot, &node->pos);
1348     }
1350     gboolean show_handles = node->selected;
1351     if (node->p.other != NULL) {
1352         if (node->p.other->selected) show_handles = TRUE;
1353     }
1354     if (node->n.other != NULL) {
1355         if (node->n.other->selected) show_handles = TRUE;
1356     }
1358     if (node->subpath->nodepath->show_handles == false)
1359         show_handles = FALSE;
1361     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1362     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1365 /**
1366  * Call sp_node_update_handles() for all nodes on subpath.
1367  */
1368 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1370     g_assert(subpath != NULL);
1372     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1373         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1374     }
1377 /**
1378  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1379  */
1380 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1382     g_assert(nodepath != NULL);
1384     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1385         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1386     }
1389 void
1390 sp_nodepath_show_handles(bool show)
1392     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1393     if (nodepath == NULL) return;
1395     nodepath->show_handles = show;
1396     sp_nodepath_update_handles(nodepath);
1399 /**
1400  * Adds all selected nodes in nodepath to list.
1401  */
1402 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1404     StlConv<Node *>::list(l, selected);
1405 /// \todo this adds a copying, rework when the selection becomes a stl list
1408 /**
1409  * Align selected nodes on the specified axis.
1410  */
1411 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1413     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1414         return;
1415     }
1417     if ( !nodepath->selected->next ) { // only one node selected
1418         return;
1419     }
1420    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1421     NR::Point dest(pNode->pos);
1422     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1423         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1424         if (pNode) {
1425             dest[axis] = pNode->pos[axis];
1426             sp_node_moveto(pNode, dest);
1427         }
1428     }
1430     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1433 /// Helper struct.
1434 struct NodeSort
1436    Inkscape::NodePath::Node *_node;
1437     NR::Coord _coord;
1438     /// \todo use vectorof pointers instead of calling copy ctor
1439     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1440         _node(node), _coord(node->pos[axis])
1441     {}
1443 };
1445 static bool operator<(NodeSort const &a, NodeSort const &b)
1447     return (a._coord < b._coord);
1450 /**
1451  * Distribute selected nodes on the specified axis.
1452  */
1453 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1455     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1456         return;
1457     }
1459     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1460         return;
1461     }
1463    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1464     std::vector<NodeSort> sorted;
1465     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1466         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1467         if (pNode) {
1468             NodeSort n(pNode, axis);
1469             sorted.push_back(n);
1470             //dest[axis] = pNode->pos[axis];
1471             //sp_node_moveto(pNode, dest);
1472         }
1473     }
1474     std::sort(sorted.begin(), sorted.end());
1475     unsigned int len = sorted.size();
1476     //overall bboxes span
1477     float dist = (sorted.back()._coord -
1478                   sorted.front()._coord);
1479     //new distance between each bbox
1480     float step = (dist) / (len - 1);
1481     float pos = sorted.front()._coord;
1482     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1483           it < sorted.end();
1484           it ++ )
1485     {
1486         NR::Point dest((*it)._node->pos);
1487         dest[axis] = pos;
1488         sp_node_moveto((*it)._node, dest);
1489         pos += step;
1490     }
1492     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1496 /**
1497  * Call sp_nodepath_line_add_node() for all selected segments.
1498  */
1499 void
1500 sp_node_selected_add_node(void)
1502     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1503     if (!nodepath) {
1504         return;
1505     }
1507     GList *nl = NULL;
1509     int n_added = 0;
1511     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1512        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1513         g_assert(t->selected);
1514         if (t->p.other && t->p.other->selected) {
1515             nl = g_list_prepend(nl, t);
1516         }
1517     }
1519     while (nl) {
1520        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1521        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1522        sp_nodepath_node_select(n, TRUE, FALSE);
1523        n_added ++;
1524        nl = g_list_remove(nl, t);
1525     }
1527     /** \todo fixme: adjust ? */
1528     sp_nodepath_update_handles(nodepath);
1530     if (n_added > 1) {
1531         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1532     } else if (n_added > 0) {
1533         sp_nodepath_update_repr(nodepath, _("Add node"));
1534     }
1536     sp_nodepath_update_statusbar(nodepath);
1539 /**
1540  * Select segment nearest to point
1541  */
1542 void
1543 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1545     if (!nodepath) {
1546         return;
1547     }
1549     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1551     //find segment to segment
1552     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1554     gboolean force = FALSE;
1555     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1556         force = TRUE;
1557     }
1558     sp_nodepath_node_select(e, (gboolean) toggle, force);
1559     if (e->p.other)
1560         sp_nodepath_node_select(e->p.other, TRUE, force);
1562     sp_nodepath_update_handles(nodepath);
1564     sp_nodepath_update_statusbar(nodepath);
1567 /**
1568  * Add a node nearest to point
1569  */
1570 void
1571 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1573     if (!nodepath) {
1574         return;
1575     }
1577     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1579     //find segment to split
1580     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1582     //don't know why but t seems to flip for lines
1583     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1584         position.t = 1.0 - position.t;
1585     }
1586     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1587     sp_nodepath_node_select(n, FALSE, TRUE);
1589     /* fixme: adjust ? */
1590     sp_nodepath_update_handles(nodepath);
1592     sp_nodepath_update_repr(nodepath, _("Add node"));
1594     sp_nodepath_update_statusbar(nodepath);
1597 /*
1598  * Adjusts a segment so that t moves by a certain delta for dragging
1599  * converts lines to curves
1600  *
1601  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1602  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1603  */
1604 void
1605 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1607     /* feel good is an arbitrary parameter that distributes the delta between handles
1608      * if t of the drag point is less than 1/6 distance form the endpoint only
1609      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1610      */
1611     double feel_good;
1612     if (t <= 1.0 / 6.0)
1613         feel_good = 0;
1614     else if (t <= 0.5)
1615         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1616     else if (t <= 5.0 / 6.0)
1617         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1618     else
1619         feel_good = 1;
1621     //if we're dragging a line convert it to a curve
1622     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1623         sp_nodepath_set_line_type(e, NR_CURVETO);
1624     }
1626     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1627     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1628     e->p.other->n.pos += offsetcoord0;
1629     e->p.pos += offsetcoord1;
1631     // adjust handles of adjacent nodes where necessary
1632     sp_node_adjust_handle(e,1);
1633     sp_node_adjust_handle(e->p.other,-1);
1635     sp_nodepath_update_handles(e->subpath->nodepath);
1637     update_object(e->subpath->nodepath);
1639     sp_nodepath_update_statusbar(e->subpath->nodepath);
1643 /**
1644  * Call sp_nodepath_break() for all selected segments.
1645  */
1646 void sp_node_selected_break()
1648     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1649     if (!nodepath) return;
1651     GList *temp = NULL;
1652     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1653        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1654        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1655         if (nn == NULL) continue; // no break, no new node
1656         temp = g_list_prepend(temp, nn);
1657     }
1659     if (temp) {
1660         sp_nodepath_deselect(nodepath);
1661     }
1662     for (GList *l = temp; l != NULL; l = l->next) {
1663         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1664     }
1666     sp_nodepath_update_handles(nodepath);
1668     sp_nodepath_update_repr(nodepath, _("Break path"));
1671 /**
1672  * Duplicate the selected node(s).
1673  */
1674 void sp_node_selected_duplicate()
1676     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1677     if (!nodepath) {
1678         return;
1679     }
1681     GList *temp = NULL;
1682     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1683        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1684        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1685         if (nn == NULL) continue; // could not duplicate
1686         temp = g_list_prepend(temp, nn);
1687     }
1689     if (temp) {
1690         sp_nodepath_deselect(nodepath);
1691     }
1692     for (GList *l = temp; l != NULL; l = l->next) {
1693         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1694     }
1696     sp_nodepath_update_handles(nodepath);
1698     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1701 /**
1702  *  Join two nodes by merging them into one.
1703  */
1704 void sp_node_selected_join()
1706     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1707     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1709     if (g_list_length(nodepath->selected) != 2) {
1710         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1711         return;
1712     }
1714    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1715    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1717     g_assert(a != b);
1718     g_assert(a->p.other || a->n.other);
1719     g_assert(b->p.other || b->n.other);
1721     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1722         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1723         return;
1724     }
1726     /* a and b are endpoints */
1728     NR::Point c;
1729     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1730         c = a->pos;
1731     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1732         c = b->pos;
1733     } else {
1734         c = (a->pos + b->pos) / 2;
1735     }
1737     if (a->subpath == b->subpath) {
1738        Inkscape::NodePath::SubPath *sp = a->subpath;
1739         sp_nodepath_subpath_close(sp);
1740         sp_node_moveto (sp->first, c);
1742         sp_nodepath_update_handles(sp->nodepath);
1743         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1744         return;
1745     }
1747     /* a and b are separate subpaths */
1748    Inkscape::NodePath::SubPath *sa = a->subpath;
1749    Inkscape::NodePath::SubPath *sb = b->subpath;
1750     NR::Point p;
1751    Inkscape::NodePath::Node *n;
1752     NRPathcode code;
1753     if (a == sa->first) {
1754         p = sa->first->n.pos;
1755         code = (NRPathcode)sa->first->n.other->code;
1756        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1757         n = sa->last;
1758         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1759         n = n->p.other;
1760         while (n) {
1761             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1762             n = n->p.other;
1763             if (n == sa->first) n = NULL;
1764         }
1765         sp_nodepath_subpath_destroy(sa);
1766         sa = t;
1767     } else if (a == sa->last) {
1768         p = sa->last->p.pos;
1769         code = (NRPathcode)sa->last->code;
1770         sp_nodepath_node_destroy(sa->last);
1771     } else {
1772         code = NR_END;
1773         g_assert_not_reached();
1774     }
1776     if (b == sb->first) {
1777         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1778         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1779             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1780         }
1781     } else if (b == sb->last) {
1782         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1783         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1784             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1785         }
1786     } else {
1787         g_assert_not_reached();
1788     }
1789     /* and now destroy sb */
1791     sp_nodepath_subpath_destroy(sb);
1793     sp_nodepath_update_handles(sa->nodepath);
1795     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1797     sp_nodepath_update_statusbar(nodepath);
1800 /**
1801  *  Join two nodes by adding a segment between them.
1802  */
1803 void sp_node_selected_join_segment()
1805     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1806     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1808     if (g_list_length(nodepath->selected) != 2) {
1809         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1810         return;
1811     }
1813    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1814    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1816     g_assert(a != b);
1817     g_assert(a->p.other || a->n.other);
1818     g_assert(b->p.other || b->n.other);
1820     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1821         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1822         return;
1823     }
1825     if (a->subpath == b->subpath) {
1826        Inkscape::NodePath::SubPath *sp = a->subpath;
1828         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1829         sp->closed = TRUE;
1831         sp->first->p.other = sp->last;
1832         sp->last->n.other  = sp->first;
1834         sp_node_handle_mirror_p_to_n(sp->last);
1835         sp_node_handle_mirror_n_to_p(sp->first);
1837         sp->first->code = sp->last->code;
1838         sp->first       = sp->last;
1840         sp_nodepath_update_handles(sp->nodepath);
1842         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1844         return;
1845     }
1847     /* a and b are separate subpaths */
1848    Inkscape::NodePath::SubPath *sa = a->subpath;
1849    Inkscape::NodePath::SubPath *sb = b->subpath;
1851    Inkscape::NodePath::Node *n;
1852     NR::Point p;
1853     NRPathcode code;
1854     if (a == sa->first) {
1855         code = (NRPathcode) sa->first->n.other->code;
1856        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1857         n = sa->last;
1858         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1859         for (n = n->p.other; n != NULL; n = n->p.other) {
1860             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1861         }
1862         sp_nodepath_subpath_destroy(sa);
1863         sa = t;
1864     } else if (a == sa->last) {
1865         code = (NRPathcode)sa->last->code;
1866     } else {
1867         code = NR_END;
1868         g_assert_not_reached();
1869     }
1871     if (b == sb->first) {
1872         n = sb->first;
1873         sp_node_handle_mirror_p_to_n(sa->last);
1874         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1875         sp_node_handle_mirror_n_to_p(sa->last);
1876         for (n = n->n.other; n != NULL; n = n->n.other) {
1877             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1878         }
1879     } else if (b == sb->last) {
1880         n = sb->last;
1881         sp_node_handle_mirror_p_to_n(sa->last);
1882         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1883         sp_node_handle_mirror_n_to_p(sa->last);
1884         for (n = n->p.other; n != NULL; n = n->p.other) {
1885             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1886         }
1887     } else {
1888         g_assert_not_reached();
1889     }
1890     /* and now destroy sb */
1892     sp_nodepath_subpath_destroy(sb);
1894     sp_nodepath_update_handles(sa->nodepath);
1896     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1899 /**
1900  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1901  */
1902 void sp_node_delete_preserve(GList *nodes_to_delete)
1904     GSList *nodepaths = NULL;
1905     
1906     while (nodes_to_delete) {
1907         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1908         Inkscape::NodePath::SubPath *sp = node->subpath;
1909         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1910         Inkscape::NodePath::Node *sample_cursor = NULL;
1911         Inkscape::NodePath::Node *sample_end = NULL;
1912         Inkscape::NodePath::Node *delete_cursor = node;
1913         bool just_delete = false;
1914         
1915         //find the start of this contiguous selection
1916         //move left to the first node that is not selected
1917         //or the start of the non-closed path
1918         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1919             delete_cursor = curr;
1920         }
1922         //just delete at the beginning of an open path
1923         if (!delete_cursor->p.other) {
1924             sample_cursor = delete_cursor;
1925             just_delete = true;
1926         } else {
1927             sample_cursor = delete_cursor->p.other;
1928         }
1929         
1930         //calculate points for each segment
1931         int rate = 5;
1932         float period = 1.0 / rate;
1933         std::vector<NR::Point> data;
1934         if (!just_delete) {
1935             data.push_back(sample_cursor->pos);
1936             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1937                 //just delete at the end of an open path
1938                 if (!sp->closed && curr == sp->last) {
1939                     just_delete = true;
1940                     break;
1941                 }
1942                 
1943                 //sample points on the contiguous selected segment
1944                 NR::Point *bez;
1945                 bez = new NR::Point [4];
1946                 bez[0] = curr->pos;
1947                 bez[1] = curr->n.pos;
1948                 bez[2] = curr->n.other->p.pos;
1949                 bez[3] = curr->n.other->pos;
1950                 for (int i=1; i<rate; i++) {
1951                     gdouble t = i * period;
1952                     NR::Point p = bezier_pt(3, bez, t);
1953                     data.push_back(p);
1954                 }
1955                 data.push_back(curr->n.other->pos);
1957                 sample_end = curr->n.other;
1958                 //break if we've come full circle or hit the end of the selection
1959                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1960                     break;
1961                 }
1962             }
1963         }
1965         if (!just_delete) {
1966             //calculate the best fitting single segment and adjust the endpoints
1967             NR::Point *adata;
1968             adata = new NR::Point [data.size()];
1969             copy(data.begin(), data.end(), adata);
1970             
1971             NR::Point *bez;
1972             bez = new NR::Point [4];
1973             //would decreasing error create a better fitting approximation?
1974             gdouble error = 1.0;
1975             gint ret;
1976             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1978             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
1979             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
1980             //the resulting nodes behave as expected.
1981             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
1982             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
1983             
1984             //adjust endpoints
1985             sample_cursor->n.pos = bez[1];
1986             sample_end->p.pos = bez[2];
1987         }
1988        
1989         //destroy this contiguous selection
1990         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
1991             Inkscape::NodePath::Node *temp = delete_cursor;
1992             if (delete_cursor->n.other == delete_cursor) {
1993                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
1994                 delete_cursor = NULL; 
1995             } else {
1996                 delete_cursor = delete_cursor->n.other;
1997             }
1998             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
1999             sp_nodepath_node_destroy(temp);
2000         }
2002         sp_nodepath_update_handles(nodepath);
2004         if (!g_slist_find(nodepaths, nodepath))
2005             nodepaths = g_slist_prepend (nodepaths, nodepath);
2006     }
2008     for (GSList *i = nodepaths; i; i = i->next) {
2009         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2010         // different nodepaths will give us one undo event per nodepath
2011         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2013         // if the entire nodepath is removed, delete the selected object.
2014         if (nodepath->subpaths == NULL ||
2015             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2016             //at least 2
2017             sp_nodepath_get_node_count(nodepath) < 2) {
2018             SPDocument *document = sp_desktop_document (nodepath->desktop);
2019             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2020             //delete this nodepath's object, not the entire selection! (though at this time, this
2021             //does not matter)
2022             sp_selection_delete();
2023             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2024                               _("Delete nodes"));
2025         } else {
2026             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2027             sp_nodepath_update_statusbar(nodepath);
2028         }
2029     }
2031     g_slist_free (nodepaths);
2034 /**
2035  * Delete one or more selected nodes.
2036  */
2037 void sp_node_selected_delete()
2039     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2040     if (!nodepath) return;
2041     if (!nodepath->selected) return;
2043     /** \todo fixme: do it the right way */
2044     while (nodepath->selected) {
2045        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2046         sp_nodepath_node_destroy(node);
2047     }
2050     //clean up the nodepath (such as for trivial subpaths)
2051     sp_nodepath_cleanup(nodepath);
2053     sp_nodepath_update_handles(nodepath);
2055     // if the entire nodepath is removed, delete the selected object.
2056     if (nodepath->subpaths == NULL ||
2057         sp_nodepath_get_node_count(nodepath) < 2) {
2058         SPDocument *document = sp_desktop_document (nodepath->desktop);
2059         sp_selection_delete();
2060         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2061                           _("Delete nodes"));
2062         return;
2063     }
2065     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2067     sp_nodepath_update_statusbar(nodepath);
2070 /**
2071  * Delete one or more segments between two selected nodes.
2072  * This is the code for 'split'.
2073  */
2074 void
2075 sp_node_selected_delete_segment(void)
2077    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2078    Inkscape::NodePath::Node *curr, *next;     //Iterators
2080     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2081     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2083     if (g_list_length(nodepath->selected) != 2) {
2084         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2085                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2086         return;
2087     }
2089     //Selected nodes, not inclusive
2090    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2091    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2093     if ( ( a==b)                       ||  //same node
2094          (a->subpath  != b->subpath )  ||  //not the same path
2095          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2096          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2097     {
2098         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2099                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2100         return;
2101     }
2103     //###########################################
2104     //# BEGIN EDITS
2105     //###########################################
2106     //##################################
2107     //# CLOSED PATH
2108     //##################################
2109     if (a->subpath->closed) {
2112         gboolean reversed = FALSE;
2114         //Since we can go in a circle, we need to find the shorter distance.
2115         //  a->b or b->a
2116         start = end = NULL;
2117         int distance    = 0;
2118         int minDistance = 0;
2119         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2120             if (curr==b) {
2121                 //printf("a to b:%d\n", distance);
2122                 start = a;//go from a to b
2123                 end   = b;
2124                 minDistance = distance;
2125                 //printf("A to B :\n");
2126                 break;
2127             }
2128             distance++;
2129         }
2131         //try again, the other direction
2132         distance = 0;
2133         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2134             if (curr==a) {
2135                 //printf("b to a:%d\n", distance);
2136                 if (distance < minDistance) {
2137                     start    = b;  //we go from b to a
2138                     end      = a;
2139                     reversed = TRUE;
2140                     //printf("B to A\n");
2141                 }
2142                 break;
2143             }
2144             distance++;
2145         }
2148         //Copy everything from 'end' to 'start' to a new subpath
2149        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2150         for (curr=end ; curr ; curr=curr->n.other) {
2151             NRPathcode code = (NRPathcode) curr->code;
2152             if (curr == end)
2153                 code = NR_MOVETO;
2154             sp_nodepath_node_new(t, NULL,
2155                                  (Inkscape::NodePath::NodeType)curr->type, code,
2156                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2157             if (curr == start)
2158                 break;
2159         }
2160         sp_nodepath_subpath_destroy(a->subpath);
2163     }
2167     //##################################
2168     //# OPEN PATH
2169     //##################################
2170     else {
2172         //We need to get the direction of the list between A and B
2173         //Can we walk from a to b?
2174         start = end = NULL;
2175         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2176             if (curr==b) {
2177                 start = a;  //did it!  we go from a to b
2178                 end   = b;
2179                 //printf("A to B\n");
2180                 break;
2181             }
2182         }
2183         if (!start) {//didn't work?  let's try the other direction
2184             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2185                 if (curr==a) {
2186                     start = b;  //did it!  we go from b to a
2187                     end   = a;
2188                     //printf("B to A\n");
2189                     break;
2190                 }
2191             }
2192         }
2193         if (!start) {
2194             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2195                                                      _("Cannot find path between nodes."));
2196             return;
2197         }
2201         //Copy everything after 'end' to a new subpath
2202        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2203         for (curr=end ; curr ; curr=curr->n.other) {
2204             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2205                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2206         }
2208         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2209         for (curr = start->n.other ; curr  ; curr=next) {
2210             next = curr->n.other;
2211             sp_nodepath_node_destroy(curr);
2212         }
2214     }
2215     //###########################################
2216     //# END EDITS
2217     //###########################################
2219     //clean up the nodepath (such as for trivial subpaths)
2220     sp_nodepath_cleanup(nodepath);
2222     sp_nodepath_update_handles(nodepath);
2224     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2226     sp_nodepath_update_statusbar(nodepath);
2229 /**
2230  * Call sp_nodepath_set_line() for all selected segments.
2231  */
2232 void
2233 sp_node_selected_set_line_type(NRPathcode code)
2235     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2236     if (nodepath == NULL) return;
2238     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2239        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2240         g_assert(n->selected);
2241         if (n->p.other && n->p.other->selected) {
2242             sp_nodepath_set_line_type(n, code);
2243         }
2244     }
2246     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2249 /**
2250  * Call sp_nodepath_convert_node_type() for all selected nodes.
2251  */
2252 void
2253 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2255     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2256     if (nodepath == NULL) return;
2258     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2259         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2260     }
2262     sp_nodepath_update_repr(nodepath, _("Change node type"));
2265 /**
2266  * Change select status of node, update its own and neighbour handles.
2267  */
2268 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2270     node->selected = selected;
2272     if (selected) {
2273         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2274         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2275         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2276         sp_knot_update_ctrl(node->knot);
2277     } else {
2278         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2279         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2280         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2281         sp_knot_update_ctrl(node->knot);
2282     }
2284     sp_node_update_handles(node);
2285     if (node->n.other) sp_node_update_handles(node->n.other);
2286     if (node->p.other) sp_node_update_handles(node->p.other);
2289 /**
2290 \brief Select a node
2291 \param node     The node to select
2292 \param incremental   If true, add to selection, otherwise deselect others
2293 \param override   If true, always select this node, otherwise toggle selected status
2294 */
2295 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2297     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2299     if (incremental) {
2300         if (override) {
2301             if (!g_list_find(nodepath->selected, node)) {
2302                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2303             }
2304             sp_node_set_selected(node, TRUE);
2305         } else { // toggle
2306             if (node->selected) {
2307                 g_assert(g_list_find(nodepath->selected, node));
2308                 nodepath->selected = g_list_remove(nodepath->selected, node);
2309             } else {
2310                 g_assert(!g_list_find(nodepath->selected, node));
2311                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2312             }
2313             sp_node_set_selected(node, !node->selected);
2314         }
2315     } else {
2316         sp_nodepath_deselect(nodepath);
2317         nodepath->selected = g_list_prepend(nodepath->selected, node);
2318         sp_node_set_selected(node, TRUE);
2319     }
2321     sp_nodepath_update_statusbar(nodepath);
2325 /**
2326 \brief Deselect all nodes in the nodepath
2327 */
2328 void
2329 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2331     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2333     while (nodepath->selected) {
2334         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2335         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2336     }
2337     sp_nodepath_update_statusbar(nodepath);
2340 /**
2341 \brief Select or invert selection of all nodes in the nodepath
2342 */
2343 void
2344 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2346     if (!nodepath) return;
2348     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2349        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2350         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2351            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2352            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2353         }
2354     }
2357 /**
2358  * If nothing selected, does the same as sp_nodepath_select_all();
2359  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2360  * (i.e., similar to "select all in layer", with the "selected" subpaths
2361  * being treated as "layers" in the path).
2362  */
2363 void
2364 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2366     if (!nodepath) return;
2368     if (g_list_length (nodepath->selected) == 0) {
2369         sp_nodepath_select_all (nodepath, invert);
2370         return;
2371     }
2373     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2374     GSList *subpaths = NULL;
2376     for (GList *l = copy; l != NULL; l = l->next) {
2377         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2378         Inkscape::NodePath::SubPath *subpath = n->subpath;
2379         if (!g_slist_find (subpaths, subpath))
2380             subpaths = g_slist_prepend (subpaths, subpath);
2381     }
2383     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2384         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2385         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2386             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2387             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2388         }
2389     }
2391     g_slist_free (subpaths);
2392     g_list_free (copy);
2395 /**
2396  * \brief Select the node after the last selected; if none is selected,
2397  * select the first within path.
2398  */
2399 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2401     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2403    Inkscape::NodePath::Node *last = NULL;
2404     if (nodepath->selected) {
2405         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2406            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2407             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2408             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2409                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2410                 if (node->selected) {
2411                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2412                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2413                             if (spl->next) { // there's a next subpath
2414                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2415                                 last = subpath_next->first;
2416                             } else if (spl->prev) { // there's a previous subpath
2417                                 last = NULL; // to be set later to the first node of first subpath
2418                             } else {
2419                                 last = node->n.other;
2420                             }
2421                         } else {
2422                             last = node->n.other;
2423                         }
2424                     } else {
2425                         if (node->n.other) {
2426                             last = node->n.other;
2427                         } else {
2428                             if (spl->next) { // there's a next subpath
2429                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2430                                 last = subpath_next->first;
2431                             } else if (spl->prev) { // there's a previous subpath
2432                                 last = NULL; // to be set later to the first node of first subpath
2433                             } else {
2434                                 last = (Inkscape::NodePath::Node *) subpath->first;
2435                             }
2436                         }
2437                     }
2438                 }
2439             }
2440         }
2441         sp_nodepath_deselect(nodepath);
2442     }
2444     if (last) { // there's at least one more node after selected
2445         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2446     } else { // no more nodes, select the first one in first subpath
2447        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2448         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2449     }
2452 /**
2453  * \brief Select the node before the first selected; if none is selected,
2454  * select the last within path
2455  */
2456 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2458     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2460    Inkscape::NodePath::Node *last = NULL;
2461     if (nodepath->selected) {
2462         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2463            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2464             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2465                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2466                 if (node->selected) {
2467                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2468                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2469                             if (spl->prev) { // there's a prev subpath
2470                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2471                                 last = subpath_prev->last;
2472                             } else if (spl->next) { // there's a next subpath
2473                                 last = NULL; // to be set later to the last node of last subpath
2474                             } else {
2475                                 last = node->p.other;
2476                             }
2477                         } else {
2478                             last = node->p.other;
2479                         }
2480                     } else {
2481                         if (node->p.other) {
2482                             last = node->p.other;
2483                         } else {
2484                             if (spl->prev) { // there's a prev subpath
2485                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2486                                 last = subpath_prev->last;
2487                             } else if (spl->next) { // there's a next subpath
2488                                 last = NULL; // to be set later to the last node of last subpath
2489                             } else {
2490                                 last = (Inkscape::NodePath::Node *) subpath->last;
2491                             }
2492                         }
2493                     }
2494                 }
2495             }
2496         }
2497         sp_nodepath_deselect(nodepath);
2498     }
2500     if (last) { // there's at least one more node before selected
2501         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2502     } else { // no more nodes, select the last one in last subpath
2503         GList *spl = g_list_last(nodepath->subpaths);
2504        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2505         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2506     }
2509 /**
2510  * \brief Select all nodes that are within the rectangle.
2511  */
2512 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2514     if (!incremental) {
2515         sp_nodepath_deselect(nodepath);
2516     }
2518     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2519        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2520         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2521            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2523             if (b.contains(node->pos)) {
2524                 sp_nodepath_node_select(node, TRUE, TRUE);
2525             }
2526         }
2527     }
2531 void
2532 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2534     g_assert (n);
2535     g_assert (nodepath);
2536     g_assert (n->subpath->nodepath == nodepath);
2538     if (g_list_length (nodepath->selected) == 0) {
2539         if (grow > 0) {
2540             sp_nodepath_node_select(n, TRUE, TRUE);
2541         }
2542         return;
2543     }
2545     if (g_list_length (nodepath->selected) == 1) {
2546         if (grow < 0) {
2547             sp_nodepath_deselect (nodepath);
2548             return;
2549         }
2550     }
2552         double n_sel_range = 0, p_sel_range = 0;
2553             Inkscape::NodePath::Node *farthest_n_node = n;
2554             Inkscape::NodePath::Node *farthest_p_node = n;
2556         // Calculate ranges
2557         {
2558             double n_range = 0, p_range = 0;
2559             bool n_going = true, p_going = true;
2560             Inkscape::NodePath::Node *n_node = n;
2561             Inkscape::NodePath::Node *p_node = n;
2562             do {
2563                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2564                 if (n_node && n_going)
2565                     n_node = n_node->n.other;
2566                 if (n_node == NULL) {
2567                     n_going = false;
2568                 } else {
2569                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2570                     if (n_node->selected) {
2571                         n_sel_range = n_range;
2572                         farthest_n_node = n_node;
2573                     }
2574                     if (n_node == p_node) {
2575                         n_going = false;
2576                         p_going = false;
2577                     }
2578                 }
2579                 if (p_node && p_going)
2580                     p_node = p_node->p.other;
2581                 if (p_node == NULL) {
2582                     p_going = false;
2583                 } else {
2584                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2585                     if (p_node->selected) {
2586                         p_sel_range = p_range;
2587                         farthest_p_node = p_node;
2588                     }
2589                     if (p_node == n_node) {
2590                         n_going = false;
2591                         p_going = false;
2592                     }
2593                 }
2594             } while (n_going || p_going);
2595         }
2597     if (grow > 0) {
2598         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2599                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2600         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2601                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2602         }
2603     } else {
2604         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2605                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2606         } else if (farthest_p_node && farthest_p_node->selected) {
2607                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2608         }
2609     }
2612 void
2613 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2615     g_assert (n);
2616     g_assert (nodepath);
2617     g_assert (n->subpath->nodepath == nodepath);
2619     if (g_list_length (nodepath->selected) == 0) {
2620         if (grow > 0) {
2621             sp_nodepath_node_select(n, TRUE, TRUE);
2622         }
2623         return;
2624     }
2626     if (g_list_length (nodepath->selected) == 1) {
2627         if (grow < 0) {
2628             sp_nodepath_deselect (nodepath);
2629             return;
2630         }
2631     }
2633     Inkscape::NodePath::Node *farthest_selected = NULL;
2634     double farthest_dist = 0;
2636     Inkscape::NodePath::Node *closest_unselected = NULL;
2637     double closest_dist = NR_HUGE;
2639     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2640        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2641         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2642            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2643            if (node == n)
2644                continue;
2645            if (node->selected) {
2646                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2647                    farthest_dist = NR::L2(node->pos - n->pos);
2648                    farthest_selected = node;
2649                }
2650            } else {
2651                if (NR::L2(node->pos - n->pos) < closest_dist) {
2652                    closest_dist = NR::L2(node->pos - n->pos);
2653                    closest_unselected = node;
2654                }
2655            }
2656         }
2657     }
2659     if (grow > 0) {
2660         if (closest_unselected) {
2661             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2662         }
2663     } else {
2664         if (farthest_selected) {
2665             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2666         }
2667     }
2671 /**
2672 \brief  Saves all nodes' and handles' current positions in their origin members
2673 */
2674 void
2675 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2677     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2678        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2679         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2680            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2681            n->origin = n->pos;
2682            n->p.origin = n->p.pos;
2683            n->n.origin = n->n.pos;
2684         }
2685     }
2686
2688 /**
2689 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2690 */
2691 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2693     if (!nodepath->selected) {
2694         return NULL;
2695     }
2697     GList *r = NULL;
2698     guint i = 0;
2699     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2700        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2701         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2702            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2703             i++;
2704             if (node->selected) {
2705                 r = g_list_append(r, GINT_TO_POINTER(i));
2706             }
2707         }
2708     }
2709     return r;
2712 /**
2713 \brief  Restores selection by selecting nodes whose positions are in the list
2714 */
2715 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2717     sp_nodepath_deselect(nodepath);
2719     guint i = 0;
2720     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2721        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2722         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2723            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2724             i++;
2725             if (g_list_find(r, GINT_TO_POINTER(i))) {
2726                 sp_nodepath_node_select(node, TRUE, TRUE);
2727             }
2728         }
2729     }
2733 /**
2734 \brief Adjusts handle according to node type and line code.
2735 */
2736 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2738     double len, otherlen, linelen;
2740     g_assert(node);
2742    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2743    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2745     /** \todo fixme: */
2746     if (me->other == NULL) return;
2747     if (other->other == NULL) return;
2749     /* I have line */
2751     NRPathcode mecode, ocode;
2752     if (which_adjust == 1) {
2753         mecode = (NRPathcode)me->other->code;
2754         ocode = (NRPathcode)node->code;
2755     } else {
2756         mecode = (NRPathcode)node->code;
2757         ocode = (NRPathcode)other->other->code;
2758     }
2760     if (mecode == NR_LINETO) return;
2762     /* I am curve */
2764     if (other->other == NULL) return;
2766     /* Other has line */
2768     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2770     NR::Point delta;
2771     if (ocode == NR_LINETO) {
2772         /* other is lineto, we are either smooth or symm */
2773        Inkscape::NodePath::Node *othernode = other->other;
2774         len = NR::L2(me->pos - node->pos);
2775         delta = node->pos - othernode->pos;
2776         linelen = NR::L2(delta);
2777         if (linelen < 1e-18) 
2778             return;
2779         me->pos = node->pos + (len / linelen)*delta;
2780         return;
2781     }
2783     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2785         me->pos = 2 * node->pos - other->pos;
2786         return;
2787     }
2789     /* We are smooth */
2791     len = NR::L2(me->pos - node->pos);
2792     delta = other->pos - node->pos;
2793     otherlen = NR::L2(delta);
2794     if (otherlen < 1e-18) return;
2796     me->pos = node->pos - (len / otherlen) * delta;
2799 /**
2800  \brief Adjusts both handles according to node type and line code
2801  */
2802 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2804     g_assert(node);
2806     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2808     /* we are either smooth or symm */
2810     if (node->p.other == NULL) return;
2812     if (node->n.other == NULL) return;
2814     if (node->code == NR_LINETO) {
2815         if (node->n.other->code == NR_LINETO) return;
2816         sp_node_adjust_handle(node, 1);
2817         return;
2818     }
2820     if (node->n.other->code == NR_LINETO) {
2821         if (node->code == NR_LINETO) return;
2822         sp_node_adjust_handle(node, -1);
2823         return;
2824     }
2826     /* both are curves */
2827     NR::Point const delta( node->n.pos - node->p.pos );
2829     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2830         node->p.pos = node->pos - delta / 2;
2831         node->n.pos = node->pos + delta / 2;
2832         return;
2833     }
2835     /* We are smooth */
2836     double plen = NR::L2(node->p.pos - node->pos);
2837     if (plen < 1e-18) return;
2838     double nlen = NR::L2(node->n.pos - node->pos);
2839     if (nlen < 1e-18) return;
2840     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2841     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2844 /**
2845  * Node event callback.
2846  */
2847 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2849     gboolean ret = FALSE;
2850     switch (event->type) {
2851         case GDK_ENTER_NOTIFY:
2852             active_node = n;
2853             break;
2854         case GDK_LEAVE_NOTIFY:
2855             active_node = NULL;
2856             break;
2857         case GDK_KEY_PRESS:
2858             switch (get_group0_keyval (&event->key)) {
2859                 case GDK_space:
2860                     if (event->key.state & GDK_BUTTON1_MASK) {
2861                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2862                         stamp_repr(nodepath);
2863                         ret = TRUE;
2864                     }
2865                     break;
2866                 case GDK_Page_Up:
2867                     if (event->key.state & GDK_CONTROL_MASK) {
2868                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2869                     } else {
2870                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2871                     }
2872                     break;
2873                 case GDK_Page_Down:
2874                     if (event->key.state & GDK_CONTROL_MASK) {
2875                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2876                     } else {
2877                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2878                     }
2879                     break;
2880                 default:
2881                     break;
2882             }
2883             break;
2884         default:
2885             break;
2886     }
2888     return ret;
2891 /**
2892  * Handle keypress on node; directly called.
2893  */
2894 gboolean node_key(GdkEvent *event)
2896     Inkscape::NodePath::Path *np;
2898     // there is no way to verify nodes so set active_node to nil when deleting!!
2899     if (active_node == NULL) return FALSE;
2901     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2902         gint ret = FALSE;
2903         switch (get_group0_keyval (&event->key)) {
2904             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2905             case GDK_BackSpace:
2906                 np = active_node->subpath->nodepath;
2907                 sp_nodepath_node_destroy(active_node);
2908                 sp_nodepath_update_repr(np, _("Delete node"));
2909                 active_node = NULL;
2910                 ret = TRUE;
2911                 break;
2912             case GDK_c:
2913                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2914                 ret = TRUE;
2915                 break;
2916             case GDK_s:
2917                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2918                 ret = TRUE;
2919                 break;
2920             case GDK_y:
2921                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2922                 ret = TRUE;
2923                 break;
2924             case GDK_b:
2925                 sp_nodepath_node_break(active_node);
2926                 ret = TRUE;
2927                 break;
2928         }
2929         return ret;
2930     }
2931     return FALSE;
2934 /**
2935  * Mouseclick on node callback.
2936  */
2937 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2939    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2941     if (state & GDK_CONTROL_MASK) {
2942         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2944         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2945             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2946                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2947             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2948                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2949             } else {
2950                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2951             }
2952             sp_nodepath_update_repr(nodepath, _("Change node type"));
2953             sp_nodepath_update_statusbar(nodepath);
2955         } else { //ctrl+alt+click: delete node
2956             GList *node_to_delete = NULL;
2957             node_to_delete = g_list_append(node_to_delete, n);
2958             sp_node_delete_preserve(node_to_delete);
2959         }
2961     } else {
2962         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2963     }
2966 /**
2967  * Mouse grabbed node callback.
2968  */
2969 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2971    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2973     if (!n->selected) {
2974         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2975     }
2977     sp_nodepath_remember_origins (n->subpath->nodepath);
2980 /**
2981  * Mouse ungrabbed node callback.
2982  */
2983 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2985    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2987    n->dragging_out = NULL;
2989    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
2992 /**
2993  * The point on a line, given by its angle, closest to the given point.
2994  * \param p  A point.
2995  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2996  * \param closest  Pointer to the point struct where the result is stored.
2997  * \todo FIXME: use dot product perhaps?
2998  */
2999 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3001     if (a == HUGE_VAL) { // vertical
3002         *closest = NR::Point(0, (*p)[NR::Y]);
3003     } else {
3004         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3005         (*closest)[NR::Y] = a * (*closest)[NR::X];
3006     }
3009 /**
3010  * Distance from the point to a line given by its angle.
3011  * \param p  A point.
3012  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3013  */
3014 static double point_line_distance(NR::Point *p, double a)
3016     NR::Point c;
3017     point_line_closest(p, a, &c);
3018     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]));
3021 /**
3022  * Callback for node "request" signal.
3023  * \todo fixme: This goes to "moved" event? (lauris)
3024  */
3025 static gboolean
3026 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3028     double yn, xn, yp, xp;
3029     double an, ap, na, pa;
3030     double d_an, d_ap, d_na, d_pa;
3031     gboolean collinear = FALSE;
3032     NR::Point c;
3033     NR::Point pr;
3035    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3037    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3038    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3040        NR::Point mouse = (*p);
3042        if (!n->dragging_out) {
3043            // This is the first drag-out event; find out which handle to drag out
3044            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3045            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3047            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3048                return FALSE;
3050            Inkscape::NodePath::NodeSide *opposite;
3051            if (appr_p > appr_n) { // closer to p
3052                n->dragging_out = &n->p;
3053                opposite = &n->n;
3054                n->code = NR_CURVETO;
3055            } else if (appr_p < appr_n) { // closer to n
3056                n->dragging_out = &n->n;
3057                opposite = &n->p;
3058                n->n.other->code = NR_CURVETO;
3059            } else { // p and n nodes are the same
3060                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3061                    n->dragging_out = &n->p;
3062                    opposite = &n->n;
3063                    n->code = NR_CURVETO;
3064                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3065                    n->dragging_out = &n->n;
3066                    opposite = &n->p;
3067                    n->n.other->code = NR_CURVETO;
3068                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3069                    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);
3070                    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);
3071                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3072                        n->dragging_out = &n->n;
3073                        opposite = &n->p;
3074                        n->n.other->code = NR_CURVETO;
3075                    } else { // closer to other's n handle
3076                        n->dragging_out = &n->p;
3077                        opposite = &n->n;
3078                        n->code = NR_CURVETO;
3079                    }
3080                }
3081            }
3083            // if there's another handle, make sure the one we drag out starts parallel to it
3084            if (opposite->pos != n->pos) {
3085                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3086            }
3088            // knots might not be created yet!
3089            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3090            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3091        }
3093        // pass this on to the handle-moved callback
3094        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3095        sp_node_update_handles(n);
3096        return TRUE;
3097    }
3099     if (state & GDK_CONTROL_MASK) { // constrained motion
3101         // calculate relative distances of handles
3102         // n handle:
3103         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3104         xn = n->n.pos[NR::X] - n->pos[NR::X];
3105         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3106         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3107             if (n->n.other) { // if there is the next point
3108                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3109                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3110                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3111             }
3112         }
3113         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3114         if (yn < 0) { xn = -xn; yn = -yn; }
3116         // p handle:
3117         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3118         xp = n->p.pos[NR::X] - n->pos[NR::X];
3119         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3120         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3121             if (n->p.other) {
3122                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3123                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3124                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3125             }
3126         }
3127         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3128         if (yp < 0) { xp = -xp; yp = -yp; }
3130         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3131             // sliding on handles, only if at least one of the handles is non-vertical
3132             // (otherwise it's the same as ctrl+drag anyway)
3134             // calculate angles of the handles
3135             if (xn == 0) {
3136                 if (yn == 0) { // no handle, consider it the continuation of the other one
3137                     an = 0;
3138                     collinear = TRUE;
3139                 }
3140                 else an = 0; // vertical; set the angle to horizontal
3141             } else an = yn/xn;
3143             if (xp == 0) {
3144                 if (yp == 0) { // no handle, consider it the continuation of the other one
3145                     ap = an;
3146                 }
3147                 else ap = 0; // vertical; set the angle to horizontal
3148             } else  ap = yp/xp;
3150             if (collinear) an = ap;
3152             // angles of the perpendiculars; HUGE_VAL means vertical
3153             if (an == 0) na = HUGE_VAL; else na = -1/an;
3154             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3156             // mouse point relative to the node's original pos
3157             pr = (*p) - n->origin;
3159             // distances to the four lines (two handles and two perpendiculars)
3160             d_an = point_line_distance(&pr, an);
3161             d_na = point_line_distance(&pr, na);
3162             d_ap = point_line_distance(&pr, ap);
3163             d_pa = point_line_distance(&pr, pa);
3165             // find out which line is the closest, save its closest point in c
3166             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3167                 point_line_closest(&pr, an, &c);
3168             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3169                 point_line_closest(&pr, ap, &c);
3170             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3171                 point_line_closest(&pr, na, &c);
3172             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3173                 point_line_closest(&pr, pa, &c);
3174             }
3176             // move the node to the closest point
3177             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3178                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3179                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3181         } else {  // constraining to hor/vert
3183             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3184                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3185             } else { // snap to vert
3186                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3187             }
3188         }
3189     } else { // move freely
3190         if (state & GDK_MOD1_MASK) { // sculpt
3191             sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3192         } else {
3193             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3194                                         (*p)[NR::X] - n->pos[NR::X],
3195                                         (*p)[NR::Y] - n->pos[NR::Y],
3196                                         (state & GDK_SHIFT_MASK) == 0);
3197         }
3198     }
3200     n->subpath->nodepath->desktop->scroll_to_point(p);
3202     return TRUE;
3205 /**
3206  * Node handle clicked callback.
3207  */
3208 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3210    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3212     if (state & GDK_CONTROL_MASK) { // "delete" handle
3213         if (n->p.knot == knot) {
3214             n->p.pos = n->pos;
3215         } else if (n->n.knot == knot) {
3216             n->n.pos = n->pos;
3217         }
3218         sp_node_update_handles(n);
3219         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3220         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3221         sp_nodepath_update_statusbar(nodepath);
3223     } else { // just select or add to selection, depending in Shift
3224         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3225     }
3228 /**
3229  * Node handle grabbed callback.
3230  */
3231 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3233    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3235     if (!n->selected) {
3236         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3237     }
3239     // remember the origin point of the handle
3240     if (n->p.knot == knot) {
3241         n->p.origin_radial = n->p.pos - n->pos;
3242     } else if (n->n.knot == knot) {
3243         n->n.origin_radial = n->n.pos - n->pos;
3244     } else {
3245         g_assert_not_reached();
3246     }
3250 /**
3251  * Node handle ungrabbed callback.
3252  */
3253 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3255    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3257     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3258     if (n->p.knot == knot) {
3259         n->p.origin_radial.a = 0;
3260         sp_knot_set_position(knot, &n->p.pos, state);
3261     } else if (n->n.knot == knot) {
3262         n->n.origin_radial.a = 0;
3263         sp_knot_set_position(knot, &n->n.pos, state);
3264     } else {
3265         g_assert_not_reached();
3266     }
3268     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3271 /**
3272  * Node handle "request" signal callback.
3273  */
3274 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3276     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3278     Inkscape::NodePath::NodeSide *me, *opposite;
3279     gint which;
3280     if (n->p.knot == knot) {
3281         me = &n->p;
3282         opposite = &n->n;
3283         which = -1;
3284     } else if (n->n.knot == knot) {
3285         me = &n->n;
3286         opposite = &n->p;
3287         which = 1;
3288     } else {
3289         me = opposite = NULL;
3290         which = 0;
3291         g_assert_not_reached();
3292     }
3294     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3296     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3298     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3299         /* We are smooth node adjacent with line */
3300         NR::Point const delta = *p - n->pos;
3301         NR::Coord const len = NR::L2(delta);
3302         Inkscape::NodePath::Node *othernode = opposite->other;
3303         NR::Point const ndelta = n->pos - othernode->pos;
3304         NR::Coord const linelen = NR::L2(ndelta);
3305         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3306             NR::Coord const scal = dot(delta, ndelta) / linelen;
3307             (*p) = n->pos + (scal / linelen) * ndelta;
3308         }
3309         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3310     } else {
3311         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3312     }
3314     sp_node_adjust_handle(n, -which);
3316     return FALSE;
3319 /**
3320  * Node handle moved callback.
3321  */
3322 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3324    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3326    Inkscape::NodePath::NodeSide *me;
3327    Inkscape::NodePath::NodeSide *other;
3328     if (n->p.knot == knot) {
3329         me = &n->p;
3330         other = &n->n;
3331     } else if (n->n.knot == knot) {
3332         me = &n->n;
3333         other = &n->p;
3334     } else {
3335         me = NULL;
3336         other = NULL;
3337         g_assert_not_reached();
3338     }
3340     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3341     Radial rme(me->pos - n->pos);
3342     Radial rother(other->pos - n->pos);
3343     Radial rnew(*p - n->pos);
3345     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3346         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3347         /* 0 interpreted as "no snapping". */
3349         // The closest PI/snaps angle, starting from zero.
3350         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3351         if (me->origin_radial.a == HUGE_VAL) {
3352             // ortho doesn't exist: original handle was zero length.
3353             rnew.a = a_snapped;
3354         } else {
3355             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3356              * its opposite and perpendiculars). */
3357             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3359             // Snap to the closest.
3360             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3361                        ? a_snapped
3362                        : a_ortho );
3363         }
3364     }
3366     if (state & GDK_MOD1_MASK) {
3367         // lock handle length
3368         rnew.r = me->origin_radial.r;
3369     }
3371     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3372         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3373         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3374         rother.a += rnew.a - rme.a;
3375         other->pos = NR::Point(rother) + n->pos;
3376         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3377         sp_knot_set_position(other->knot, &other->pos, 0);
3378     }
3380     me->pos = NR::Point(rnew) + n->pos;
3381     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3383     // this is what sp_knot_set_position does, but without emitting the signal:
3384     // we cannot emit a "moved" signal because we're now processing it
3385     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3387     knot->desktop->set_coordinate_status(me->pos);
3389     update_object(n->subpath->nodepath);
3391     /* status text */
3392     SPDesktop *desktop = n->subpath->nodepath->desktop;
3393     if (!desktop) return;
3394     SPEventContext *ec = desktop->event_context;
3395     if (!ec) return;
3396     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3397     if (!mc) return;
3399     double degrees = 180 / M_PI * rnew.a;
3400     if (degrees > 180) degrees -= 360;
3401     if (degrees < -180) degrees += 360;
3402     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3403         degrees = angle_to_compass (degrees);
3405     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3407     mc->setF(Inkscape::NORMAL_MESSAGE,
3408          _("<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);
3410     g_string_free(length, TRUE);
3413 /**
3414  * Node handle event callback.
3415  */
3416 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3418     gboolean ret = FALSE;
3419     switch (event->type) {
3420         case GDK_KEY_PRESS:
3421             switch (get_group0_keyval (&event->key)) {
3422                 case GDK_space:
3423                     if (event->key.state & GDK_BUTTON1_MASK) {
3424                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3425                         stamp_repr(nodepath);
3426                         ret = TRUE;
3427                     }
3428                     break;
3429                 default:
3430                     break;
3431             }
3432             break;
3433         default:
3434             break;
3435     }
3437     return ret;
3440 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3441                                  Radial &rme, Radial &rother, gboolean const both)
3443     rme.a += angle;
3444     if ( both
3445          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3446          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3447     {
3448         rother.a += angle;
3449     }
3452 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3453                                         Radial &rme, Radial &rother, gboolean const both)
3455     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3457     gdouble r;
3458     if ( both
3459          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3460          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3461     {
3462         r = MAX(rme.r, rother.r);
3463     } else {
3464         r = rme.r;
3465     }
3467     gdouble const weird_angle = atan2(norm_angle, r);
3468 /* Bulia says norm_angle is just the visible distance that the
3469  * object's end must travel on the screen.  Left as 'angle' for want of
3470  * a better name.*/
3472     rme.a += weird_angle;
3473     if ( both
3474          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3475          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3476     {
3477         rother.a += weird_angle;
3478     }
3481 /**
3482  * Rotate one node.
3483  */
3484 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3486     Inkscape::NodePath::NodeSide *me, *other;
3487     bool both = false;
3489     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3490     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3492     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3493         me = &(n->p);
3494         other = &(n->n);
3495     } else if (!n->p.other) {
3496         me = &(n->n);
3497         other = &(n->p);
3498     } else {
3499         if (which > 0) { // right handle
3500             if (xn > xp) {
3501                 me = &(n->n);
3502                 other = &(n->p);
3503             } else {
3504                 me = &(n->p);
3505                 other = &(n->n);
3506             }
3507         } else if (which < 0){ // left handle
3508             if (xn <= xp) {
3509                 me = &(n->n);
3510                 other = &(n->p);
3511             } else {
3512                 me = &(n->p);
3513                 other = &(n->n);
3514             }
3515         } else { // both handles
3516             me = &(n->n);
3517             other = &(n->p);
3518             both = true;
3519         }
3520     }
3522     Radial rme(me->pos - n->pos);
3523     Radial rother(other->pos - n->pos);
3525     if (screen) {
3526         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3527     } else {
3528         node_rotate_one_internal (*n, angle, rme, rother, both);
3529     }
3531     me->pos = n->pos + NR::Point(rme);
3533     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3534         other->pos =  n->pos + NR::Point(rother);
3535     }
3537     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3538     // so here we just move all the knots without emitting move signals, for speed
3539     sp_node_update_handles(n, false);
3542 /**
3543  * Rotate selected nodes.
3544  */
3545 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3547     if (!nodepath || !nodepath->selected) return;
3549     if (g_list_length(nodepath->selected) == 1) {
3550        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3551         node_rotate_one (n, angle, which, screen);
3552     } else {
3553        // rotate as an object:
3555         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3556         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3557         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3558             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3559             box.expandTo (n->pos); // contain all selected nodes
3560         }
3562         gdouble rot;
3563         if (screen) {
3564             gdouble const zoom = nodepath->desktop->current_zoom();
3565             gdouble const zmove = angle / zoom;
3566             gdouble const r = NR::L2(box.max() - box.midpoint());
3567             rot = atan2(zmove, r);
3568         } else {
3569             rot = angle;
3570         }
3572         NR::Matrix t =
3573             NR::Matrix (NR::translate(-box.midpoint())) *
3574             NR::Matrix (NR::rotate(rot)) *
3575             NR::Matrix (NR::translate(box.midpoint()));
3577         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3578             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3579             n->pos *= t;
3580             n->n.pos *= t;
3581             n->p.pos *= t;
3582             sp_node_update_handles(n, false);
3583         }
3584     }
3586     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3589 /**
3590  * Scale one node.
3591  */
3592 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3594     bool both = false;
3595     Inkscape::NodePath::NodeSide *me, *other;
3597     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3598     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3600     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3601         me = &(n->p);
3602         other = &(n->n);
3603         n->code = NR_CURVETO;
3604     } else if (!n->p.other) {
3605         me = &(n->n);
3606         other = &(n->p);
3607         if (n->n.other)
3608             n->n.other->code = NR_CURVETO;
3609     } else {
3610         if (which > 0) { // right handle
3611             if (xn > xp) {
3612                 me = &(n->n);
3613                 other = &(n->p);
3614                 if (n->n.other)
3615                     n->n.other->code = NR_CURVETO;
3616             } else {
3617                 me = &(n->p);
3618                 other = &(n->n);
3619                 n->code = NR_CURVETO;
3620             }
3621         } else if (which < 0){ // left handle
3622             if (xn <= xp) {
3623                 me = &(n->n);
3624                 other = &(n->p);
3625                 if (n->n.other)
3626                     n->n.other->code = NR_CURVETO;
3627             } else {
3628                 me = &(n->p);
3629                 other = &(n->n);
3630                 n->code = NR_CURVETO;
3631             }
3632         } else { // both handles
3633             me = &(n->n);
3634             other = &(n->p);
3635             both = true;
3636             n->code = NR_CURVETO;
3637             if (n->n.other)
3638                 n->n.other->code = NR_CURVETO;
3639         }
3640     }
3642     Radial rme(me->pos - n->pos);
3643     Radial rother(other->pos - n->pos);
3645     rme.r += grow;
3646     if (rme.r < 0) rme.r = 0;
3647     if (rme.a == HUGE_VAL) {
3648         if (me->other) { // if direction is unknown, initialize it towards the next node
3649             Radial rme_next(me->other->pos - n->pos);
3650             rme.a = rme_next.a;
3651         } else { // if there's no next, initialize to 0
3652             rme.a = 0;
3653         }
3654     }
3655     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3656         rother.r += grow;
3657         if (rother.r < 0) rother.r = 0;
3658         if (rother.a == HUGE_VAL) {
3659             rother.a = rme.a + M_PI;
3660         }
3661     }
3663     me->pos = n->pos + NR::Point(rme);
3665     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3666         other->pos = n->pos + NR::Point(rother);
3667     }
3669     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3670     // so here we just move all the knots without emitting move signals, for speed
3671     sp_node_update_handles(n, false);
3674 /**
3675  * Scale selected nodes.
3676  */
3677 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3679     if (!nodepath || !nodepath->selected) return;
3681     if (g_list_length(nodepath->selected) == 1) {
3682         // scale handles of the single selected node
3683         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3684         node_scale_one (n, grow, which);
3685     } else {
3686         // scale nodes as an "object":
3688         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3689         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3690         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3691             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3692             box.expandTo (n->pos); // contain all selected nodes
3693         }
3695         double scale = (box.maxExtent() + grow)/box.maxExtent();
3697         NR::Matrix t =
3698             NR::Matrix (NR::translate(-box.midpoint())) *
3699             NR::Matrix (NR::scale(scale, scale)) *
3700             NR::Matrix (NR::translate(box.midpoint()));
3702         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3703             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3704             n->pos *= t;
3705             n->n.pos *= t;
3706             n->p.pos *= t;
3707             sp_node_update_handles(n, false);
3708         }
3709     }
3711     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3714 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3716     if (!nodepath) return;
3717     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3720 /**
3721  * Flip selected nodes horizontally/vertically.
3722  */
3723 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3725     if (!nodepath || !nodepath->selected) return;
3727     if (g_list_length(nodepath->selected) == 1) {
3728         // flip handles of the single selected node
3729         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3730         double temp = n->p.pos[axis];
3731         n->p.pos[axis] = n->n.pos[axis];
3732         n->n.pos[axis] = temp;
3733         sp_node_update_handles(n, false);
3734     } else {
3735         // scale nodes as an "object":
3737         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3738         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3739         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3740             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3741             box.expandTo (n->pos); // contain all selected nodes
3742         }
3744         NR::Matrix t =
3745             NR::Matrix (NR::translate(-box.midpoint())) *
3746             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3747             NR::Matrix (NR::translate(box.midpoint()));
3749         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3750             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3751             n->pos *= t;
3752             n->n.pos *= t;
3753             n->p.pos *= t;
3754             sp_node_update_handles(n, false);
3755         }
3756     }
3758     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3761 //-----------------------------------------------
3762 /**
3763  * Return new subpath under given nodepath.
3764  */
3765 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3767     g_assert(nodepath);
3768     g_assert(nodepath->desktop);
3770    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3772     s->nodepath = nodepath;
3773     s->closed = FALSE;
3774     s->nodes = NULL;
3775     s->first = NULL;
3776     s->last = NULL;
3778     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3779     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3780     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3782     return s;
3785 /**
3786  * Destroy nodes in subpath, then subpath itself.
3787  */
3788 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3790     g_assert(subpath);
3791     g_assert(subpath->nodepath);
3792     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3794     while (subpath->nodes) {
3795         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3796     }
3798     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3800     g_free(subpath);
3803 /**
3804  * Link head to tail in subpath.
3805  */
3806 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3808     g_assert(!sp->closed);
3809     g_assert(sp->last != sp->first);
3810     g_assert(sp->first->code == NR_MOVETO);
3812     sp->closed = TRUE;
3814     //Link the head to the tail
3815     sp->first->p.other = sp->last;
3816     sp->last->n.other  = sp->first;
3817     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3818     sp->first          = sp->last;
3820     //Remove the extra end node
3821     sp_nodepath_node_destroy(sp->last->n.other);
3824 /**
3825  * Open closed (loopy) subpath at node.
3826  */
3827 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3829     g_assert(sp->closed);
3830     g_assert(n->subpath == sp);
3831     g_assert(sp->first == sp->last);
3833     /* We create new startpoint, current node will become last one */
3835    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3836                                                 &n->pos, &n->pos, &n->n.pos);
3839     sp->closed        = FALSE;
3841     //Unlink to make a head and tail
3842     sp->first         = new_path;
3843     sp->last          = n;
3844     n->n.other        = NULL;
3845     new_path->p.other = NULL;
3848 /**
3849  * Returns area in triangle given by points; may be negative.
3850  */
3851 inline double
3852 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3854     return (p1[NR::X]*p2[NR::Y] + p1[NR::Y]*p3[NR::X] + p2[NR::X]*p3[NR::Y] - p2[NR::Y]*p3[NR::X] - p1[NR::Y]*p2[NR::X] - p1[NR::X]*p3[NR::Y]);
3857 /**
3858  * Return new node in subpath with given properties.
3859  * \param pos Position of node.
3860  * \param ppos Handle position in previous direction
3861  * \param npos Handle position in previous direction
3862  */
3863 Inkscape::NodePath::Node *
3864 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)
3866     g_assert(sp);
3867     g_assert(sp->nodepath);
3868     g_assert(sp->nodepath->desktop);
3870     if (nodechunk == NULL)
3871         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3873     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3875     n->subpath  = sp;
3877     if (type != Inkscape::NodePath::NODE_NONE) {
3878         // use the type from sodipodi:nodetypes
3879         n->type = type;
3880     } else {
3881         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3882             // points are (almost) collinear
3883             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3884                 // endnode, or a node with a retracted handle
3885                 n->type = Inkscape::NodePath::NODE_CUSP;
3886             } else {
3887                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3888             }
3889         } else {
3890             n->type = Inkscape::NodePath::NODE_CUSP;
3891         }
3892     }
3894     n->code     = code;
3895     n->selected = FALSE;
3896     n->pos      = *pos;
3897     n->p.pos    = *ppos;
3898     n->n.pos    = *npos;
3900     n->dragging_out = NULL;
3902     Inkscape::NodePath::Node *prev;
3903     if (next) {
3904         //g_assert(g_list_find(sp->nodes, next));
3905         prev = next->p.other;
3906     } else {
3907         prev = sp->last;
3908     }
3910     if (prev)
3911         prev->n.other = n;
3912     else
3913         sp->first = n;
3915     if (next)
3916         next->p.other = n;
3917     else
3918         sp->last = n;
3920     n->p.other = prev;
3921     n->n.other = next;
3923     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"));
3924     sp_knot_set_position(n->knot, pos, 0);
3926     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3927     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3928     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3929     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3930     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3931     sp_knot_update_ctrl(n->knot);
3933     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3934     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3935     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3936     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3937     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3938     sp_knot_show(n->knot);
3940     // We only create handle knots and lines on demand
3941     n->p.knot = NULL;
3942     n->p.line = NULL;
3943     n->n.knot = NULL;
3944     n->n.line = NULL;
3946     sp->nodes = g_list_prepend(sp->nodes, n);
3948     return n;
3951 /**
3952  * Destroy node and its knots, link neighbors in subpath.
3953  */
3954 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3956     g_assert(node);
3957     g_assert(node->subpath);
3958     g_assert(SP_IS_KNOT(node->knot));
3960    Inkscape::NodePath::SubPath *sp = node->subpath;
3962     if (node->selected) { // first, deselect
3963         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3964         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3965     }
3967     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3969     g_object_unref(G_OBJECT(node->knot));
3970     if (node->p.knot)
3971         g_object_unref(G_OBJECT(node->p.knot));
3972     if (node->n.knot)
3973         g_object_unref(G_OBJECT(node->n.knot));
3975     if (node->p.line)
3976         gtk_object_destroy(GTK_OBJECT(node->p.line));
3977     if (node->n.line)
3978         gtk_object_destroy(GTK_OBJECT(node->n.line));
3980     if (sp->nodes) { // there are others nodes on the subpath
3981         if (sp->closed) {
3982             if (sp->first == node) {
3983                 g_assert(sp->last == node);
3984                 sp->first = node->n.other;
3985                 sp->last = sp->first;
3986             }
3987             node->p.other->n.other = node->n.other;
3988             node->n.other->p.other = node->p.other;
3989         } else {
3990             if (sp->first == node) {
3991                 sp->first = node->n.other;
3992                 sp->first->code = NR_MOVETO;
3993             }
3994             if (sp->last == node) sp->last = node->p.other;
3995             if (node->p.other) node->p.other->n.other = node->n.other;
3996             if (node->n.other) node->n.other->p.other = node->p.other;
3997         }
3998     } else { // this was the last node on subpath
3999         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4000     }
4002     g_mem_chunk_free(nodechunk, node);
4005 /**
4006  * Returns one of the node's two sides.
4007  * \param which Indicates which side.
4008  * \return Pointer to previous node side if which==-1, next if which==1.
4009  */
4010 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4012     g_assert(node);
4014     switch (which) {
4015         case -1:
4016             return &node->p;
4017         case 1:
4018             return &node->n;
4019         default:
4020             break;
4021     }
4023     g_assert_not_reached();
4025     return NULL;
4028 /**
4029  * Return the other side of the node, given one of its sides.
4030  */
4031 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4033     g_assert(node);
4035     if (me == &node->p) return &node->n;
4036     if (me == &node->n) return &node->p;
4038     g_assert_not_reached();
4040     return NULL;
4043 /**
4044  * Return NRPathcode on the given side of the node.
4045  */
4046 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4048     g_assert(node);
4050     if (me == &node->p) {
4051         if (node->p.other) return (NRPathcode)node->code;
4052         return NR_MOVETO;
4053     }
4055     if (me == &node->n) {
4056         if (node->n.other) return (NRPathcode)node->n.other->code;
4057         return NR_MOVETO;
4058     }
4060     g_assert_not_reached();
4062     return NR_END;
4065 /**
4066  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4067  */
4068 Inkscape::NodePath::Node *
4069 sp_nodepath_get_node_by_index(int index)
4071     Inkscape::NodePath::Node *e = NULL;
4073     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4074     if (!nodepath) {
4075         return e;
4076     }
4078     //find segment
4079     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4081         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4082         int n = g_list_length(sp->nodes);
4083         if (sp->closed) {
4084             n++;
4085         }
4087         //if the piece belongs to this subpath grab it
4088         //otherwise move onto the next subpath
4089         if (index < n) {
4090             e = sp->first;
4091             for (int i = 0; i < index; ++i) {
4092                 e = e->n.other;
4093             }
4094             break;
4095         } else {
4096             if (sp->closed) {
4097                 index -= (n+1);
4098             } else {
4099                 index -= n;
4100             }
4101         }
4102     }
4104     return e;
4107 /**
4108  * Returns plain text meaning of node type.
4109  */
4110 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4112     unsigned retracted = 0;
4113     bool endnode = false;
4115     for (int which = -1; which <= 1; which += 2) {
4116         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4117         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4118             retracted ++;
4119         if (!side->other)
4120             endnode = true;
4121     }
4123     if (retracted == 0) {
4124         if (endnode) {
4125                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4126                 return _("end node");
4127         } else {
4128             switch (node->type) {
4129                 case Inkscape::NodePath::NODE_CUSP:
4130                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4131                     return _("cusp");
4132                 case Inkscape::NodePath::NODE_SMOOTH:
4133                     // TRANSLATORS: "smooth" is an adjective here
4134                     return _("smooth");
4135                 case Inkscape::NodePath::NODE_SYMM:
4136                     return _("symmetric");
4137             }
4138         }
4139     } else if (retracted == 1) {
4140         if (endnode) {
4141             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4142             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4143         } else {
4144             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4145         }
4146     } else {
4147         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4148     }
4150     return NULL;
4153 /**
4154  * Handles content of statusbar as long as node tool is active.
4155  */
4156 void
4157 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
4159     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");
4160     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4162     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4163     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4164     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4165     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4167     SPDesktop *desktop = NULL;
4168     if (nodepath) {
4169         desktop = nodepath->desktop;
4170     } else {
4171         desktop = SP_ACTIVE_DESKTOP;
4172     }
4174     SPEventContext *ec = desktop->event_context;
4175     if (!ec) return;
4176     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4177     if (!mc) return;
4179     if (selected_nodes == 0) {
4180         Inkscape::Selection *sel = desktop->selection;
4181         if (!sel || sel->isEmpty()) {
4182             mc->setF(Inkscape::NORMAL_MESSAGE,
4183                      _("Select a single object to edit its nodes or handles."));
4184         } else {
4185             if (nodepath) {
4186             mc->setF(Inkscape::NORMAL_MESSAGE,
4187                      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.",
4188                               "<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.",
4189                               total_nodes),
4190                      total_nodes);
4191             } else {
4192                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4193                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4194                 } else {
4195                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4196                 }
4197             }
4198         }
4199     } else if (nodepath && selected_nodes == 1) {
4200         mc->setF(Inkscape::NORMAL_MESSAGE,
4201                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4202                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4203                           total_nodes),
4204                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4205     } else {
4206         if (selected_subpaths > 1) {
4207             mc->setF(Inkscape::NORMAL_MESSAGE,
4208                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4209                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4210                               total_nodes),
4211                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4212         } else {
4213             mc->setF(Inkscape::NORMAL_MESSAGE,
4214                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4215                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4216                               total_nodes),
4217                      selected_nodes, total_nodes, when_selected);
4218         }
4219     }
4223 /*
4224   Local Variables:
4225   mode:c++
4226   c-file-style:"stroustrup"
4227   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4228   indent-tabs-mode:nil
4229   fill-column:99
4230   End:
4231 */
4232 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :