Code

restored pedro/work and added it to make.exclude
[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 "display/bezier-utils.h"
44 #include <vector>
45 #include <algorithm>
47 class NR::Matrix;
49 /// \todo
50 /// evil evil evil. FIXME: conflict of two different Path classes!
51 /// There is a conflict in the namespace between two classes named Path.
52 /// #include "sp-flowtext.h"
53 /// #include "sp-flowregion.h"
55 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
56 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
57 GType sp_flowregion_get_type (void);
58 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
59 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
60 GType sp_flowtext_get_type (void);
61 // end evil workaround
63 #include "helper/stlport.h"
66 /// \todo fixme: Implement these via preferences */
68 #define NODE_FILL          0xbfbfbf00
69 #define NODE_STROKE        0x000000ff
70 #define NODE_FILL_HI       0xff000000
71 #define NODE_STROKE_HI     0x000000ff
72 #define NODE_FILL_SEL      0x0000ffff
73 #define NODE_STROKE_SEL    0x000000ff
74 #define NODE_FILL_SEL_HI   0xff000000
75 #define NODE_STROKE_SEL_HI 0x000000ff
76 #define KNOT_FILL          0xffffffff
77 #define KNOT_STROKE        0x000000ff
78 #define KNOT_FILL_HI       0xff000000
79 #define KNOT_STROKE_HI     0x000000ff
81 static GMemChunk *nodechunk = NULL;
83 /* Creation from object */
85 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
86 static gchar *parse_nodetypes(gchar const *types, gint length);
88 /* Object updating */
90 static void stamp_repr(Inkscape::NodePath::Path *np);
91 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
92 static gchar *create_typestr(Inkscape::NodePath::Path *np);
94 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
96 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
98 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
100 /* Adjust handle placement, if the node or the other handle is moved */
101 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
102 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
104 /* Node event callbacks */
105 static void node_clicked(SPKnot *knot, guint state, gpointer data);
106 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
107 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
108 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
110 /* Handle event callbacks */
111 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
112 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
113 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
114 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
115 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
116 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
118 /* Constructors and destructors */
120 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
121 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
122 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
123 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
124 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
125                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
126 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
128 /* Helpers */
130 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
131 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
132 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
134 // active_node indicates mouseover node
135 static Inkscape::NodePath::Node *active_node = NULL;
137 /**
138  * \brief Creates new nodepath from item
139  */
140 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool show_handles)
142     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
144     /** \todo
145      * FIXME: remove this. We don't want to edit paths inside flowtext.
146      * Instead we will build our flowtext with cloned paths, so that the
147      * real paths are outside the flowtext and thus editable as usual.
148      */
149     if (SP_IS_FLOWTEXT(item)) {
150         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
151             if SP_IS_FLOWREGION(child) {
152                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
153                 if (grandchild && SP_IS_PATH(grandchild)) {
154                     item = SP_ITEM(grandchild);
155                     break;
156                 }
157             }
158         }
159     }
161     if (!SP_IS_PATH(item))
162         return NULL;
163     SPPath *path = SP_PATH(item);
164     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
165     if (curve == NULL)
166         return NULL;
168     NArtBpath *bpath = sp_curve_first_bpath(curve);
169     gint length = curve->end;
170     if (length == 0)
171         return NULL; // prevent crash for one-node paths
173     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
174     gchar *typestr = parse_nodetypes(nodetypes, length);
176     //Create new nodepath
177     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
178     if (!np)
179         return NULL;
181     // Set defaults
182     np->desktop     = desktop;
183     np->path        = path;
184     np->subpaths    = NULL;
185     np->selected    = NULL;
186     np->nodeContext = NULL; //Let the context that makes this set it
187     np->livarot_path = NULL;
188     np->local_change = 0;
189     np->show_handles = show_handles;
191     // we need to update item's transform from the repr here,
192     // because they may be out of sync when we respond
193     // to a change in repr by regenerating nodepath     --bb
194     sp_object_read_attr(SP_OBJECT(item), "transform");
196     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
197     np->d2i  = np->i2d.inverse();
198     np->repr = repr;
200     // create the subpath(s) from the bpath
201     NArtBpath *b = bpath;
202     while (b->code != NR_END) {
203         b = subpath_from_bpath(np, b, typestr + (b - bpath));
204     }
206     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
207     np->subpaths = g_list_reverse(np->subpaths);
209     g_free(typestr);
210     sp_curve_unref(curve);
212     // create the livarot representation from the same item
213     np->livarot_path = Path_for_item(item, true, true);
214     if (np->livarot_path)
215         np->livarot_path->ConvertWithBackData(0.01);
217     return np;
220 /**
221  * Destroys nodepath's subpaths, then itself, also tell context about it.
222  */
223 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
225     if (!np)  //soft fail, like delete
226         return;
228     while (np->subpaths) {
229         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
230     }
232     //Inform the context that made me, if any, that I am gone.
233     if (np->nodeContext)
234         np->nodeContext->nodepath = NULL;
236     g_assert(!np->selected);
238     if (np->livarot_path) {
239         delete np->livarot_path;
240         np->livarot_path = NULL;
241     }
243     np->desktop = NULL;
245     g_free(np);
249 /**
250  *  Return the node count of a given NodeSubPath.
251  */
252 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
254     if (!subpath)
255         return 0;
256     gint nodeCount = g_list_length(subpath->nodes);
257     return nodeCount;
260 /**
261  *  Return the node count of a given NodePath.
262  */
263 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
265     if (!np)
266         return 0;
267     gint nodeCount = 0;
268     for (GList *item = np->subpaths ; item ; item=item->next) {
269        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
270         nodeCount += g_list_length(subpath->nodes);
271     }
272     return nodeCount;
275 /**
276  *  Return the subpath count of a given NodePath.
277  */
278 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
280     if (!np)
281         return 0;
282     return g_list_length (np->subpaths);
285 /**
286  *  Return the selected node count of a given NodePath.
287  */
288 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
290     if (!np)
291         return 0;
292     return g_list_length (np->selected);
295 /**
296  *  Return the number of subpaths where nodes are selected in a given NodePath.
297  */
298 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
300     if (!np)
301         return 0;
302     if (!np->selected)
303         return 0;
304     if (!np->selected->next)
305         return 1;
306     gint count = 0;
307     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
308         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
309         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
310             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
311             if (node->selected) {
312                 count ++;
313                 break;
314             }
315         }
316     }
317     return count;
319  
320 /**
321  * Clean up a nodepath after editing.
322  *
323  * Currently we are deleting trivial subpaths.
324  */
325 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
327     GList *badSubPaths = NULL;
329     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
330     for (GList *l = nodepath->subpaths; l ; l=l->next) {
331        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
332        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
333             badSubPaths = g_list_append(badSubPaths, sp);
334     }
336     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
337     //also removes the subpath from nodepath->subpaths
338     for (GList *l = badSubPaths; l ; l=l->next) {
339        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
340         sp_nodepath_subpath_destroy(sp);
341     }
343     g_list_free(badSubPaths);
346 /**
347  * Create new nodepath from b, make it subpath of np.
348  * \param t The node type.
349  * \todo Fixme: t should be a proper type, rather than gchar
350  */
351 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
353     NR::Point ppos, pos, npos;
355     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
357     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
358     bool const closed = (b->code == NR_MOVETO);
360     pos = NR::Point(b->x3, b->y3) * np->i2d;
361     if (b[1].code == NR_CURVETO) {
362         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
363     } else {
364         npos = pos;
365     }
366     Inkscape::NodePath::Node *n;
367     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
368     g_assert(sp->first == n);
369     g_assert(sp->last  == n);
371     b++;
372     t++;
373     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
374         pos = NR::Point(b->x3, b->y3) * np->i2d;
375         if (b->code == NR_CURVETO) {
376             ppos = NR::Point(b->x2, b->y2) * np->i2d;
377         } else {
378             ppos = pos;
379         }
380         if (b[1].code == NR_CURVETO) {
381             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
382         } else {
383             npos = pos;
384         }
385         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
386         b++;
387         t++;
388     }
390     if (closed) sp_nodepath_subpath_close(sp);
392     return b;
395 /**
396  * Convert from sodipodi:nodetypes to new style type string.
397  */
398 static gchar *parse_nodetypes(gchar const *types, gint length)
400     g_assert(length > 0);
402     gchar *typestr = g_new(gchar, length + 1);
404     gint pos = 0;
406     if (types) {
407         for (gint i = 0; types[i] && ( i < length ); i++) {
408             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
409             if (types[i] != '\0') {
410                 switch (types[i]) {
411                     case 's':
412                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
413                         break;
414                     case 'z':
415                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
416                         break;
417                     case 'c':
418                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
419                         break;
420                     default:
421                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
422                         break;
423                 }
424             }
425         }
426     }
428     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
430     return typestr;
433 /**
434  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
435  * updated but repr is not (for speed). Used during curve and node drag.
436  */
437 static void update_object(Inkscape::NodePath::Path *np)
439     g_assert(np);
441     SPCurve *curve = create_curve(np);
443     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
445     sp_curve_unref(curve);
448 /**
449  * Update XML path node with data from path object.
450  */
451 static void update_repr_internal(Inkscape::NodePath::Path *np)
453     g_assert(np);
455     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
457     SPCurve *curve = create_curve(np);
458     gchar *typestr = create_typestr(np);
459     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
461     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
462         np->local_change++;
463         repr->setAttribute("d", svgpath);
464     }
466     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
467         np->local_change++;
468         repr->setAttribute("sodipodi:nodetypes", typestr);
469     }
471     g_free(svgpath);
472     g_free(typestr);
473     sp_curve_unref(curve);
476 /**
477  * Update XML path node with data from path object, commit changes forever.
478  */
479 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np)
481     update_repr_internal(np);
482     sp_document_done(sp_desktop_document(np->desktop));
484     if (np->livarot_path) {
485         delete np->livarot_path;
486         np->livarot_path = NULL;
487     }
489     if (np->path && SP_IS_ITEM(np->path)) {
490         np->livarot_path = Path_for_item (np->path, true, true);
491         if (np->livarot_path)
492             np->livarot_path->ConvertWithBackData(0.01);
493     }
496 /**
497  * Update XML path node with data from path object, commit changes with undo.
498  */
499 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key)
501     update_repr_internal(np);
502     sp_document_maybe_done(sp_desktop_document(np->desktop), key);
504     if (np->livarot_path) {
505         delete np->livarot_path;
506         np->livarot_path = NULL;
507     }
509     if (np->path && SP_IS_ITEM(np->path)) {
510         np->livarot_path = Path_for_item (np->path, true, true);
511         if (np->livarot_path)
512             np->livarot_path->ConvertWithBackData(0.01);
513     }
516 /**
517  * Make duplicate of path, replace corresponding XML node in tree, commit.
518  */
519 static void stamp_repr(Inkscape::NodePath::Path *np)
521     g_assert(np);
523     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
524     Inkscape::XML::Node *new_repr = old_repr->duplicate();
526     // remember the position of the item
527     gint pos = old_repr->position();
528     // remember parent
529     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
531     SPCurve *curve = create_curve(np);
532     gchar *typestr = create_typestr(np);
534     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
536     new_repr->setAttribute("d", svgpath);
537     new_repr->setAttribute("sodipodi:nodetypes", typestr);
539     // add the new repr to the parent
540     parent->appendChild(new_repr);
541     // move to the saved position
542     new_repr->setPosition(pos > 0 ? pos : 0);
544     sp_document_done(sp_desktop_document(np->desktop));
546     Inkscape::GC::release(new_repr);
547     g_free(svgpath);
548     g_free(typestr);
549     sp_curve_unref(curve);
552 /**
553  * Create curve from path.
554  */
555 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
557     SPCurve *curve = sp_curve_new();
559     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
560        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
561         sp_curve_moveto(curve,
562                         sp->first->pos * np->d2i);
563        Inkscape::NodePath::Node *n = sp->first->n.other;
564         while (n) {
565             NR::Point const end_pt = n->pos * np->d2i;
566             switch (n->code) {
567                 case NR_LINETO:
568                     sp_curve_lineto(curve, end_pt);
569                     break;
570                 case NR_CURVETO:
571                     sp_curve_curveto(curve,
572                                      n->p.other->n.pos * np->d2i,
573                                      n->p.pos * np->d2i,
574                                      end_pt);
575                     break;
576                 default:
577                     g_assert_not_reached();
578                     break;
579             }
580             if (n != sp->last) {
581                 n = n->n.other;
582             } else {
583                 n = NULL;
584             }
585         }
586         if (sp->closed) {
587             sp_curve_closepath(curve);
588         }
589     }
591     return curve;
594 /**
595  * Convert path type string to sodipodi:nodetypes style.
596  */
597 static gchar *create_typestr(Inkscape::NodePath::Path *np)
599     gchar *typestr = g_new(gchar, 32);
600     gint len = 32;
601     gint pos = 0;
603     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
604        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
606         if (pos >= len) {
607             typestr = g_renew(gchar, typestr, len + 32);
608             len += 32;
609         }
611         typestr[pos++] = 'c';
613        Inkscape::NodePath::Node *n;
614         n = sp->first->n.other;
615         while (n) {
616             gchar code;
618             switch (n->type) {
619                 case Inkscape::NodePath::NODE_CUSP:
620                     code = 'c';
621                     break;
622                 case Inkscape::NodePath::NODE_SMOOTH:
623                     code = 's';
624                     break;
625                 case Inkscape::NodePath::NODE_SYMM:
626                     code = 'z';
627                     break;
628                 default:
629                     g_assert_not_reached();
630                     code = '\0';
631                     break;
632             }
634             if (pos >= len) {
635                 typestr = g_renew(gchar, typestr, len + 32);
636                 len += 32;
637             }
639             typestr[pos++] = code;
641             if (n != sp->last) {
642                 n = n->n.other;
643             } else {
644                 n = NULL;
645             }
646         }
647     }
649     if (pos >= len) {
650         typestr = g_renew(gchar, typestr, len + 1);
651         len += 1;
652     }
654     typestr[pos++] = '\0';
656     return typestr;
659 /**
660  * Returns current path in context.
661  */
662 static Inkscape::NodePath::Path *sp_nodepath_current()
664     if (!SP_ACTIVE_DESKTOP) {
665         return NULL;
666     }
668     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
670     if (!SP_IS_NODE_CONTEXT(event_context)) {
671         return NULL;
672     }
674     return SP_NODE_CONTEXT(event_context)->nodepath;
679 /**
680  \brief Fills node and handle positions for three nodes, splitting line
681   marked by end at distance t.
682  */
683 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
685     g_assert(new_path != NULL);
686     g_assert(end      != NULL);
688     g_assert(end->p.other == new_path);
689    Inkscape::NodePath::Node *start = new_path->p.other;
690     g_assert(start);
692     if (end->code == NR_LINETO) {
693         new_path->type =Inkscape::NodePath::NODE_CUSP;
694         new_path->code = NR_LINETO;
695         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
696     } else {
697         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
698         new_path->code = NR_CURVETO;
699         gdouble s      = 1 - t;
700         for (int dim = 0; dim < 2; dim++) {
701             NR::Coord const f000 = start->pos[dim];
702             NR::Coord const f001 = start->n.pos[dim];
703             NR::Coord const f011 = end->p.pos[dim];
704             NR::Coord const f111 = end->pos[dim];
705             NR::Coord const f00t = s * f000 + t * f001;
706             NR::Coord const f01t = s * f001 + t * f011;
707             NR::Coord const f11t = s * f011 + t * f111;
708             NR::Coord const f0tt = s * f00t + t * f01t;
709             NR::Coord const f1tt = s * f01t + t * f11t;
710             NR::Coord const fttt = s * f0tt + t * f1tt;
711             start->n.pos[dim]    = f00t;
712             new_path->p.pos[dim] = f0tt;
713             new_path->pos[dim]   = fttt;
714             new_path->n.pos[dim] = f1tt;
715             end->p.pos[dim]      = f11t;
716         }
717     }
720 /**
721  * Adds new node on direct line between two nodes, activates handles of all
722  * three nodes.
723  */
724 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
726     g_assert(end);
727     g_assert(end->subpath);
728     g_assert(g_list_find(end->subpath->nodes, end));
730    Inkscape::NodePath::Node *start = end->p.other;
731     g_assert( start->n.other == end );
732    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
733                                                end,
734                                               Inkscape::NodePath::NODE_SMOOTH,
735                                                (NRPathcode)end->code,
736                                                &start->pos, &start->pos, &start->n.pos);
737     sp_nodepath_line_midpoint(newnode, end, t);
739     sp_node_update_handles(start);
740     sp_node_update_handles(newnode);
741     sp_node_update_handles(end);
743     return newnode;
746 /**
747 \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
748 */
749 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
751     g_assert(node);
752     g_assert(node->subpath);
753     g_assert(g_list_find(node->subpath->nodes, node));
755    Inkscape::NodePath::SubPath *sp = node->subpath;
756     Inkscape::NodePath::Path *np    = sp->nodepath;
758     if (sp->closed) {
759         sp_nodepath_subpath_open(sp, node);
760         return sp->first;
761     } else {
762         // no break for end nodes
763         if (node == sp->first) return NULL;
764         if (node == sp->last ) return NULL;
766         // create a new subpath
767        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
769         // duplicate the break node as start of the new subpath
770        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
772         while (node->n.other) { // copy the remaining nodes into the new subpath
773            Inkscape::NodePath::Node *n  = node->n.other;
774            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);
775             if (n->selected) {
776                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
777             }
778             sp_nodepath_node_destroy(n); // remove the point on the original subpath
779         }
781         return newnode;
782     }
785 /**
786  * Duplicate node and connect to neighbours.
787  */
788 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
790     g_assert(node);
791     g_assert(node->subpath);
792     g_assert(g_list_find(node->subpath->nodes, node));
794    Inkscape::NodePath::SubPath *sp = node->subpath;
796     NRPathcode code = (NRPathcode) node->code;
797     if (code == NR_MOVETO) { // if node is the endnode,
798         node->code = NR_LINETO; // new one is inserted before it, so change that to line
799     }
801     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
803     if (!node->n.other || !node->p.other) // if node is an endnode, select it
804         return node;
805     else
806         return newnode; // otherwise select the newly created node
809 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
811     node->p.pos = (node->pos + (node->pos - node->n.pos));
814 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
816     node->n.pos = (node->pos + (node->pos - node->p.pos));
819 /**
820  * Change line type at node, with side effects on neighbours.
821  */
822 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
824     g_assert(end);
825     g_assert(end->subpath);
826     g_assert(end->p.other);
828     if (end->code == static_cast< guint > ( code ) )
829         return;
831    Inkscape::NodePath::Node *start = end->p.other;
833     end->code = code;
835     if (code == NR_LINETO) {
836         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
837         if (end->n.other) {
838             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
839         }
840         sp_node_adjust_handle(start, -1);
841         sp_node_adjust_handle(end, 1);
842     } else {
843         NR::Point delta = end->pos - start->pos;
844         start->n.pos = start->pos + delta / 3;
845         end->p.pos = end->pos - delta / 3;
846         sp_node_adjust_handle(start, 1);
847         sp_node_adjust_handle(end, -1);
848     }
850     sp_node_update_handles(start);
851     sp_node_update_handles(end);
854 /**
855  * Change node type, and its handles accordingly.
856  */
857 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
859     g_assert(node);
860     g_assert(node->subpath);
862     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
863         return node;
865     if ((node->p.other != NULL) && (node->n.other != NULL)) {
866         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
867             type =Inkscape::NodePath::NODE_CUSP;
868         }
869     }
871     node->type = type;
873     if (node->type == Inkscape::NodePath::NODE_CUSP) {
874         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
875         node->knot->setSize (node->selected? 11 : 9);
876         sp_knot_update_ctrl(node->knot);
877     } else {
878         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
879         node->knot->setSize (node->selected? 9 : 7);
880         sp_knot_update_ctrl(node->knot);
881     }
883     // if one of handles is mouseovered, preserve its position
884     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
885         sp_node_adjust_handle(node, 1);
886     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
887         sp_node_adjust_handle(node, -1);
888     } else {
889         sp_node_adjust_handles(node);
890     }
892     sp_node_update_handles(node);
894     sp_nodepath_update_statusbar(node->subpath->nodepath);
896     return node;
899 /**
900  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
901  * adjacent segments from lines to curves.
902 */
903 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
905     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
906         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
907             // convert adjacent segment BEFORE to curve
908             node->code = NR_CURVETO;
909             NR::Point delta;
910             if (node->n.other != NULL)
911                 delta = node->n.other->pos - node->p.other->pos;
912             else
913                 delta = node->pos - node->p.other->pos;
914             node->p.pos = node->pos - delta / 4;
915             sp_node_update_handles(node);
916         }
918         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
919             // convert adjacent segment AFTER to curve
920             node->n.other->code = NR_CURVETO;
921             NR::Point delta;
922             if (node->p.other != NULL)
923                 delta = node->p.other->pos - node->n.other->pos;
924             else
925                 delta = node->pos - node->n.other->pos;
926             node->n.pos = node->pos - delta / 4;
927             sp_node_update_handles(node);
928         }
929     }
931     sp_nodepath_set_node_type (node, type);
934 /**
935  * Move node to point, and adjust its and neighbouring handles.
936  */
937 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
939     NR::Point delta = p - node->pos;
940     node->pos = p;
942     node->p.pos += delta;
943     node->n.pos += delta;
945     if (node->p.other) {
946         if (node->code == NR_LINETO) {
947             sp_node_adjust_handle(node, 1);
948             sp_node_adjust_handle(node->p.other, -1);
949         }
950     }
951     if (node->n.other) {
952         if (node->n.other->code == NR_LINETO) {
953             sp_node_adjust_handle(node, -1);
954             sp_node_adjust_handle(node->n.other, 1);
955         }
956     }
958     // this function is only called from batch movers that will update display at the end
959     // themselves, so here we just move all the knots without emitting move signals, for speed
960     sp_node_update_handles(node, false);
963 /**
964  * Call sp_node_moveto() for node selection and handle possible snapping.
965  */
966 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
967                                             bool const snap = true)
969     NR::Coord best = NR_HUGE;
970     NR::Point delta(dx, dy);
971     NR::Point best_pt = delta;
973     if (snap) {
974         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
975         
976         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
977             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
978             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
979             if (s.getDistance() < best) {
980                 best = s.getDistance();
981                 best_pt = s.getPoint() - n->pos;
982             }
983         }
984     }
986     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
987        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
988         sp_node_moveto(n, n->pos + best_pt);
989     }
991     // do not update repr here so that node dragging is acceptably fast
992     update_object(nodepath);
995 /**
996 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
997 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
998 near x = 0.
999  */
1000 double
1001 sculpt_profile (double x, double alpha, guint profile)
1003     if (x >= 1)
1004         return 0;
1005     if (x <= 0)
1006         return 1;
1008     switch (profile) {
1009         case SCULPT_PROFILE_LINEAR:
1010         return 1 - x;
1011         case SCULPT_PROFILE_BELL:
1012         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1013         case SCULPT_PROFILE_ELLIPTIC:
1014         return sqrt(1 - x*x);
1015     }
1017     return 1;
1020 double
1021 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1023     // extremely primitive for now, don't have time to look for the real one
1024     double lower = NR::L2(b - a);
1025     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1026     return (lower + upper)/2;
1029 void
1030 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1032     n->pos = n->origin + delta;
1033     n->n.pos = n->n.origin + delta_n;
1034     n->p.pos = n->p.origin + delta_p;
1035     sp_node_adjust_handles(n);
1036     sp_node_update_handles(n, false);
1039 /**
1040  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1041  * on how far they are from the dragged node n.
1042  */
1043 static void 
1044 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1046     g_assert (n);
1047     g_assert (nodepath);
1048     g_assert (n->subpath->nodepath == nodepath);
1050     double pressure = n->knot->pressure;
1051     if (pressure == 0)
1052         pressure = 0.5; // default
1053     pressure = CLAMP (pressure, 0.2, 0.8);
1055     // map pressure to alpha = 1/5 ... 5
1056     double alpha = 1 - 2 * fabs(pressure - 0.5);
1057     if (pressure > 0.5)
1058         alpha = 1/alpha;
1060     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1062     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1063         // Only one subpath has selected nodes:
1064         // use linear mode, where the distance from n to node being dragged is calculated along the path
1066         double n_sel_range = 0, p_sel_range = 0;
1067         guint n_nodes = 0, p_nodes = 0;
1068         guint n_sel_nodes = 0, p_sel_nodes = 0;
1070         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1071         {
1072             double n_range = 0, p_range = 0;
1073             bool n_going = true, p_going = true;
1074             Inkscape::NodePath::Node *n_node = n;
1075             Inkscape::NodePath::Node *p_node = n;
1076             do {
1077                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1078                 if (n_node && n_going)
1079                     n_node = n_node->n.other;
1080                 if (n_node == NULL) {
1081                     n_going = false;
1082                 } else {
1083                     n_nodes ++;
1084                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1085                     if (n_node->selected) {
1086                         n_sel_nodes ++;
1087                         n_sel_range = n_range;
1088                     }
1089                     if (n_node == p_node) {
1090                         n_going = false;
1091                         p_going = false;
1092                     }
1093                 }
1094                 if (p_node && p_going)
1095                     p_node = p_node->p.other;
1096                 if (p_node == NULL) {
1097                     p_going = false;
1098                 } else {
1099                     p_nodes ++;
1100                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1101                     if (p_node->selected) {
1102                         p_sel_nodes ++;
1103                         p_sel_range = p_range;
1104                     }
1105                     if (p_node == n_node) {
1106                         n_going = false;
1107                         p_going = false;
1108                     }
1109                 }
1110             } while (n_going || p_going);
1111         }
1113         // Second pass: actually move nodes in this subpath
1114         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1115         {
1116             double n_range = 0, p_range = 0;
1117             bool n_going = true, p_going = true;
1118             Inkscape::NodePath::Node *n_node = n;
1119             Inkscape::NodePath::Node *p_node = n;
1120             do {
1121                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1122                 if (n_node && n_going)
1123                     n_node = n_node->n.other;
1124                 if (n_node == NULL) {
1125                     n_going = false;
1126                 } else {
1127                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1128                     if (n_node->selected) {
1129                         sp_nodepath_move_node_and_handles (n_node, 
1130                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1131                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1132                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1133                     }
1134                     if (n_node == p_node) {
1135                         n_going = false;
1136                         p_going = false;
1137                     }
1138                 }
1139                 if (p_node && p_going)
1140                     p_node = p_node->p.other;
1141                 if (p_node == NULL) {
1142                     p_going = false;
1143                 } else {
1144                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1145                     if (p_node->selected) {
1146                         sp_nodepath_move_node_and_handles (p_node, 
1147                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1148                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1149                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1150                     }
1151                     if (p_node == n_node) {
1152                         n_going = false;
1153                         p_going = false;
1154                     }
1155                 }
1156             } while (n_going || p_going);
1157         }
1159     } else {
1160         // Multiple subpaths have selected nodes:
1161         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1162         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1163         // fix the pear-like shape when sculpting e.g. a ring
1165         // First pass: calculate range
1166         gdouble direct_range = 0;
1167         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1168             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1169             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1170                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1171                 if (node->selected) {
1172                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1173                 }
1174             }
1175         }
1177         // Second pass: actually move nodes
1178         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1179             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1180             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1181                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1182                 if (node->selected) {
1183                     if (direct_range > 1e-6) {
1184                         sp_nodepath_move_node_and_handles (node,
1185                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1186                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1187                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1188                     } else {
1189                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1190                     }
1192                 }
1193             }
1194         }
1195     }
1197     // do not update repr here so that node dragging is acceptably fast
1198     update_object(nodepath);
1202 /**
1203  * Move node selection to point, adjust its and neighbouring handles,
1204  * handle possible snapping, and commit the change with possible undo.
1205  */
1206 void
1207 sp_node_selected_move(gdouble dx, gdouble dy)
1209     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1210     if (!nodepath) return;
1212     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1214     if (dx == 0) {
1215         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1216     } else if (dy == 0) {
1217         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1218     } else {
1219         sp_nodepath_update_repr(nodepath);
1220     }
1223 /**
1224  * Move node selection off screen and commit the change.
1225  */
1226 void
1227 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1229     // borrowed from sp_selection_move_screen in selection-chemistry.c
1230     // we find out the current zoom factor and divide deltas by it
1231     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1233     gdouble zoom = desktop->current_zoom();
1234     gdouble zdx = dx / zoom;
1235     gdouble zdy = dy / zoom;
1237     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1238     if (!nodepath) return;
1240     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1242     if (dx == 0) {
1243         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1244     } else if (dy == 0) {
1245         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1246     } else {
1247         sp_nodepath_update_repr(nodepath);
1248     }
1251 /** If they don't yet exist, creates knot and line for the given side of the node */
1252 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1254     if (!side->knot) {
1255         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"));
1257         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1258         side->knot->setSize (7);
1259         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1260         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1261         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1262         sp_knot_update_ctrl(side->knot);
1264         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1265         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1266         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1267         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1268         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1269         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1270     }
1272     if (!side->line) {
1273         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1274                                         SP_TYPE_CTRLLINE, NULL);
1275     }
1278 /**
1279  * Ensure the given handle of the node is visible/invisible, update its screen position
1280  */
1281 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1283     g_assert(node != NULL);
1285    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1286     NRPathcode code = sp_node_path_code_from_side(node, side);
1288     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1290     if (show_handle) {
1291         if (!side->knot) { // No handle knot at all
1292             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1293             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1294             side->knot->pos = side->pos;
1295             if (side->knot->item) 
1296                 SP_CTRL(side->knot->item)->moveto(side->pos);
1297             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1298             sp_knot_show(side->knot);
1299         } else {
1300             if (side->knot->pos != side->pos) { // only if it's really moved
1301                 if (fire_move_signals) {
1302                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1303                 } else {
1304                     sp_knot_moveto(side->knot, &side->pos);
1305                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1306                 }
1307             }
1308             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1309                 sp_knot_show(side->knot);
1310             }
1311         }
1312         sp_canvas_item_show(side->line);
1313     } else {
1314         if (side->knot) {
1315             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1316                 sp_knot_hide(side->knot);
1317             }
1318         }
1319         if (side->line) {
1320             sp_canvas_item_hide(side->line);
1321         }
1322     }
1325 /**
1326  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1327  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1328  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1329  * updated; otherwise, just move the knots silently (used in batch moves).
1330  */
1331 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1333     g_assert(node != NULL);
1335     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1336         sp_knot_show(node->knot);
1337     }
1339     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1340         if (fire_move_signals)
1341             sp_knot_set_position(node->knot, &node->pos, 0);
1342         else 
1343             sp_knot_moveto(node->knot, &node->pos);
1344     }
1346     gboolean show_handles = node->selected;
1347     if (node->p.other != NULL) {
1348         if (node->p.other->selected) show_handles = TRUE;
1349     }
1350     if (node->n.other != NULL) {
1351         if (node->n.other->selected) show_handles = TRUE;
1352     }
1354     if (node->subpath->nodepath->show_handles == false)
1355         show_handles = FALSE;
1357     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1358     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1361 /**
1362  * Call sp_node_update_handles() for all nodes on subpath.
1363  */
1364 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1366     g_assert(subpath != NULL);
1368     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1369         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1370     }
1373 /**
1374  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1375  */
1376 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1378     g_assert(nodepath != NULL);
1380     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1381         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1382     }
1385 void
1386 sp_nodepath_show_handles(bool show)
1388     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1389     if (nodepath == NULL) return;
1391     nodepath->show_handles = show;
1392     sp_nodepath_update_handles(nodepath);
1395 /**
1396  * Adds all selected nodes in nodepath to list.
1397  */
1398 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1400     StlConv<Node *>::list(l, selected);
1401 /// \todo this adds a copying, rework when the selection becomes a stl list
1404 /**
1405  * Align selected nodes on the specified axis.
1406  */
1407 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1409     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1410         return;
1411     }
1413     if ( !nodepath->selected->next ) { // only one node selected
1414         return;
1415     }
1416    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1417     NR::Point dest(pNode->pos);
1418     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1419         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1420         if (pNode) {
1421             dest[axis] = pNode->pos[axis];
1422             sp_node_moveto(pNode, dest);
1423         }
1424     }
1426     sp_nodepath_update_repr(nodepath);
1429 /// Helper struct.
1430 struct NodeSort
1432    Inkscape::NodePath::Node *_node;
1433     NR::Coord _coord;
1434     /// \todo use vectorof pointers instead of calling copy ctor
1435     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1436         _node(node), _coord(node->pos[axis])
1437     {}
1439 };
1441 static bool operator<(NodeSort const &a, NodeSort const &b)
1443     return (a._coord < b._coord);
1446 /**
1447  * Distribute selected nodes on the specified axis.
1448  */
1449 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1451     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1452         return;
1453     }
1455     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1456         return;
1457     }
1459    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1460     std::vector<NodeSort> sorted;
1461     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1462         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1463         if (pNode) {
1464             NodeSort n(pNode, axis);
1465             sorted.push_back(n);
1466             //dest[axis] = pNode->pos[axis];
1467             //sp_node_moveto(pNode, dest);
1468         }
1469     }
1470     std::sort(sorted.begin(), sorted.end());
1471     unsigned int len = sorted.size();
1472     //overall bboxes span
1473     float dist = (sorted.back()._coord -
1474                   sorted.front()._coord);
1475     //new distance between each bbox
1476     float step = (dist) / (len - 1);
1477     float pos = sorted.front()._coord;
1478     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1479           it < sorted.end();
1480           it ++ )
1481     {
1482         NR::Point dest((*it)._node->pos);
1483         dest[axis] = pos;
1484         sp_node_moveto((*it)._node, dest);
1485         pos += step;
1486     }
1488     sp_nodepath_update_repr(nodepath);
1492 /**
1493  * Call sp_nodepath_line_add_node() for all selected segments.
1494  */
1495 void
1496 sp_node_selected_add_node(void)
1498     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1499     if (!nodepath) {
1500         return;
1501     }
1503     GList *nl = NULL;
1505     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1506        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1507         g_assert(t->selected);
1508         if (t->p.other && t->p.other->selected) {
1509             nl = g_list_prepend(nl, t);
1510         }
1511     }
1513     while (nl) {
1514        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1515        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1516         sp_nodepath_node_select(n, TRUE, FALSE);
1517         nl = g_list_remove(nl, t);
1518     }
1520     /** \todo fixme: adjust ? */
1521     sp_nodepath_update_handles(nodepath);
1523     sp_nodepath_update_repr(nodepath);
1525     sp_nodepath_update_statusbar(nodepath);
1528 /**
1529  * Select segment nearest to point
1530  */
1531 void
1532 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1534     if (!nodepath) {
1535         return;
1536     }
1538     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1540     //find segment to segment
1541     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1543     gboolean force = FALSE;
1544     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1545         force = TRUE;
1546     }
1547     sp_nodepath_node_select(e, (gboolean) toggle, force);
1548     if (e->p.other)
1549         sp_nodepath_node_select(e->p.other, TRUE, force);
1551     sp_nodepath_update_handles(nodepath);
1553     sp_nodepath_update_statusbar(nodepath);
1556 /**
1557  * Add a node nearest to point
1558  */
1559 void
1560 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1562     if (!nodepath) {
1563         return;
1564     }
1566     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1568     //find segment to split
1569     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1571     //don't know why but t seems to flip for lines
1572     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1573         position.t = 1.0 - position.t;
1574     }
1575     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1576     sp_nodepath_node_select(n, FALSE, TRUE);
1578     /* fixme: adjust ? */
1579     sp_nodepath_update_handles(nodepath);
1581     sp_nodepath_update_repr(nodepath);
1583     sp_nodepath_update_statusbar(nodepath);
1586 /*
1587  * Adjusts a segment so that t moves by a certain delta for dragging
1588  * converts lines to curves
1589  *
1590  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1591  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1592  */
1593 void
1594 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1596     /* feel good is an arbitrary parameter that distributes the delta between handles
1597      * if t of the drag point is less than 1/6 distance form the endpoint only
1598      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1599      */
1600     double feel_good;
1601     if (t <= 1.0 / 6.0)
1602         feel_good = 0;
1603     else if (t <= 0.5)
1604         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1605     else if (t <= 5.0 / 6.0)
1606         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1607     else
1608         feel_good = 1;
1610     //if we're dragging a line convert it to a curve
1611     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1612         sp_nodepath_set_line_type(e, NR_CURVETO);
1613     }
1615     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1616     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1617     e->p.other->n.pos += offsetcoord0;
1618     e->p.pos += offsetcoord1;
1620     // adjust handles of adjacent nodes where necessary
1621     sp_node_adjust_handle(e,1);
1622     sp_node_adjust_handle(e->p.other,-1);
1624     sp_nodepath_update_handles(e->subpath->nodepath);
1626     update_object(e->subpath->nodepath);
1628     sp_nodepath_update_statusbar(e->subpath->nodepath);
1632 /**
1633  * Call sp_nodepath_break() for all selected segments.
1634  */
1635 void sp_node_selected_break()
1637     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1638     if (!nodepath) return;
1640     GList *temp = NULL;
1641     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1642        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1643        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1644         if (nn == NULL) continue; // no break, no new node
1645         temp = g_list_prepend(temp, nn);
1646     }
1648     if (temp) {
1649         sp_nodepath_deselect(nodepath);
1650     }
1651     for (GList *l = temp; l != NULL; l = l->next) {
1652         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1653     }
1655     sp_nodepath_update_handles(nodepath);
1657     sp_nodepath_update_repr(nodepath);
1660 /**
1661  * Duplicate the selected node(s).
1662  */
1663 void sp_node_selected_duplicate()
1665     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1666     if (!nodepath) {
1667         return;
1668     }
1670     GList *temp = NULL;
1671     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1672        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1673        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1674         if (nn == NULL) continue; // could not duplicate
1675         temp = g_list_prepend(temp, nn);
1676     }
1678     if (temp) {
1679         sp_nodepath_deselect(nodepath);
1680     }
1681     for (GList *l = temp; l != NULL; l = l->next) {
1682         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1683     }
1685     sp_nodepath_update_handles(nodepath);
1687     sp_nodepath_update_repr(nodepath);
1690 /**
1691  *  Join two nodes by merging them into one.
1692  */
1693 void sp_node_selected_join()
1695     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1696     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1698     if (g_list_length(nodepath->selected) != 2) {
1699         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1700         return;
1701     }
1703    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1704    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1706     g_assert(a != b);
1707     g_assert(a->p.other || a->n.other);
1708     g_assert(b->p.other || b->n.other);
1710     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1711         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1712         return;
1713     }
1715     /* a and b are endpoints */
1717     NR::Point c;
1718     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1719         c = a->pos;
1720     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1721         c = b->pos;
1722     } else {
1723         c = (a->pos + b->pos) / 2;
1724     }
1726     if (a->subpath == b->subpath) {
1727        Inkscape::NodePath::SubPath *sp = a->subpath;
1728         sp_nodepath_subpath_close(sp);
1729         sp_node_moveto (sp->first, c);
1731         sp_nodepath_update_handles(sp->nodepath);
1732         sp_nodepath_update_repr(nodepath);
1733         return;
1734     }
1736     /* a and b are separate subpaths */
1737    Inkscape::NodePath::SubPath *sa = a->subpath;
1738    Inkscape::NodePath::SubPath *sb = b->subpath;
1739     NR::Point p;
1740    Inkscape::NodePath::Node *n;
1741     NRPathcode code;
1742     if (a == sa->first) {
1743         p = sa->first->n.pos;
1744         code = (NRPathcode)sa->first->n.other->code;
1745        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1746         n = sa->last;
1747         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1748         n = n->p.other;
1749         while (n) {
1750             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1751             n = n->p.other;
1752             if (n == sa->first) n = NULL;
1753         }
1754         sp_nodepath_subpath_destroy(sa);
1755         sa = t;
1756     } else if (a == sa->last) {
1757         p = sa->last->p.pos;
1758         code = (NRPathcode)sa->last->code;
1759         sp_nodepath_node_destroy(sa->last);
1760     } else {
1761         code = NR_END;
1762         g_assert_not_reached();
1763     }
1765     if (b == sb->first) {
1766         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1767         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1768             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1769         }
1770     } else if (b == sb->last) {
1771         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1772         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1773             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1774         }
1775     } else {
1776         g_assert_not_reached();
1777     }
1778     /* and now destroy sb */
1780     sp_nodepath_subpath_destroy(sb);
1782     sp_nodepath_update_handles(sa->nodepath);
1784     sp_nodepath_update_repr(nodepath);
1786     sp_nodepath_update_statusbar(nodepath);
1789 /**
1790  *  Join two nodes by adding a segment between them.
1791  */
1792 void sp_node_selected_join_segment()
1794     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1795     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1797     if (g_list_length(nodepath->selected) != 2) {
1798         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1799         return;
1800     }
1802    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1803    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1805     g_assert(a != b);
1806     g_assert(a->p.other || a->n.other);
1807     g_assert(b->p.other || b->n.other);
1809     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1810         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1811         return;
1812     }
1814     if (a->subpath == b->subpath) {
1815        Inkscape::NodePath::SubPath *sp = a->subpath;
1817         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1818         sp->closed = TRUE;
1820         sp->first->p.other = sp->last;
1821         sp->last->n.other  = sp->first;
1823         sp_node_handle_mirror_p_to_n(sp->last);
1824         sp_node_handle_mirror_n_to_p(sp->first);
1826         sp->first->code = sp->last->code;
1827         sp->first       = sp->last;
1829         sp_nodepath_update_handles(sp->nodepath);
1831         sp_nodepath_update_repr(nodepath);
1833         return;
1834     }
1836     /* a and b are separate subpaths */
1837    Inkscape::NodePath::SubPath *sa = a->subpath;
1838    Inkscape::NodePath::SubPath *sb = b->subpath;
1840    Inkscape::NodePath::Node *n;
1841     NR::Point p;
1842     NRPathcode code;
1843     if (a == sa->first) {
1844         code = (NRPathcode) sa->first->n.other->code;
1845        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1846         n = sa->last;
1847         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1848         for (n = n->p.other; n != NULL; n = n->p.other) {
1849             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1850         }
1851         sp_nodepath_subpath_destroy(sa);
1852         sa = t;
1853     } else if (a == sa->last) {
1854         code = (NRPathcode)sa->last->code;
1855     } else {
1856         code = NR_END;
1857         g_assert_not_reached();
1858     }
1860     if (b == sb->first) {
1861         n = sb->first;
1862         sp_node_handle_mirror_p_to_n(sa->last);
1863         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1864         sp_node_handle_mirror_n_to_p(sa->last);
1865         for (n = n->n.other; n != NULL; n = n->n.other) {
1866             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1867         }
1868     } else if (b == sb->last) {
1869         n = sb->last;
1870         sp_node_handle_mirror_p_to_n(sa->last);
1871         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1872         sp_node_handle_mirror_n_to_p(sa->last);
1873         for (n = n->p.other; n != NULL; n = n->p.other) {
1874             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1875         }
1876     } else {
1877         g_assert_not_reached();
1878     }
1879     /* and now destroy sb */
1881     sp_nodepath_subpath_destroy(sb);
1883     sp_nodepath_update_handles(sa->nodepath);
1885     sp_nodepath_update_repr(nodepath);
1888 /**
1889  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1890  */
1891 void sp_node_delete_preserve(GList *nodes_to_delete)
1893     GSList *nodepaths = NULL;
1894     
1895     while (nodes_to_delete) {
1896         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1897         Inkscape::NodePath::SubPath *sp = node->subpath;
1898         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1899         Inkscape::NodePath::Node *sample_cursor = NULL;
1900         Inkscape::NodePath::Node *sample_end = NULL;
1901         Inkscape::NodePath::Node *delete_cursor = node;
1902         bool just_delete = false;
1903         
1904         //find the start of this contiguous selection
1905         //move left to the first node that is not selected
1906         //or the start of the non-closed path
1907         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1908             delete_cursor = curr;
1909         }
1911         //just delete at the beginning of an open path
1912         if (!delete_cursor->p.other) {
1913             sample_cursor = delete_cursor;
1914             just_delete = true;
1915         } else {
1916             sample_cursor = delete_cursor->p.other;
1917         }
1918         
1919         //calculate points for each segment
1920         int rate = 5;
1921         float period = 1.0 / rate;
1922         std::vector<NR::Point> data;
1923         if (!just_delete) {
1924             data.push_back(sample_cursor->pos);
1925             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1926                 //just delete at the end of an open path
1927                 if (!sp->closed && curr->n.other == sp->last) {
1928                     just_delete = true;
1929                     break;
1930                 }
1931                 
1932                 //sample points on the contiguous selected segment
1933                 NR::Point *bez;
1934                 bez = new NR::Point [4];
1935                 bez[0] = curr->pos;
1936                 bez[1] = curr->n.pos;
1937                 bez[2] = curr->n.other->p.pos;
1938                 bez[3] = curr->n.other->pos;
1939                 for (int i=1; i<rate; i++) {
1940                     gdouble t = i * period;
1941                     NR::Point p = bezier_pt(3, bez, t);
1942                     data.push_back(p);
1943                 }
1944                 data.push_back(curr->n.other->pos);
1946                 sample_end = curr->n.other;
1947                 //break if we've come full circle or hit the end of the selection
1948                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1949                     break;
1950                 }
1951             }
1952         }
1954         if (!just_delete) {
1955             //calculate the best fitting single segment and adjust the endpoints
1956             NR::Point *adata;
1957             adata = new NR::Point [data.size()];
1958             copy(data.begin(), data.end(), adata);
1959             
1960             NR::Point *bez;
1961             bez = new NR::Point [4];
1962             //would decreasing error create a better fitting approximation?
1963             gdouble error = 1.0;
1964             gint ret;
1965             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1967             //adjust endpoints
1968             sample_cursor->n.pos = bez[1];
1969             sample_end->p.pos = bez[2];
1970         }
1971        
1972         //destroy this contiguous selection
1973         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
1974             Inkscape::NodePath::Node *temp = delete_cursor;
1975             if (delete_cursor->n.other == delete_cursor) {
1976                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
1977                 delete_cursor = NULL; 
1978             } else {
1979                 delete_cursor = delete_cursor->n.other;
1980             }
1981             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
1982             sp_nodepath_node_destroy(temp);
1983         }
1985         sp_nodepath_update_handles(nodepath);
1987         if (!g_slist_find(nodepaths, nodepath))
1988             nodepaths = g_slist_prepend (nodepaths, nodepath);
1989     }
1991     for (GSList *i = nodepaths; i; i = i->next) {
1992         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
1993         // different nodepaths will give us one undo event per nodepath
1994         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
1996         // if the entire nodepath is removed, delete the selected object.
1997         if (nodepath->subpaths == NULL ||
1998             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
1999             //at least 2
2000             sp_nodepath_get_node_count(nodepath) < 2) {
2001             SPDocument *document = sp_desktop_document (nodepath->desktop);
2002             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2003             //delete this nodepath's object, not the entire selection! (though at this time, this
2004             //does not matter)
2005             sp_selection_delete();
2006             sp_document_done (document);
2007         } else {
2008             sp_nodepath_update_repr(nodepath);
2009             sp_nodepath_update_statusbar(nodepath);
2010         }
2011     }
2013     g_slist_free (nodepaths);
2016 /**
2017  * Delete one or more selected nodes.
2018  */
2019 void sp_node_selected_delete()
2021     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2022     if (!nodepath) return;
2023     if (!nodepath->selected) return;
2025     /** \todo fixme: do it the right way */
2026     while (nodepath->selected) {
2027        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2028         sp_nodepath_node_destroy(node);
2029     }
2032     //clean up the nodepath (such as for trivial subpaths)
2033     sp_nodepath_cleanup(nodepath);
2035     sp_nodepath_update_handles(nodepath);
2037     // if the entire nodepath is removed, delete the selected object.
2038     if (nodepath->subpaths == NULL ||
2039         sp_nodepath_get_node_count(nodepath) < 2) {
2040         SPDocument *document = sp_desktop_document (nodepath->desktop);
2041         sp_selection_delete();
2042         sp_document_done (document);
2043         return;
2044     }
2046     sp_nodepath_update_repr(nodepath);
2048     sp_nodepath_update_statusbar(nodepath);
2051 /**
2052  * Delete one or more segments between two selected nodes.
2053  * This is the code for 'split'.
2054  */
2055 void
2056 sp_node_selected_delete_segment(void)
2058    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2059    Inkscape::NodePath::Node *curr, *next;     //Iterators
2061     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2062     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2064     if (g_list_length(nodepath->selected) != 2) {
2065         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2066                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2067         return;
2068     }
2070     //Selected nodes, not inclusive
2071    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2072    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2074     if ( ( a==b)                       ||  //same node
2075          (a->subpath  != b->subpath )  ||  //not the same path
2076          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2077          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2078     {
2079         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2080                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2081         return;
2082     }
2084     //###########################################
2085     //# BEGIN EDITS
2086     //###########################################
2087     //##################################
2088     //# CLOSED PATH
2089     //##################################
2090     if (a->subpath->closed) {
2093         gboolean reversed = FALSE;
2095         //Since we can go in a circle, we need to find the shorter distance.
2096         //  a->b or b->a
2097         start = end = NULL;
2098         int distance    = 0;
2099         int minDistance = 0;
2100         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2101             if (curr==b) {
2102                 //printf("a to b:%d\n", distance);
2103                 start = a;//go from a to b
2104                 end   = b;
2105                 minDistance = distance;
2106                 //printf("A to B :\n");
2107                 break;
2108             }
2109             distance++;
2110         }
2112         //try again, the other direction
2113         distance = 0;
2114         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2115             if (curr==a) {
2116                 //printf("b to a:%d\n", distance);
2117                 if (distance < minDistance) {
2118                     start    = b;  //we go from b to a
2119                     end      = a;
2120                     reversed = TRUE;
2121                     //printf("B to A\n");
2122                 }
2123                 break;
2124             }
2125             distance++;
2126         }
2129         //Copy everything from 'end' to 'start' to a new subpath
2130        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2131         for (curr=end ; curr ; curr=curr->n.other) {
2132             NRPathcode code = (NRPathcode) curr->code;
2133             if (curr == end)
2134                 code = NR_MOVETO;
2135             sp_nodepath_node_new(t, NULL,
2136                                  (Inkscape::NodePath::NodeType)curr->type, code,
2137                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2138             if (curr == start)
2139                 break;
2140         }
2141         sp_nodepath_subpath_destroy(a->subpath);
2144     }
2148     //##################################
2149     //# OPEN PATH
2150     //##################################
2151     else {
2153         //We need to get the direction of the list between A and B
2154         //Can we walk from a to b?
2155         start = end = NULL;
2156         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2157             if (curr==b) {
2158                 start = a;  //did it!  we go from a to b
2159                 end   = b;
2160                 //printf("A to B\n");
2161                 break;
2162             }
2163         }
2164         if (!start) {//didn't work?  let's try the other direction
2165             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2166                 if (curr==a) {
2167                     start = b;  //did it!  we go from b to a
2168                     end   = a;
2169                     //printf("B to A\n");
2170                     break;
2171                 }
2172             }
2173         }
2174         if (!start) {
2175             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2176                                                      _("Cannot find path between nodes."));
2177             return;
2178         }
2182         //Copy everything after 'end' to a new subpath
2183        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2184         for (curr=end ; curr ; curr=curr->n.other) {
2185             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2186                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2187         }
2189         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2190         for (curr = start->n.other ; curr  ; curr=next) {
2191             next = curr->n.other;
2192             sp_nodepath_node_destroy(curr);
2193         }
2195     }
2196     //###########################################
2197     //# END EDITS
2198     //###########################################
2200     //clean up the nodepath (such as for trivial subpaths)
2201     sp_nodepath_cleanup(nodepath);
2203     sp_nodepath_update_handles(nodepath);
2205     sp_nodepath_update_repr(nodepath);
2207     sp_nodepath_update_statusbar(nodepath);
2210 /**
2211  * Call sp_nodepath_set_line() for all selected segments.
2212  */
2213 void
2214 sp_node_selected_set_line_type(NRPathcode code)
2216     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2217     if (nodepath == NULL) return;
2219     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2220        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2221         g_assert(n->selected);
2222         if (n->p.other && n->p.other->selected) {
2223             sp_nodepath_set_line_type(n, code);
2224         }
2225     }
2227     sp_nodepath_update_repr(nodepath);
2230 /**
2231  * Call sp_nodepath_convert_node_type() for all selected nodes.
2232  */
2233 void
2234 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2236     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2237     if (nodepath == NULL) return;
2239     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2240         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2241     }
2243     sp_nodepath_update_repr(nodepath);
2246 /**
2247  * Change select status of node, update its own and neighbour handles.
2248  */
2249 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2251     node->selected = selected;
2253     if (selected) {
2254         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2255         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2256         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2257         sp_knot_update_ctrl(node->knot);
2258     } else {
2259         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2260         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2261         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2262         sp_knot_update_ctrl(node->knot);
2263     }
2265     sp_node_update_handles(node);
2266     if (node->n.other) sp_node_update_handles(node->n.other);
2267     if (node->p.other) sp_node_update_handles(node->p.other);
2270 /**
2271 \brief Select a node
2272 \param node     The node to select
2273 \param incremental   If true, add to selection, otherwise deselect others
2274 \param override   If true, always select this node, otherwise toggle selected status
2275 */
2276 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2278     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2280     if (incremental) {
2281         if (override) {
2282             if (!g_list_find(nodepath->selected, node)) {
2283                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2284             }
2285             sp_node_set_selected(node, TRUE);
2286         } else { // toggle
2287             if (node->selected) {
2288                 g_assert(g_list_find(nodepath->selected, node));
2289                 nodepath->selected = g_list_remove(nodepath->selected, node);
2290             } else {
2291                 g_assert(!g_list_find(nodepath->selected, node));
2292                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2293             }
2294             sp_node_set_selected(node, !node->selected);
2295         }
2296     } else {
2297         sp_nodepath_deselect(nodepath);
2298         nodepath->selected = g_list_prepend(nodepath->selected, node);
2299         sp_node_set_selected(node, TRUE);
2300     }
2302     sp_nodepath_update_statusbar(nodepath);
2306 /**
2307 \brief Deselect all nodes in the nodepath
2308 */
2309 void
2310 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2312     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2314     while (nodepath->selected) {
2315         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2316         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2317     }
2318     sp_nodepath_update_statusbar(nodepath);
2321 /**
2322 \brief Select or invert selection of all nodes in the nodepath
2323 */
2324 void
2325 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2327     if (!nodepath) return;
2329     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2330        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2331         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2332            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2333            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2334         }
2335     }
2338 /**
2339  * If nothing selected, does the same as sp_nodepath_select_all();
2340  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2341  * (i.e., similar to "select all in layer", with the "selected" subpaths
2342  * being treated as "layers" in the path).
2343  */
2344 void
2345 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2347     if (!nodepath) return;
2349     if (g_list_length (nodepath->selected) == 0) {
2350         sp_nodepath_select_all (nodepath, invert);
2351         return;
2352     }
2354     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2355     GSList *subpaths = NULL;
2357     for (GList *l = copy; l != NULL; l = l->next) {
2358         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2359         Inkscape::NodePath::SubPath *subpath = n->subpath;
2360         if (!g_slist_find (subpaths, subpath))
2361             subpaths = g_slist_prepend (subpaths, subpath);
2362     }
2364     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2365         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2366         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2367             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2368             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2369         }
2370     }
2372     g_slist_free (subpaths);
2373     g_list_free (copy);
2376 /**
2377  * \brief Select the node after the last selected; if none is selected,
2378  * select the first within path.
2379  */
2380 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2382     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2384    Inkscape::NodePath::Node *last = NULL;
2385     if (nodepath->selected) {
2386         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2387            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2388             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2389             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2390                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2391                 if (node->selected) {
2392                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2393                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2394                             if (spl->next) { // there's a next subpath
2395                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2396                                 last = subpath_next->first;
2397                             } else if (spl->prev) { // there's a previous subpath
2398                                 last = NULL; // to be set later to the first node of first subpath
2399                             } else {
2400                                 last = node->n.other;
2401                             }
2402                         } else {
2403                             last = node->n.other;
2404                         }
2405                     } else {
2406                         if (node->n.other) {
2407                             last = node->n.other;
2408                         } else {
2409                             if (spl->next) { // there's a next subpath
2410                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2411                                 last = subpath_next->first;
2412                             } else if (spl->prev) { // there's a previous subpath
2413                                 last = NULL; // to be set later to the first node of first subpath
2414                             } else {
2415                                 last = (Inkscape::NodePath::Node *) subpath->first;
2416                             }
2417                         }
2418                     }
2419                 }
2420             }
2421         }
2422         sp_nodepath_deselect(nodepath);
2423     }
2425     if (last) { // there's at least one more node after selected
2426         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2427     } else { // no more nodes, select the first one in first subpath
2428        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2429         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2430     }
2433 /**
2434  * \brief Select the node before the first selected; if none is selected,
2435  * select the last within path
2436  */
2437 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2439     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2441    Inkscape::NodePath::Node *last = NULL;
2442     if (nodepath->selected) {
2443         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2444            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2445             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2446                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2447                 if (node->selected) {
2448                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2449                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2450                             if (spl->prev) { // there's a prev subpath
2451                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2452                                 last = subpath_prev->last;
2453                             } else if (spl->next) { // there's a next subpath
2454                                 last = NULL; // to be set later to the last node of last subpath
2455                             } else {
2456                                 last = node->p.other;
2457                             }
2458                         } else {
2459                             last = node->p.other;
2460                         }
2461                     } else {
2462                         if (node->p.other) {
2463                             last = node->p.other;
2464                         } else {
2465                             if (spl->prev) { // there's a prev subpath
2466                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2467                                 last = subpath_prev->last;
2468                             } else if (spl->next) { // there's a next subpath
2469                                 last = NULL; // to be set later to the last node of last subpath
2470                             } else {
2471                                 last = (Inkscape::NodePath::Node *) subpath->last;
2472                             }
2473                         }
2474                     }
2475                 }
2476             }
2477         }
2478         sp_nodepath_deselect(nodepath);
2479     }
2481     if (last) { // there's at least one more node before selected
2482         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2483     } else { // no more nodes, select the last one in last subpath
2484         GList *spl = g_list_last(nodepath->subpaths);
2485        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2486         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2487     }
2490 /**
2491  * \brief Select all nodes that are within the rectangle.
2492  */
2493 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2495     if (!incremental) {
2496         sp_nodepath_deselect(nodepath);
2497     }
2499     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2500        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2501         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2502            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2504             if (b.contains(node->pos)) {
2505                 sp_nodepath_node_select(node, TRUE, TRUE);
2506             }
2507         }
2508     }
2512 void
2513 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2515     g_assert (n);
2516     g_assert (nodepath);
2517     g_assert (n->subpath->nodepath == nodepath);
2519     if (g_list_length (nodepath->selected) == 0) {
2520         if (grow > 0) {
2521             sp_nodepath_node_select(n, TRUE, TRUE);
2522         }
2523         return;
2524     }
2526     if (g_list_length (nodepath->selected) == 1) {
2527         if (grow < 0) {
2528             sp_nodepath_deselect (nodepath);
2529             return;
2530         }
2531     }
2533         double n_sel_range = 0, p_sel_range = 0;
2534             Inkscape::NodePath::Node *farthest_n_node = n;
2535             Inkscape::NodePath::Node *farthest_p_node = n;
2537         // Calculate ranges
2538         {
2539             double n_range = 0, p_range = 0;
2540             bool n_going = true, p_going = true;
2541             Inkscape::NodePath::Node *n_node = n;
2542             Inkscape::NodePath::Node *p_node = n;
2543             do {
2544                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2545                 if (n_node && n_going)
2546                     n_node = n_node->n.other;
2547                 if (n_node == NULL) {
2548                     n_going = false;
2549                 } else {
2550                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2551                     if (n_node->selected) {
2552                         n_sel_range = n_range;
2553                         farthest_n_node = n_node;
2554                     }
2555                     if (n_node == p_node) {
2556                         n_going = false;
2557                         p_going = false;
2558                     }
2559                 }
2560                 if (p_node && p_going)
2561                     p_node = p_node->p.other;
2562                 if (p_node == NULL) {
2563                     p_going = false;
2564                 } else {
2565                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2566                     if (p_node->selected) {
2567                         p_sel_range = p_range;
2568                         farthest_p_node = p_node;
2569                     }
2570                     if (p_node == n_node) {
2571                         n_going = false;
2572                         p_going = false;
2573                     }
2574                 }
2575             } while (n_going || p_going);
2576         }
2578     if (grow > 0) {
2579         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2580                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2581         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2582                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2583         }
2584     } else {
2585         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2586                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2587         } else if (farthest_p_node && farthest_p_node->selected) {
2588                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2589         }
2590     }
2593 void
2594 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2596     g_assert (n);
2597     g_assert (nodepath);
2598     g_assert (n->subpath->nodepath == nodepath);
2600     if (g_list_length (nodepath->selected) == 0) {
2601         if (grow > 0) {
2602             sp_nodepath_node_select(n, TRUE, TRUE);
2603         }
2604         return;
2605     }
2607     if (g_list_length (nodepath->selected) == 1) {
2608         if (grow < 0) {
2609             sp_nodepath_deselect (nodepath);
2610             return;
2611         }
2612     }
2614     Inkscape::NodePath::Node *farthest_selected = NULL;
2615     double farthest_dist = 0;
2617     Inkscape::NodePath::Node *closest_unselected = NULL;
2618     double closest_dist = NR_HUGE;
2620     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2621        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2622         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2623            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2624            if (node == n)
2625                continue;
2626            if (node->selected) {
2627                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2628                    farthest_dist = NR::L2(node->pos - n->pos);
2629                    farthest_selected = node;
2630                }
2631            } else {
2632                if (NR::L2(node->pos - n->pos) < closest_dist) {
2633                    closest_dist = NR::L2(node->pos - n->pos);
2634                    closest_unselected = node;
2635                }
2636            }
2637         }
2638     }
2640     if (grow > 0) {
2641         if (closest_unselected) {
2642             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2643         }
2644     } else {
2645         if (farthest_selected) {
2646             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2647         }
2648     }
2652 /**
2653 \brief  Saves all nodes' and handles' current positions in their origin members
2654 */
2655 void
2656 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2658     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2659        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2660         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2661            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2662            n->origin = n->pos;
2663            n->p.origin = n->p.pos;
2664            n->n.origin = n->n.pos;
2665         }
2666     }
2667
2669 /**
2670 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2671 */
2672 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2674     if (!nodepath->selected) {
2675         return NULL;
2676     }
2678     GList *r = NULL;
2679     guint i = 0;
2680     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2681        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2682         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2683            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2684             i++;
2685             if (node->selected) {
2686                 r = g_list_append(r, GINT_TO_POINTER(i));
2687             }
2688         }
2689     }
2690     return r;
2693 /**
2694 \brief  Restores selection by selecting nodes whose positions are in the list
2695 */
2696 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2698     sp_nodepath_deselect(nodepath);
2700     guint i = 0;
2701     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2702        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2703         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2704            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2705             i++;
2706             if (g_list_find(r, GINT_TO_POINTER(i))) {
2707                 sp_nodepath_node_select(node, TRUE, TRUE);
2708             }
2709         }
2710     }
2714 /**
2715 \brief Adjusts handle according to node type and line code.
2716 */
2717 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2719     double len, otherlen, linelen;
2721     g_assert(node);
2723    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2724    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2726     /** \todo fixme: */
2727     if (me->other == NULL) return;
2728     if (other->other == NULL) return;
2730     /* I have line */
2732     NRPathcode mecode, ocode;
2733     if (which_adjust == 1) {
2734         mecode = (NRPathcode)me->other->code;
2735         ocode = (NRPathcode)node->code;
2736     } else {
2737         mecode = (NRPathcode)node->code;
2738         ocode = (NRPathcode)other->other->code;
2739     }
2741     if (mecode == NR_LINETO) return;
2743     /* I am curve */
2745     if (other->other == NULL) return;
2747     /* Other has line */
2749     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2751     NR::Point delta;
2752     if (ocode == NR_LINETO) {
2753         /* other is lineto, we are either smooth or symm */
2754        Inkscape::NodePath::Node *othernode = other->other;
2755         len = NR::L2(me->pos - node->pos);
2756         delta = node->pos - othernode->pos;
2757         linelen = NR::L2(delta);
2758         if (linelen < 1e-18) 
2759             return;
2760         me->pos = node->pos + (len / linelen)*delta;
2761         return;
2762     }
2764     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2766         me->pos = 2 * node->pos - other->pos;
2767         return;
2768     }
2770     /* We are smooth */
2772     len = NR::L2(me->pos - node->pos);
2773     delta = other->pos - node->pos;
2774     otherlen = NR::L2(delta);
2775     if (otherlen < 1e-18) return;
2777     me->pos = node->pos - (len / otherlen) * delta;
2780 /**
2781  \brief Adjusts both handles according to node type and line code
2782  */
2783 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2785     g_assert(node);
2787     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2789     /* we are either smooth or symm */
2791     if (node->p.other == NULL) return;
2793     if (node->n.other == NULL) return;
2795     if (node->code == NR_LINETO) {
2796         if (node->n.other->code == NR_LINETO) return;
2797         sp_node_adjust_handle(node, 1);
2798         return;
2799     }
2801     if (node->n.other->code == NR_LINETO) {
2802         if (node->code == NR_LINETO) return;
2803         sp_node_adjust_handle(node, -1);
2804         return;
2805     }
2807     /* both are curves */
2808     NR::Point const delta( node->n.pos - node->p.pos );
2810     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2811         node->p.pos = node->pos - delta / 2;
2812         node->n.pos = node->pos + delta / 2;
2813         return;
2814     }
2816     /* We are smooth */
2817     double plen = NR::L2(node->p.pos - node->pos);
2818     if (plen < 1e-18) return;
2819     double nlen = NR::L2(node->n.pos - node->pos);
2820     if (nlen < 1e-18) return;
2821     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2822     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2825 /**
2826  * Node event callback.
2827  */
2828 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2830     gboolean ret = FALSE;
2831     switch (event->type) {
2832         case GDK_ENTER_NOTIFY:
2833             active_node = n;
2834             break;
2835         case GDK_LEAVE_NOTIFY:
2836             active_node = NULL;
2837             break;
2838         case GDK_KEY_PRESS:
2839             switch (get_group0_keyval (&event->key)) {
2840                 case GDK_space:
2841                     if (event->key.state & GDK_BUTTON1_MASK) {
2842                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2843                         stamp_repr(nodepath);
2844                         ret = TRUE;
2845                     }
2846                     break;
2847                 case GDK_Page_Up:
2848                     if (event->key.state & GDK_CONTROL_MASK) {
2849                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2850                     } else {
2851                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2852                     }
2853                     break;
2854                 case GDK_Page_Down:
2855                     if (event->key.state & GDK_CONTROL_MASK) {
2856                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2857                     } else {
2858                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2859                     }
2860                     break;
2861                 default:
2862                     break;
2863             }
2864             break;
2865         default:
2866             break;
2867     }
2869     return ret;
2872 /**
2873  * Handle keypress on node; directly called.
2874  */
2875 gboolean node_key(GdkEvent *event)
2877     Inkscape::NodePath::Path *np;
2879     // there is no way to verify nodes so set active_node to nil when deleting!!
2880     if (active_node == NULL) return FALSE;
2882     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2883         gint ret = FALSE;
2884         switch (get_group0_keyval (&event->key)) {
2885             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2886             case GDK_BackSpace:
2887                 np = active_node->subpath->nodepath;
2888                 sp_nodepath_node_destroy(active_node);
2889                 sp_nodepath_update_repr(np);
2890                 active_node = NULL;
2891                 ret = TRUE;
2892                 break;
2893             case GDK_c:
2894                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2895                 ret = TRUE;
2896                 break;
2897             case GDK_s:
2898                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2899                 ret = TRUE;
2900                 break;
2901             case GDK_y:
2902                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2903                 ret = TRUE;
2904                 break;
2905             case GDK_b:
2906                 sp_nodepath_node_break(active_node);
2907                 ret = TRUE;
2908                 break;
2909         }
2910         return ret;
2911     }
2912     return FALSE;
2915 /**
2916  * Mouseclick on node callback.
2917  */
2918 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2920    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2922     if (state & GDK_CONTROL_MASK) {
2923         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2925         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2926             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2927                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2928             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2929                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2930             } else {
2931                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2932             }
2933             sp_nodepath_update_repr(nodepath);
2934             sp_nodepath_update_statusbar(nodepath);
2936         } else { //ctrl+alt+click: delete node
2937             GList *node_to_delete = NULL;
2938             node_to_delete = g_list_append(node_to_delete, n);
2939             sp_node_delete_preserve(node_to_delete);
2940         }
2942     } else {
2943         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2944     }
2947 /**
2948  * Mouse grabbed node callback.
2949  */
2950 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2952    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2954     if (!n->selected) {
2955         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2956     }
2958     sp_nodepath_remember_origins (n->subpath->nodepath);
2961 /**
2962  * Mouse ungrabbed node callback.
2963  */
2964 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2966    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2968    n->dragging_out = NULL;
2970    sp_nodepath_update_repr(n->subpath->nodepath);
2973 /**
2974  * The point on a line, given by its angle, closest to the given point.
2975  * \param p  A point.
2976  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2977  * \param closest  Pointer to the point struct where the result is stored.
2978  * \todo FIXME: use dot product perhaps?
2979  */
2980 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2982     if (a == HUGE_VAL) { // vertical
2983         *closest = NR::Point(0, (*p)[NR::Y]);
2984     } else {
2985         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2986         (*closest)[NR::Y] = a * (*closest)[NR::X];
2987     }
2990 /**
2991  * Distance from the point to a line given by its angle.
2992  * \param p  A point.
2993  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2994  */
2995 static double point_line_distance(NR::Point *p, double a)
2997     NR::Point c;
2998     point_line_closest(p, a, &c);
2999     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]));
3002 /**
3003  * Callback for node "request" signal.
3004  * \todo fixme: This goes to "moved" event? (lauris)
3005  */
3006 static gboolean
3007 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3009     double yn, xn, yp, xp;
3010     double an, ap, na, pa;
3011     double d_an, d_ap, d_na, d_pa;
3012     gboolean collinear = FALSE;
3013     NR::Point c;
3014     NR::Point pr;
3016    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3018    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3019    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3021        NR::Point mouse = (*p);
3023        if (!n->dragging_out) {
3024            // This is the first drag-out event; find out which handle to drag out
3025            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3026            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3028            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3029                return FALSE;
3031            Inkscape::NodePath::NodeSide *opposite;
3032            if (appr_p > appr_n) { // closer to p
3033                n->dragging_out = &n->p;
3034                opposite = &n->n;
3035                n->code = NR_CURVETO;
3036            } else if (appr_p < appr_n) { // closer to n
3037                n->dragging_out = &n->n;
3038                opposite = &n->p;
3039                n->n.other->code = NR_CURVETO;
3040            } else { // p and n nodes are the same
3041                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3042                    n->dragging_out = &n->p;
3043                    opposite = &n->n;
3044                    n->code = NR_CURVETO;
3045                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3046                    n->dragging_out = &n->n;
3047                    opposite = &n->p;
3048                    n->n.other->code = NR_CURVETO;
3049                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3050                    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);
3051                    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);
3052                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3053                        n->dragging_out = &n->n;
3054                        opposite = &n->p;
3055                        n->n.other->code = NR_CURVETO;
3056                    } else { // closer to other's n handle
3057                        n->dragging_out = &n->p;
3058                        opposite = &n->n;
3059                        n->code = NR_CURVETO;
3060                    }
3061                }
3062            }
3064            // if there's another handle, make sure the one we drag out starts parallel to it
3065            if (opposite->pos != n->pos) {
3066                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3067            }
3069            // knots might not be created yet!
3070            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3071            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3072        }
3074        // pass this on to the handle-moved callback
3075        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3076        sp_node_update_handles(n);
3077        return TRUE;
3078    }
3080     if (state & GDK_CONTROL_MASK) { // constrained motion
3082         // calculate relative distances of handles
3083         // n handle:
3084         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3085         xn = n->n.pos[NR::X] - n->pos[NR::X];
3086         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3087         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3088             if (n->n.other) { // if there is the next point
3089                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3090                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3091                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3092             }
3093         }
3094         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3095         if (yn < 0) { xn = -xn; yn = -yn; }
3097         // p handle:
3098         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3099         xp = n->p.pos[NR::X] - n->pos[NR::X];
3100         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3101         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3102             if (n->p.other) {
3103                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3104                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3105                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3106             }
3107         }
3108         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3109         if (yp < 0) { xp = -xp; yp = -yp; }
3111         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3112             // sliding on handles, only if at least one of the handles is non-vertical
3113             // (otherwise it's the same as ctrl+drag anyway)
3115             // calculate angles of the handles
3116             if (xn == 0) {
3117                 if (yn == 0) { // no handle, consider it the continuation of the other one
3118                     an = 0;
3119                     collinear = TRUE;
3120                 }
3121                 else an = 0; // vertical; set the angle to horizontal
3122             } else an = yn/xn;
3124             if (xp == 0) {
3125                 if (yp == 0) { // no handle, consider it the continuation of the other one
3126                     ap = an;
3127                 }
3128                 else ap = 0; // vertical; set the angle to horizontal
3129             } else  ap = yp/xp;
3131             if (collinear) an = ap;
3133             // angles of the perpendiculars; HUGE_VAL means vertical
3134             if (an == 0) na = HUGE_VAL; else na = -1/an;
3135             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3137             // mouse point relative to the node's original pos
3138             pr = (*p) - n->origin;
3140             // distances to the four lines (two handles and two perpendiculars)
3141             d_an = point_line_distance(&pr, an);
3142             d_na = point_line_distance(&pr, na);
3143             d_ap = point_line_distance(&pr, ap);
3144             d_pa = point_line_distance(&pr, pa);
3146             // find out which line is the closest, save its closest point in c
3147             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3148                 point_line_closest(&pr, an, &c);
3149             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3150                 point_line_closest(&pr, ap, &c);
3151             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3152                 point_line_closest(&pr, na, &c);
3153             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3154                 point_line_closest(&pr, pa, &c);
3155             }
3157             // move the node to the closest point
3158             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3159                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3160                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3162         } else {  // constraining to hor/vert
3164             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3165                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3166             } else { // snap to vert
3167                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3168             }
3169         }
3170     } else { // move freely
3171         if (state & GDK_MOD1_MASK) { // sculpt
3172             sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3173         } else {
3174             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3175                                         (*p)[NR::X] - n->pos[NR::X],
3176                                         (*p)[NR::Y] - n->pos[NR::Y],
3177                                         (state & GDK_SHIFT_MASK) == 0);
3178         }
3179     }
3181     n->subpath->nodepath->desktop->scroll_to_point(p);
3183     return TRUE;
3186 /**
3187  * Node handle clicked callback.
3188  */
3189 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3191    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3193     if (state & GDK_CONTROL_MASK) { // "delete" handle
3194         if (n->p.knot == knot) {
3195             n->p.pos = n->pos;
3196         } else if (n->n.knot == knot) {
3197             n->n.pos = n->pos;
3198         }
3199         sp_node_update_handles(n);
3200         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3201         sp_nodepath_update_repr(nodepath);
3202         sp_nodepath_update_statusbar(nodepath);
3204     } else { // just select or add to selection, depending in Shift
3205         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3206     }
3209 /**
3210  * Node handle grabbed callback.
3211  */
3212 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3214    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3216     if (!n->selected) {
3217         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3218     }
3220     // remember the origin point of the handle
3221     if (n->p.knot == knot) {
3222         n->p.origin_radial = n->p.pos - n->pos;
3223     } else if (n->n.knot == knot) {
3224         n->n.origin_radial = n->n.pos - n->pos;
3225     } else {
3226         g_assert_not_reached();
3227     }
3231 /**
3232  * Node handle ungrabbed callback.
3233  */
3234 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3236    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3238     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3239     if (n->p.knot == knot) {
3240         n->p.origin_radial.a = 0;
3241         sp_knot_set_position(knot, &n->p.pos, state);
3242     } else if (n->n.knot == knot) {
3243         n->n.origin_radial.a = 0;
3244         sp_knot_set_position(knot, &n->n.pos, state);
3245     } else {
3246         g_assert_not_reached();
3247     }
3249     sp_nodepath_update_repr(n->subpath->nodepath);
3252 /**
3253  * Node handle "request" signal callback.
3254  */
3255 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3257     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3259     Inkscape::NodePath::NodeSide *me, *opposite;
3260     gint which;
3261     if (n->p.knot == knot) {
3262         me = &n->p;
3263         opposite = &n->n;
3264         which = -1;
3265     } else if (n->n.knot == knot) {
3266         me = &n->n;
3267         opposite = &n->p;
3268         which = 1;
3269     } else {
3270         me = opposite = NULL;
3271         which = 0;
3272         g_assert_not_reached();
3273     }
3275     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3277     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3279     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3280         /* We are smooth node adjacent with line */
3281         NR::Point const delta = *p - n->pos;
3282         NR::Coord const len = NR::L2(delta);
3283         Inkscape::NodePath::Node *othernode = opposite->other;
3284         NR::Point const ndelta = n->pos - othernode->pos;
3285         NR::Coord const linelen = NR::L2(ndelta);
3286         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3287             NR::Coord const scal = dot(delta, ndelta) / linelen;
3288             (*p) = n->pos + (scal / linelen) * ndelta;
3289         }
3290         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3291     } else {
3292         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3293     }
3295     sp_node_adjust_handle(n, -which);
3297     return FALSE;
3300 /**
3301  * Node handle moved callback.
3302  */
3303 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3305    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3307    Inkscape::NodePath::NodeSide *me;
3308    Inkscape::NodePath::NodeSide *other;
3309     if (n->p.knot == knot) {
3310         me = &n->p;
3311         other = &n->n;
3312     } else if (n->n.knot == knot) {
3313         me = &n->n;
3314         other = &n->p;
3315     } else {
3316         me = NULL;
3317         other = NULL;
3318         g_assert_not_reached();
3319     }
3321     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3322     Radial rme(me->pos - n->pos);
3323     Radial rother(other->pos - n->pos);
3324     Radial rnew(*p - n->pos);
3326     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3327         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3328         /* 0 interpreted as "no snapping". */
3330         // The closest PI/snaps angle, starting from zero.
3331         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3332         if (me->origin_radial.a == HUGE_VAL) {
3333             // ortho doesn't exist: original handle was zero length.
3334             rnew.a = a_snapped;
3335         } else {
3336             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3337              * its opposite and perpendiculars). */
3338             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3340             // Snap to the closest.
3341             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3342                        ? a_snapped
3343                        : a_ortho );
3344         }
3345     }
3347     if (state & GDK_MOD1_MASK) {
3348         // lock handle length
3349         rnew.r = me->origin_radial.r;
3350     }
3352     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3353         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3354         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3355         rother.a += rnew.a - rme.a;
3356         other->pos = NR::Point(rother) + n->pos;
3357         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3358         sp_knot_set_position(other->knot, &other->pos, 0);
3359     }
3361     me->pos = NR::Point(rnew) + n->pos;
3362     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3364     // this is what sp_knot_set_position does, but without emitting the signal:
3365     // we cannot emit a "moved" signal because we're now processing it
3366     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3368     knot->desktop->set_coordinate_status(me->pos);
3370     update_object(n->subpath->nodepath);
3372     /* status text */
3373     SPDesktop *desktop = n->subpath->nodepath->desktop;
3374     if (!desktop) return;
3375     SPEventContext *ec = desktop->event_context;
3376     if (!ec) return;
3377     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3378     if (!mc) return;
3380     double degrees = 180 / M_PI * rnew.a;
3381     if (degrees > 180) degrees -= 360;
3382     if (degrees < -180) degrees += 360;
3383     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3384         degrees = angle_to_compass (degrees);
3386     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3388     mc->setF(Inkscape::NORMAL_MESSAGE,
3389          _("<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);
3391     g_string_free(length, TRUE);
3394 /**
3395  * Node handle event callback.
3396  */
3397 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3399     gboolean ret = FALSE;
3400     switch (event->type) {
3401         case GDK_KEY_PRESS:
3402             switch (get_group0_keyval (&event->key)) {
3403                 case GDK_space:
3404                     if (event->key.state & GDK_BUTTON1_MASK) {
3405                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3406                         stamp_repr(nodepath);
3407                         ret = TRUE;
3408                     }
3409                     break;
3410                 default:
3411                     break;
3412             }
3413             break;
3414         default:
3415             break;
3416     }
3418     return ret;
3421 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3422                                  Radial &rme, Radial &rother, gboolean const both)
3424     rme.a += angle;
3425     if ( both
3426          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3427          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3428     {
3429         rother.a += angle;
3430     }
3433 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3434                                         Radial &rme, Radial &rother, gboolean const both)
3436     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3438     gdouble r;
3439     if ( both
3440          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3441          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3442     {
3443         r = MAX(rme.r, rother.r);
3444     } else {
3445         r = rme.r;
3446     }
3448     gdouble const weird_angle = atan2(norm_angle, r);
3449 /* Bulia says norm_angle is just the visible distance that the
3450  * object's end must travel on the screen.  Left as 'angle' for want of
3451  * a better name.*/
3453     rme.a += weird_angle;
3454     if ( both
3455          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3456          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3457     {
3458         rother.a += weird_angle;
3459     }
3462 /**
3463  * Rotate one node.
3464  */
3465 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3467     Inkscape::NodePath::NodeSide *me, *other;
3468     bool both = false;
3470     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3471     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3473     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3474         me = &(n->p);
3475         other = &(n->n);
3476     } else if (!n->p.other) {
3477         me = &(n->n);
3478         other = &(n->p);
3479     } else {
3480         if (which > 0) { // right handle
3481             if (xn > xp) {
3482                 me = &(n->n);
3483                 other = &(n->p);
3484             } else {
3485                 me = &(n->p);
3486                 other = &(n->n);
3487             }
3488         } else if (which < 0){ // left handle
3489             if (xn <= xp) {
3490                 me = &(n->n);
3491                 other = &(n->p);
3492             } else {
3493                 me = &(n->p);
3494                 other = &(n->n);
3495             }
3496         } else { // both handles
3497             me = &(n->n);
3498             other = &(n->p);
3499             both = true;
3500         }
3501     }
3503     Radial rme(me->pos - n->pos);
3504     Radial rother(other->pos - n->pos);
3506     if (screen) {
3507         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3508     } else {
3509         node_rotate_one_internal (*n, angle, rme, rother, both);
3510     }
3512     me->pos = n->pos + NR::Point(rme);
3514     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3515         other->pos =  n->pos + NR::Point(rother);
3516     }
3518     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3519     // so here we just move all the knots without emitting move signals, for speed
3520     sp_node_update_handles(n, false);
3523 /**
3524  * Rotate selected nodes.
3525  */
3526 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3528     if (!nodepath || !nodepath->selected) return;
3530     if (g_list_length(nodepath->selected) == 1) {
3531        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3532         node_rotate_one (n, angle, which, screen);
3533     } else {
3534        // rotate as an object:
3536         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3537         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3538         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3539             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3540             box.expandTo (n->pos); // contain all selected nodes
3541         }
3543         gdouble rot;
3544         if (screen) {
3545             gdouble const zoom = nodepath->desktop->current_zoom();
3546             gdouble const zmove = angle / zoom;
3547             gdouble const r = NR::L2(box.max() - box.midpoint());
3548             rot = atan2(zmove, r);
3549         } else {
3550             rot = angle;
3551         }
3553         NR::Matrix t =
3554             NR::Matrix (NR::translate(-box.midpoint())) *
3555             NR::Matrix (NR::rotate(rot)) *
3556             NR::Matrix (NR::translate(box.midpoint()));
3558         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3559             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3560             n->pos *= t;
3561             n->n.pos *= t;
3562             n->p.pos *= t;
3563             sp_node_update_handles(n, false);
3564         }
3565     }
3567     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3570 /**
3571  * Scale one node.
3572  */
3573 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3575     bool both = false;
3576     Inkscape::NodePath::NodeSide *me, *other;
3578     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3579     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3581     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3582         me = &(n->p);
3583         other = &(n->n);
3584         n->code = NR_CURVETO;
3585     } else if (!n->p.other) {
3586         me = &(n->n);
3587         other = &(n->p);
3588         if (n->n.other)
3589             n->n.other->code = NR_CURVETO;
3590     } else {
3591         if (which > 0) { // right handle
3592             if (xn > xp) {
3593                 me = &(n->n);
3594                 other = &(n->p);
3595                 if (n->n.other)
3596                     n->n.other->code = NR_CURVETO;
3597             } else {
3598                 me = &(n->p);
3599                 other = &(n->n);
3600                 n->code = NR_CURVETO;
3601             }
3602         } else if (which < 0){ // left handle
3603             if (xn <= xp) {
3604                 me = &(n->n);
3605                 other = &(n->p);
3606                 if (n->n.other)
3607                     n->n.other->code = NR_CURVETO;
3608             } else {
3609                 me = &(n->p);
3610                 other = &(n->n);
3611                 n->code = NR_CURVETO;
3612             }
3613         } else { // both handles
3614             me = &(n->n);
3615             other = &(n->p);
3616             both = true;
3617             n->code = NR_CURVETO;
3618             if (n->n.other)
3619                 n->n.other->code = NR_CURVETO;
3620         }
3621     }
3623     Radial rme(me->pos - n->pos);
3624     Radial rother(other->pos - n->pos);
3626     rme.r += grow;
3627     if (rme.r < 0) rme.r = 0;
3628     if (rme.a == HUGE_VAL) {
3629         if (me->other) { // if direction is unknown, initialize it towards the next node
3630             Radial rme_next(me->other->pos - n->pos);
3631             rme.a = rme_next.a;
3632         } else { // if there's no next, initialize to 0
3633             rme.a = 0;
3634         }
3635     }
3636     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3637         rother.r += grow;
3638         if (rother.r < 0) rother.r = 0;
3639         if (rother.a == HUGE_VAL) {
3640             rother.a = rme.a + M_PI;
3641         }
3642     }
3644     me->pos = n->pos + NR::Point(rme);
3646     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3647         other->pos = n->pos + NR::Point(rother);
3648     }
3650     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3651     // so here we just move all the knots without emitting move signals, for speed
3652     sp_node_update_handles(n, false);
3655 /**
3656  * Scale selected nodes.
3657  */
3658 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3660     if (!nodepath || !nodepath->selected) return;
3662     if (g_list_length(nodepath->selected) == 1) {
3663         // scale handles of the single selected node
3664         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3665         node_scale_one (n, grow, which);
3666     } else {
3667         // scale nodes as an "object":
3669         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3670         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3671         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3672             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3673             box.expandTo (n->pos); // contain all selected nodes
3674         }
3676         double scale = (box.maxExtent() + grow)/box.maxExtent();
3678         NR::Matrix t =
3679             NR::Matrix (NR::translate(-box.midpoint())) *
3680             NR::Matrix (NR::scale(scale, scale)) *
3681             NR::Matrix (NR::translate(box.midpoint()));
3683         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3684             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3685             n->pos *= t;
3686             n->n.pos *= t;
3687             n->p.pos *= t;
3688             sp_node_update_handles(n, false);
3689         }
3690     }
3692     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3695 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3697     if (!nodepath) return;
3698     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3701 /**
3702  * Flip selected nodes horizontally/vertically.
3703  */
3704 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3706     if (!nodepath || !nodepath->selected) return;
3708     if (g_list_length(nodepath->selected) == 1) {
3709         // flip handles of the single selected node
3710         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3711         double temp = n->p.pos[axis];
3712         n->p.pos[axis] = n->n.pos[axis];
3713         n->n.pos[axis] = temp;
3714         sp_node_update_handles(n, false);
3715     } else {
3716         // scale nodes as an "object":
3718         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3719         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3720         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3721             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3722             box.expandTo (n->pos); // contain all selected nodes
3723         }
3725         NR::Matrix t =
3726             NR::Matrix (NR::translate(-box.midpoint())) *
3727             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3728             NR::Matrix (NR::translate(box.midpoint()));
3730         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3731             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3732             n->pos *= t;
3733             n->n.pos *= t;
3734             n->p.pos *= t;
3735             sp_node_update_handles(n, false);
3736         }
3737     }
3739     sp_nodepath_update_repr(nodepath);
3742 //-----------------------------------------------
3743 /**
3744  * Return new subpath under given nodepath.
3745  */
3746 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3748     g_assert(nodepath);
3749     g_assert(nodepath->desktop);
3751    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3753     s->nodepath = nodepath;
3754     s->closed = FALSE;
3755     s->nodes = NULL;
3756     s->first = NULL;
3757     s->last = NULL;
3759     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3760     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3761     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3763     return s;
3766 /**
3767  * Destroy nodes in subpath, then subpath itself.
3768  */
3769 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3771     g_assert(subpath);
3772     g_assert(subpath->nodepath);
3773     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3775     while (subpath->nodes) {
3776         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3777     }
3779     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3781     g_free(subpath);
3784 /**
3785  * Link head to tail in subpath.
3786  */
3787 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3789     g_assert(!sp->closed);
3790     g_assert(sp->last != sp->first);
3791     g_assert(sp->first->code == NR_MOVETO);
3793     sp->closed = TRUE;
3795     //Link the head to the tail
3796     sp->first->p.other = sp->last;
3797     sp->last->n.other  = sp->first;
3798     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3799     sp->first          = sp->last;
3801     //Remove the extra end node
3802     sp_nodepath_node_destroy(sp->last->n.other);
3805 /**
3806  * Open closed (loopy) subpath at node.
3807  */
3808 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3810     g_assert(sp->closed);
3811     g_assert(n->subpath == sp);
3812     g_assert(sp->first == sp->last);
3814     /* We create new startpoint, current node will become last one */
3816    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3817                                                 &n->pos, &n->pos, &n->n.pos);
3820     sp->closed        = FALSE;
3822     //Unlink to make a head and tail
3823     sp->first         = new_path;
3824     sp->last          = n;
3825     n->n.other        = NULL;
3826     new_path->p.other = NULL;
3829 /**
3830  * Returns area in triangle given by points; may be negative.
3831  */
3832 inline double
3833 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3835     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]);
3838 /**
3839  * Return new node in subpath with given properties.
3840  * \param pos Position of node.
3841  * \param ppos Handle position in previous direction
3842  * \param npos Handle position in previous direction
3843  */
3844 Inkscape::NodePath::Node *
3845 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)
3847     g_assert(sp);
3848     g_assert(sp->nodepath);
3849     g_assert(sp->nodepath->desktop);
3851     if (nodechunk == NULL)
3852         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3854     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3856     n->subpath  = sp;
3858     if (type != Inkscape::NodePath::NODE_NONE) {
3859         // use the type from sodipodi:nodetypes
3860         n->type = type;
3861     } else {
3862         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3863             // points are (almost) collinear
3864             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3865                 // endnode, or a node with a retracted handle
3866                 n->type = Inkscape::NodePath::NODE_CUSP;
3867             } else {
3868                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3869             }
3870         } else {
3871             n->type = Inkscape::NodePath::NODE_CUSP;
3872         }
3873     }
3875     n->code     = code;
3876     n->selected = FALSE;
3877     n->pos      = *pos;
3878     n->p.pos    = *ppos;
3879     n->n.pos    = *npos;
3881     n->dragging_out = NULL;
3883     Inkscape::NodePath::Node *prev;
3884     if (next) {
3885         //g_assert(g_list_find(sp->nodes, next));
3886         prev = next->p.other;
3887     } else {
3888         prev = sp->last;
3889     }
3891     if (prev)
3892         prev->n.other = n;
3893     else
3894         sp->first = n;
3896     if (next)
3897         next->p.other = n;
3898     else
3899         sp->last = n;
3901     n->p.other = prev;
3902     n->n.other = next;
3904     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"));
3905     sp_knot_set_position(n->knot, pos, 0);
3907     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3908     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3909     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3910     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3911     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3912     sp_knot_update_ctrl(n->knot);
3914     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3915     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3916     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3917     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3918     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3919     sp_knot_show(n->knot);
3921     // We only create handle knots and lines on demand
3922     n->p.knot = NULL;
3923     n->p.line = NULL;
3924     n->n.knot = NULL;
3925     n->n.line = NULL;
3927     sp->nodes = g_list_prepend(sp->nodes, n);
3929     return n;
3932 /**
3933  * Destroy node and its knots, link neighbors in subpath.
3934  */
3935 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3937     g_assert(node);
3938     g_assert(node->subpath);
3939     g_assert(SP_IS_KNOT(node->knot));
3941    Inkscape::NodePath::SubPath *sp = node->subpath;
3943     if (node->selected) { // first, deselect
3944         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3945         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3946     }
3948     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3950     g_object_unref(G_OBJECT(node->knot));
3951     if (node->p.knot)
3952         g_object_unref(G_OBJECT(node->p.knot));
3953     if (node->n.knot)
3954         g_object_unref(G_OBJECT(node->n.knot));
3956     if (node->p.line)
3957         gtk_object_destroy(GTK_OBJECT(node->p.line));
3958     if (node->n.line)
3959         gtk_object_destroy(GTK_OBJECT(node->n.line));
3961     if (sp->nodes) { // there are others nodes on the subpath
3962         if (sp->closed) {
3963             if (sp->first == node) {
3964                 g_assert(sp->last == node);
3965                 sp->first = node->n.other;
3966                 sp->last = sp->first;
3967             }
3968             node->p.other->n.other = node->n.other;
3969             node->n.other->p.other = node->p.other;
3970         } else {
3971             if (sp->first == node) {
3972                 sp->first = node->n.other;
3973                 sp->first->code = NR_MOVETO;
3974             }
3975             if (sp->last == node) sp->last = node->p.other;
3976             if (node->p.other) node->p.other->n.other = node->n.other;
3977             if (node->n.other) node->n.other->p.other = node->p.other;
3978         }
3979     } else { // this was the last node on subpath
3980         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3981     }
3983     g_mem_chunk_free(nodechunk, node);
3986 /**
3987  * Returns one of the node's two sides.
3988  * \param which Indicates which side.
3989  * \return Pointer to previous node side if which==-1, next if which==1.
3990  */
3991 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3993     g_assert(node);
3995     switch (which) {
3996         case -1:
3997             return &node->p;
3998         case 1:
3999             return &node->n;
4000         default:
4001             break;
4002     }
4004     g_assert_not_reached();
4006     return NULL;
4009 /**
4010  * Return the other side of the node, given one of its sides.
4011  */
4012 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4014     g_assert(node);
4016     if (me == &node->p) return &node->n;
4017     if (me == &node->n) return &node->p;
4019     g_assert_not_reached();
4021     return NULL;
4024 /**
4025  * Return NRPathcode on the given side of the node.
4026  */
4027 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4029     g_assert(node);
4031     if (me == &node->p) {
4032         if (node->p.other) return (NRPathcode)node->code;
4033         return NR_MOVETO;
4034     }
4036     if (me == &node->n) {
4037         if (node->n.other) return (NRPathcode)node->n.other->code;
4038         return NR_MOVETO;
4039     }
4041     g_assert_not_reached();
4043     return NR_END;
4046 /**
4047  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4048  */
4049 Inkscape::NodePath::Node *
4050 sp_nodepath_get_node_by_index(int index)
4052     Inkscape::NodePath::Node *e = NULL;
4054     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4055     if (!nodepath) {
4056         return e;
4057     }
4059     //find segment
4060     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4062         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4063         int n = g_list_length(sp->nodes);
4064         if (sp->closed) {
4065             n++;
4066         }
4068         //if the piece belongs to this subpath grab it
4069         //otherwise move onto the next subpath
4070         if (index < n) {
4071             e = sp->first;
4072             for (int i = 0; i < index; ++i) {
4073                 e = e->n.other;
4074             }
4075             break;
4076         } else {
4077             if (sp->closed) {
4078                 index -= (n+1);
4079             } else {
4080                 index -= n;
4081             }
4082         }
4083     }
4085     return e;
4088 /**
4089  * Returns plain text meaning of node type.
4090  */
4091 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4093     unsigned retracted = 0;
4094     bool endnode = false;
4096     for (int which = -1; which <= 1; which += 2) {
4097         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4098         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4099             retracted ++;
4100         if (!side->other)
4101             endnode = true;
4102     }
4104     if (retracted == 0) {
4105         if (endnode) {
4106                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4107                 return _("end node");
4108         } else {
4109             switch (node->type) {
4110                 case Inkscape::NodePath::NODE_CUSP:
4111                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4112                     return _("cusp");
4113                 case Inkscape::NodePath::NODE_SMOOTH:
4114                     // TRANSLATORS: "smooth" is an adjective here
4115                     return _("smooth");
4116                 case Inkscape::NodePath::NODE_SYMM:
4117                     return _("symmetric");
4118             }
4119         }
4120     } else if (retracted == 1) {
4121         if (endnode) {
4122             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4123             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4124         } else {
4125             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4126         }
4127     } else {
4128         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4129     }
4131     return NULL;
4134 /**
4135  * Handles content of statusbar as long as node tool is active.
4136  */
4137 void
4138 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
4140     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");
4141     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4143     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4144     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4145     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4146     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4148     SPDesktop *desktop = NULL;
4149     if (nodepath) {
4150         desktop = nodepath->desktop;
4151     } else {
4152         desktop = SP_ACTIVE_DESKTOP;
4153     }
4155     SPEventContext *ec = desktop->event_context;
4156     if (!ec) return;
4157     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4158     if (!mc) return;
4160     if (selected_nodes == 0) {
4161         Inkscape::Selection *sel = desktop->selection;
4162         if (!sel || sel->isEmpty()) {
4163             mc->setF(Inkscape::NORMAL_MESSAGE,
4164                      _("Select a single object to edit its nodes or handles."));
4165         } else {
4166             if (nodepath) {
4167             mc->setF(Inkscape::NORMAL_MESSAGE,
4168                      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.",
4169                               "<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.",
4170                               total_nodes),
4171                      total_nodes);
4172             } else {
4173                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4174                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4175                 } else {
4176                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4177                 }
4178             }
4179         }
4180     } else if (nodepath && selected_nodes == 1) {
4181         mc->setF(Inkscape::NORMAL_MESSAGE,
4182                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4183                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4184                           total_nodes),
4185                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4186     } else {
4187         if (selected_subpaths > 1) {
4188             mc->setF(Inkscape::NORMAL_MESSAGE,
4189                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4190                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4191                               total_nodes),
4192                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4193         } else {
4194             mc->setF(Inkscape::NORMAL_MESSAGE,
4195                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4196                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4197                               total_nodes),
4198                      selected_nodes, total_nodes, when_selected);
4199         }
4200     }
4204 /*
4205   Local Variables:
4206   mode:c++
4207   c-file-style:"stroustrup"
4208   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4209   indent-tabs-mode:nil
4210   fill-column:99
4211   End:
4212 */
4213 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :