Code

fix unstable behavior when ctrl+alt+dragging more than one sharp node
[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 /**
2513 \brief  Saves all nodes' and handles' current positions in their origin members
2514 */
2515 void
2516 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2518     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2519        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2520         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2521            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2522            n->origin = n->pos;
2523            n->p.origin = n->p.pos;
2524            n->n.origin = n->n.pos;
2525         }
2526     }
2527
2529 /**
2530 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2531 */
2532 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2534     if (!nodepath->selected) {
2535         return NULL;
2536     }
2538     GList *r = NULL;
2539     guint i = 0;
2540     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2541        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2542         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2543            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2544             i++;
2545             if (node->selected) {
2546                 r = g_list_append(r, GINT_TO_POINTER(i));
2547             }
2548         }
2549     }
2550     return r;
2553 /**
2554 \brief  Restores selection by selecting nodes whose positions are in the list
2555 */
2556 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2558     sp_nodepath_deselect(nodepath);
2560     guint i = 0;
2561     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2562        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2563         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2564            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2565             i++;
2566             if (g_list_find(r, GINT_TO_POINTER(i))) {
2567                 sp_nodepath_node_select(node, TRUE, TRUE);
2568             }
2569         }
2570     }
2574 /**
2575 \brief Adjusts handle according to node type and line code.
2576 */
2577 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2579     double len, otherlen, linelen;
2581     g_assert(node);
2583    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2584    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2586     /** \todo fixme: */
2587     if (me->other == NULL) return;
2588     if (other->other == NULL) return;
2590     /* I have line */
2592     NRPathcode mecode, ocode;
2593     if (which_adjust == 1) {
2594         mecode = (NRPathcode)me->other->code;
2595         ocode = (NRPathcode)node->code;
2596     } else {
2597         mecode = (NRPathcode)node->code;
2598         ocode = (NRPathcode)other->other->code;
2599     }
2601     if (mecode == NR_LINETO) return;
2603     /* I am curve */
2605     if (other->other == NULL) return;
2607     /* Other has line */
2609     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2611     NR::Point delta;
2612     if (ocode == NR_LINETO) {
2613         /* other is lineto, we are either smooth or symm */
2614        Inkscape::NodePath::Node *othernode = other->other;
2615         len = NR::L2(me->pos - node->pos);
2616         delta = node->pos - othernode->pos;
2617         linelen = NR::L2(delta);
2618         if (linelen < 1e-18) 
2619             return;
2620         me->pos = node->pos + (len / linelen)*delta;
2621         return;
2622     }
2624     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2626         me->pos = 2 * node->pos - other->pos;
2627         return;
2628     }
2630     /* We are smooth */
2632     len = NR::L2(me->pos - node->pos);
2633     delta = other->pos - node->pos;
2634     otherlen = NR::L2(delta);
2635     if (otherlen < 1e-18) return;
2637     me->pos = node->pos - (len / otherlen) * delta;
2640 /**
2641  \brief Adjusts both handles according to node type and line code
2642  */
2643 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2645     g_assert(node);
2647     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2649     /* we are either smooth or symm */
2651     if (node->p.other == NULL) return;
2653     if (node->n.other == NULL) return;
2655     if (node->code == NR_LINETO) {
2656         if (node->n.other->code == NR_LINETO) return;
2657         sp_node_adjust_handle(node, 1);
2658         return;
2659     }
2661     if (node->n.other->code == NR_LINETO) {
2662         if (node->code == NR_LINETO) return;
2663         sp_node_adjust_handle(node, -1);
2664         return;
2665     }
2667     /* both are curves */
2668     NR::Point const delta( node->n.pos - node->p.pos );
2670     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2671         node->p.pos = node->pos - delta / 2;
2672         node->n.pos = node->pos + delta / 2;
2673         return;
2674     }
2676     /* We are smooth */
2677     double plen = NR::L2(node->p.pos - node->pos);
2678     if (plen < 1e-18) return;
2679     double nlen = NR::L2(node->n.pos - node->pos);
2680     if (nlen < 1e-18) return;
2681     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2682     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2685 /**
2686  * Node event callback.
2687  */
2688 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2690     gboolean ret = FALSE;
2691     switch (event->type) {
2692         case GDK_ENTER_NOTIFY:
2693             active_node = n;
2694             break;
2695         case GDK_LEAVE_NOTIFY:
2696             active_node = NULL;
2697             break;
2698         case GDK_KEY_PRESS:
2699             switch (get_group0_keyval (&event->key)) {
2700                 case GDK_space:
2701                     if (event->key.state & GDK_BUTTON1_MASK) {
2702                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2703                         stamp_repr(nodepath);
2704                         ret = TRUE;
2705                     }
2706                     break;
2707                 default:
2708                     break;
2709             }
2710             break;
2711         default:
2712             break;
2713     }
2715     return ret;
2718 /**
2719  * Handle keypress on node; directly called.
2720  */
2721 gboolean node_key(GdkEvent *event)
2723     Inkscape::NodePath::Path *np;
2725     // there is no way to verify nodes so set active_node to nil when deleting!!
2726     if (active_node == NULL) return FALSE;
2728     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2729         gint ret = FALSE;
2730         switch (get_group0_keyval (&event->key)) {
2731             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2732             case GDK_BackSpace:
2733                 np = active_node->subpath->nodepath;
2734                 sp_nodepath_node_destroy(active_node);
2735                 sp_nodepath_update_repr(np);
2736                 active_node = NULL;
2737                 ret = TRUE;
2738                 break;
2739             case GDK_c:
2740                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2741                 ret = TRUE;
2742                 break;
2743             case GDK_s:
2744                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2745                 ret = TRUE;
2746                 break;
2747             case GDK_y:
2748                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2749                 ret = TRUE;
2750                 break;
2751             case GDK_b:
2752                 sp_nodepath_node_break(active_node);
2753                 ret = TRUE;
2754                 break;
2755         }
2756         return ret;
2757     }
2758     return FALSE;
2761 /**
2762  * Mouseclick on node callback.
2763  */
2764 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2766    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2768     if (state & GDK_CONTROL_MASK) {
2769         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2771         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2772             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2773                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2774             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2775                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2776             } else {
2777                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2778             }
2779             sp_nodepath_update_repr(nodepath);
2780             sp_nodepath_update_statusbar(nodepath);
2782         } else { //ctrl+alt+click: delete node
2783             GList *node_to_delete = NULL;
2784             node_to_delete = g_list_append(node_to_delete, n);
2785             sp_node_delete_preserve(node_to_delete);
2786         }
2788     } else {
2789         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2790     }
2793 /**
2794  * Mouse grabbed node callback.
2795  */
2796 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2798    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2800     if (!n->selected) {
2801         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2802     }
2804     sp_nodepath_remember_origins (n->subpath->nodepath);
2807 /**
2808  * Mouse ungrabbed node callback.
2809  */
2810 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2812    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2814    n->dragging_out = NULL;
2816    sp_nodepath_update_repr(n->subpath->nodepath);
2819 /**
2820  * The point on a line, given by its angle, closest to the given point.
2821  * \param p  A point.
2822  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2823  * \param closest  Pointer to the point struct where the result is stored.
2824  * \todo FIXME: use dot product perhaps?
2825  */
2826 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2828     if (a == HUGE_VAL) { // vertical
2829         *closest = NR::Point(0, (*p)[NR::Y]);
2830     } else {
2831         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2832         (*closest)[NR::Y] = a * (*closest)[NR::X];
2833     }
2836 /**
2837  * Distance from the point to a line given by its angle.
2838  * \param p  A point.
2839  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2840  */
2841 static double point_line_distance(NR::Point *p, double a)
2843     NR::Point c;
2844     point_line_closest(p, a, &c);
2845     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]));
2848 /**
2849  * Callback for node "request" signal.
2850  * \todo fixme: This goes to "moved" event? (lauris)
2851  */
2852 static gboolean
2853 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2855     double yn, xn, yp, xp;
2856     double an, ap, na, pa;
2857     double d_an, d_ap, d_na, d_pa;
2858     gboolean collinear = FALSE;
2859     NR::Point c;
2860     NR::Point pr;
2862    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2864    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2865    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2867        NR::Point mouse = (*p);
2869        if (!n->dragging_out) {
2870            // This is the first drag-out event; find out which handle to drag out
2871            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2872            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2874            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2875                return FALSE;
2877            Inkscape::NodePath::NodeSide *opposite;
2878            if (appr_p > appr_n) { // closer to p
2879                n->dragging_out = &n->p;
2880                opposite = &n->n;
2881                n->code = NR_CURVETO;
2882            } else if (appr_p < appr_n) { // closer to n
2883                n->dragging_out = &n->n;
2884                opposite = &n->p;
2885                n->n.other->code = NR_CURVETO;
2886            } else { // p and n nodes are the same
2887                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2888                    n->dragging_out = &n->p;
2889                    opposite = &n->n;
2890                    n->code = NR_CURVETO;
2891                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2892                    n->dragging_out = &n->n;
2893                    opposite = &n->p;
2894                    n->n.other->code = NR_CURVETO;
2895                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2896                    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);
2897                    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);
2898                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2899                        n->dragging_out = &n->n;
2900                        opposite = &n->p;
2901                        n->n.other->code = NR_CURVETO;
2902                    } else { // closer to other's n handle
2903                        n->dragging_out = &n->p;
2904                        opposite = &n->n;
2905                        n->code = NR_CURVETO;
2906                    }
2907                }
2908            }
2910            // if there's another handle, make sure the one we drag out starts parallel to it
2911            if (opposite->pos != n->pos) {
2912                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2913            }
2915            // knots might not be created yet!
2916            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2917            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2918        }
2920        // pass this on to the handle-moved callback
2921        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2922        sp_node_update_handles(n);
2923        return TRUE;
2924    }
2926     if (state & GDK_CONTROL_MASK) { // constrained motion
2928         // calculate relative distances of handles
2929         // n handle:
2930         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2931         xn = n->n.pos[NR::X] - n->pos[NR::X];
2932         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2933         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2934             if (n->n.other) { // if there is the next point
2935                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2936                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2937                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
2938             }
2939         }
2940         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2941         if (yn < 0) { xn = -xn; yn = -yn; }
2943         // p handle:
2944         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2945         xp = n->p.pos[NR::X] - n->pos[NR::X];
2946         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2947         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2948             if (n->p.other) {
2949                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2950                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
2951                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
2952             }
2953         }
2954         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2955         if (yp < 0) { xp = -xp; yp = -yp; }
2957         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2958             // sliding on handles, only if at least one of the handles is non-vertical
2959             // (otherwise it's the same as ctrl+drag anyway)
2961             // calculate angles of the handles
2962             if (xn == 0) {
2963                 if (yn == 0) { // no handle, consider it the continuation of the other one
2964                     an = 0;
2965                     collinear = TRUE;
2966                 }
2967                 else an = 0; // vertical; set the angle to horizontal
2968             } else an = yn/xn;
2970             if (xp == 0) {
2971                 if (yp == 0) { // no handle, consider it the continuation of the other one
2972                     ap = an;
2973                 }
2974                 else ap = 0; // vertical; set the angle to horizontal
2975             } else  ap = yp/xp;
2977             if (collinear) an = ap;
2979             // angles of the perpendiculars; HUGE_VAL means vertical
2980             if (an == 0) na = HUGE_VAL; else na = -1/an;
2981             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2983             // mouse point relative to the node's original pos
2984             pr = (*p) - n->origin;
2986             // distances to the four lines (two handles and two perpendiculars)
2987             d_an = point_line_distance(&pr, an);
2988             d_na = point_line_distance(&pr, na);
2989             d_ap = point_line_distance(&pr, ap);
2990             d_pa = point_line_distance(&pr, pa);
2992             // find out which line is the closest, save its closest point in c
2993             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2994                 point_line_closest(&pr, an, &c);
2995             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2996                 point_line_closest(&pr, ap, &c);
2997             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2998                 point_line_closest(&pr, na, &c);
2999             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3000                 point_line_closest(&pr, pa, &c);
3001             }
3003             // move the node to the closest point
3004             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3005                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3006                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3008         } else {  // constraining to hor/vert
3010             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3011                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3012             } else { // snap to vert
3013                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3014             }
3015         }
3016     } else { // move freely
3017         if (state & GDK_MOD1_MASK) { // sculpt
3018             sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3019         } else {
3020             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3021                                         (*p)[NR::X] - n->pos[NR::X],
3022                                         (*p)[NR::Y] - n->pos[NR::Y],
3023                                         (state & GDK_SHIFT_MASK) == 0);
3024         }
3025     }
3027     n->subpath->nodepath->desktop->scroll_to_point(p);
3029     return TRUE;
3032 /**
3033  * Node handle clicked callback.
3034  */
3035 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3037    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3039     if (state & GDK_CONTROL_MASK) { // "delete" handle
3040         if (n->p.knot == knot) {
3041             n->p.pos = n->pos;
3042         } else if (n->n.knot == knot) {
3043             n->n.pos = n->pos;
3044         }
3045         sp_node_update_handles(n);
3046         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3047         sp_nodepath_update_repr(nodepath);
3048         sp_nodepath_update_statusbar(nodepath);
3050     } else { // just select or add to selection, depending in Shift
3051         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3052     }
3055 /**
3056  * Node handle grabbed callback.
3057  */
3058 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3060    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3062     if (!n->selected) {
3063         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3064     }
3066     // remember the origin point of the handle
3067     if (n->p.knot == knot) {
3068         n->p.origin_radial = n->p.pos - n->pos;
3069     } else if (n->n.knot == knot) {
3070         n->n.origin_radial = n->n.pos - n->pos;
3071     } else {
3072         g_assert_not_reached();
3073     }
3077 /**
3078  * Node handle ungrabbed callback.
3079  */
3080 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3082    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3084     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3085     if (n->p.knot == knot) {
3086         n->p.origin_radial.a = 0;
3087         sp_knot_set_position(knot, &n->p.pos, state);
3088     } else if (n->n.knot == knot) {
3089         n->n.origin_radial.a = 0;
3090         sp_knot_set_position(knot, &n->n.pos, state);
3091     } else {
3092         g_assert_not_reached();
3093     }
3095     sp_nodepath_update_repr(n->subpath->nodepath);
3098 /**
3099  * Node handle "request" signal callback.
3100  */
3101 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3103     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3105     Inkscape::NodePath::NodeSide *me, *opposite;
3106     gint which;
3107     if (n->p.knot == knot) {
3108         me = &n->p;
3109         opposite = &n->n;
3110         which = -1;
3111     } else if (n->n.knot == knot) {
3112         me = &n->n;
3113         opposite = &n->p;
3114         which = 1;
3115     } else {
3116         me = opposite = NULL;
3117         which = 0;
3118         g_assert_not_reached();
3119     }
3121     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3123     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3125     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3126         /* We are smooth node adjacent with line */
3127         NR::Point const delta = *p - n->pos;
3128         NR::Coord const len = NR::L2(delta);
3129         Inkscape::NodePath::Node *othernode = opposite->other;
3130         NR::Point const ndelta = n->pos - othernode->pos;
3131         NR::Coord const linelen = NR::L2(ndelta);
3132         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3133             NR::Coord const scal = dot(delta, ndelta) / linelen;
3134             (*p) = n->pos + (scal / linelen) * ndelta;
3135         }
3136         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3137     } else {
3138         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3139     }
3141     sp_node_adjust_handle(n, -which);
3143     return FALSE;
3146 /**
3147  * Node handle moved callback.
3148  */
3149 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3151    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3153    Inkscape::NodePath::NodeSide *me;
3154    Inkscape::NodePath::NodeSide *other;
3155     if (n->p.knot == knot) {
3156         me = &n->p;
3157         other = &n->n;
3158     } else if (n->n.knot == knot) {
3159         me = &n->n;
3160         other = &n->p;
3161     } else {
3162         me = NULL;
3163         other = NULL;
3164         g_assert_not_reached();
3165     }
3167     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3168     Radial rme(me->pos - n->pos);
3169     Radial rother(other->pos - n->pos);
3170     Radial rnew(*p - n->pos);
3172     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3173         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3174         /* 0 interpreted as "no snapping". */
3176         // The closest PI/snaps angle, starting from zero.
3177         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3178         if (me->origin_radial.a == HUGE_VAL) {
3179             // ortho doesn't exist: original handle was zero length.
3180             rnew.a = a_snapped;
3181         } else {
3182             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3183              * its opposite and perpendiculars). */
3184             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3186             // Snap to the closest.
3187             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3188                        ? a_snapped
3189                        : a_ortho );
3190         }
3191     }
3193     if (state & GDK_MOD1_MASK) {
3194         // lock handle length
3195         rnew.r = me->origin_radial.r;
3196     }
3198     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3199         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3200         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3201         rother.a += rnew.a - rme.a;
3202         other->pos = NR::Point(rother) + n->pos;
3203         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3204         sp_knot_set_position(other->knot, &other->pos, 0);
3205     }
3207     me->pos = NR::Point(rnew) + n->pos;
3208     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3210     // this is what sp_knot_set_position does, but without emitting the signal:
3211     // we cannot emit a "moved" signal because we're now processing it
3212     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3214     knot->desktop->set_coordinate_status(me->pos);
3216     update_object(n->subpath->nodepath);
3218     /* status text */
3219     SPDesktop *desktop = n->subpath->nodepath->desktop;
3220     if (!desktop) return;
3221     SPEventContext *ec = desktop->event_context;
3222     if (!ec) return;
3223     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3224     if (!mc) return;
3226     double degrees = 180 / M_PI * rnew.a;
3227     if (degrees > 180) degrees -= 360;
3228     if (degrees < -180) degrees += 360;
3229     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3230         degrees = angle_to_compass (degrees);
3232     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3234     mc->setF(Inkscape::NORMAL_MESSAGE,
3235          _("<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);
3237     g_string_free(length, TRUE);
3240 /**
3241  * Node handle event callback.
3242  */
3243 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3245     gboolean ret = FALSE;
3246     switch (event->type) {
3247         case GDK_KEY_PRESS:
3248             switch (get_group0_keyval (&event->key)) {
3249                 case GDK_space:
3250                     if (event->key.state & GDK_BUTTON1_MASK) {
3251                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3252                         stamp_repr(nodepath);
3253                         ret = TRUE;
3254                     }
3255                     break;
3256                 default:
3257                     break;
3258             }
3259             break;
3260         default:
3261             break;
3262     }
3264     return ret;
3267 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3268                                  Radial &rme, Radial &rother, gboolean const both)
3270     rme.a += angle;
3271     if ( both
3272          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3273          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3274     {
3275         rother.a += angle;
3276     }
3279 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3280                                         Radial &rme, Radial &rother, gboolean const both)
3282     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3284     gdouble r;
3285     if ( both
3286          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3287          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3288     {
3289         r = MAX(rme.r, rother.r);
3290     } else {
3291         r = rme.r;
3292     }
3294     gdouble const weird_angle = atan2(norm_angle, r);
3295 /* Bulia says norm_angle is just the visible distance that the
3296  * object's end must travel on the screen.  Left as 'angle' for want of
3297  * a better name.*/
3299     rme.a += weird_angle;
3300     if ( both
3301          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3302          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3303     {
3304         rother.a += weird_angle;
3305     }
3308 /**
3309  * Rotate one node.
3310  */
3311 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3313     Inkscape::NodePath::NodeSide *me, *other;
3314     bool both = false;
3316     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3317     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3319     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3320         me = &(n->p);
3321         other = &(n->n);
3322     } else if (!n->p.other) {
3323         me = &(n->n);
3324         other = &(n->p);
3325     } else {
3326         if (which > 0) { // right handle
3327             if (xn > xp) {
3328                 me = &(n->n);
3329                 other = &(n->p);
3330             } else {
3331                 me = &(n->p);
3332                 other = &(n->n);
3333             }
3334         } else if (which < 0){ // left handle
3335             if (xn <= xp) {
3336                 me = &(n->n);
3337                 other = &(n->p);
3338             } else {
3339                 me = &(n->p);
3340                 other = &(n->n);
3341             }
3342         } else { // both handles
3343             me = &(n->n);
3344             other = &(n->p);
3345             both = true;
3346         }
3347     }
3349     Radial rme(me->pos - n->pos);
3350     Radial rother(other->pos - n->pos);
3352     if (screen) {
3353         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3354     } else {
3355         node_rotate_one_internal (*n, angle, rme, rother, both);
3356     }
3358     me->pos = n->pos + NR::Point(rme);
3360     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3361         other->pos =  n->pos + NR::Point(rother);
3362     }
3364     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3365     // so here we just move all the knots without emitting move signals, for speed
3366     sp_node_update_handles(n, false);
3369 /**
3370  * Rotate selected nodes.
3371  */
3372 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3374     if (!nodepath || !nodepath->selected) return;
3376     if (g_list_length(nodepath->selected) == 1) {
3377        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3378         node_rotate_one (n, angle, which, screen);
3379     } else {
3380        // rotate as an object:
3382         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3383         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3384         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3385             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3386             box.expandTo (n->pos); // contain all selected nodes
3387         }
3389         gdouble rot;
3390         if (screen) {
3391             gdouble const zoom = nodepath->desktop->current_zoom();
3392             gdouble const zmove = angle / zoom;
3393             gdouble const r = NR::L2(box.max() - box.midpoint());
3394             rot = atan2(zmove, r);
3395         } else {
3396             rot = angle;
3397         }
3399         NR::Matrix t =
3400             NR::Matrix (NR::translate(-box.midpoint())) *
3401             NR::Matrix (NR::rotate(rot)) *
3402             NR::Matrix (NR::translate(box.midpoint()));
3404         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3405             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3406             n->pos *= t;
3407             n->n.pos *= t;
3408             n->p.pos *= t;
3409             sp_node_update_handles(n, false);
3410         }
3411     }
3413     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3416 /**
3417  * Scale one node.
3418  */
3419 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3421     bool both = false;
3422     Inkscape::NodePath::NodeSide *me, *other;
3424     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3425     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3427     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3428         me = &(n->p);
3429         other = &(n->n);
3430         n->code = NR_CURVETO;
3431     } else if (!n->p.other) {
3432         me = &(n->n);
3433         other = &(n->p);
3434         if (n->n.other)
3435             n->n.other->code = NR_CURVETO;
3436     } else {
3437         if (which > 0) { // right handle
3438             if (xn > xp) {
3439                 me = &(n->n);
3440                 other = &(n->p);
3441                 if (n->n.other)
3442                     n->n.other->code = NR_CURVETO;
3443             } else {
3444                 me = &(n->p);
3445                 other = &(n->n);
3446                 n->code = NR_CURVETO;
3447             }
3448         } else if (which < 0){ // left handle
3449             if (xn <= xp) {
3450                 me = &(n->n);
3451                 other = &(n->p);
3452                 if (n->n.other)
3453                     n->n.other->code = NR_CURVETO;
3454             } else {
3455                 me = &(n->p);
3456                 other = &(n->n);
3457                 n->code = NR_CURVETO;
3458             }
3459         } else { // both handles
3460             me = &(n->n);
3461             other = &(n->p);
3462             both = true;
3463             n->code = NR_CURVETO;
3464             if (n->n.other)
3465                 n->n.other->code = NR_CURVETO;
3466         }
3467     }
3469     Radial rme(me->pos - n->pos);
3470     Radial rother(other->pos - n->pos);
3472     rme.r += grow;
3473     if (rme.r < 0) rme.r = 0;
3474     if (rme.a == HUGE_VAL) {
3475         if (me->other) { // if direction is unknown, initialize it towards the next node
3476             Radial rme_next(me->other->pos - n->pos);
3477             rme.a = rme_next.a;
3478         } else { // if there's no next, initialize to 0
3479             rme.a = 0;
3480         }
3481     }
3482     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3483         rother.r += grow;
3484         if (rother.r < 0) rother.r = 0;
3485         if (rother.a == HUGE_VAL) {
3486             rother.a = rme.a + M_PI;
3487         }
3488     }
3490     me->pos = n->pos + NR::Point(rme);
3492     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3493         other->pos = n->pos + NR::Point(rother);
3494     }
3496     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3497     // so here we just move all the knots without emitting move signals, for speed
3498     sp_node_update_handles(n, false);
3501 /**
3502  * Scale selected nodes.
3503  */
3504 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3506     if (!nodepath || !nodepath->selected) return;
3508     if (g_list_length(nodepath->selected) == 1) {
3509         // scale handles of the single selected node
3510         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3511         node_scale_one (n, grow, which);
3512     } else {
3513         // scale nodes as an "object":
3515         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3516         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3517         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3518             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3519             box.expandTo (n->pos); // contain all selected nodes
3520         }
3522         double scale = (box.maxExtent() + grow)/box.maxExtent();
3524         NR::Matrix t =
3525             NR::Matrix (NR::translate(-box.midpoint())) *
3526             NR::Matrix (NR::scale(scale, scale)) *
3527             NR::Matrix (NR::translate(box.midpoint()));
3529         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3530             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3531             n->pos *= t;
3532             n->n.pos *= t;
3533             n->p.pos *= t;
3534             sp_node_update_handles(n, false);
3535         }
3536     }
3538     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3541 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3543     if (!nodepath) return;
3544     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3547 /**
3548  * Flip selected nodes horizontally/vertically.
3549  */
3550 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3552     if (!nodepath || !nodepath->selected) return;
3554     if (g_list_length(nodepath->selected) == 1) {
3555         // flip handles of the single selected node
3556         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3557         double temp = n->p.pos[axis];
3558         n->p.pos[axis] = n->n.pos[axis];
3559         n->n.pos[axis] = temp;
3560         sp_node_update_handles(n, false);
3561     } else {
3562         // scale nodes as an "object":
3564         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3565         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3566         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3567             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3568             box.expandTo (n->pos); // contain all selected nodes
3569         }
3571         NR::Matrix t =
3572             NR::Matrix (NR::translate(-box.midpoint())) *
3573             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3574             NR::Matrix (NR::translate(box.midpoint()));
3576         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3577             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3578             n->pos *= t;
3579             n->n.pos *= t;
3580             n->p.pos *= t;
3581             sp_node_update_handles(n, false);
3582         }
3583     }
3585     sp_nodepath_update_repr(nodepath);
3588 //-----------------------------------------------
3589 /**
3590  * Return new subpath under given nodepath.
3591  */
3592 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3594     g_assert(nodepath);
3595     g_assert(nodepath->desktop);
3597    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3599     s->nodepath = nodepath;
3600     s->closed = FALSE;
3601     s->nodes = NULL;
3602     s->first = NULL;
3603     s->last = NULL;
3605     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3606     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3607     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3609     return s;
3612 /**
3613  * Destroy nodes in subpath, then subpath itself.
3614  */
3615 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3617     g_assert(subpath);
3618     g_assert(subpath->nodepath);
3619     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3621     while (subpath->nodes) {
3622         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3623     }
3625     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3627     g_free(subpath);
3630 /**
3631  * Link head to tail in subpath.
3632  */
3633 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3635     g_assert(!sp->closed);
3636     g_assert(sp->last != sp->first);
3637     g_assert(sp->first->code == NR_MOVETO);
3639     sp->closed = TRUE;
3641     //Link the head to the tail
3642     sp->first->p.other = sp->last;
3643     sp->last->n.other  = sp->first;
3644     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3645     sp->first          = sp->last;
3647     //Remove the extra end node
3648     sp_nodepath_node_destroy(sp->last->n.other);
3651 /**
3652  * Open closed (loopy) subpath at node.
3653  */
3654 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3656     g_assert(sp->closed);
3657     g_assert(n->subpath == sp);
3658     g_assert(sp->first == sp->last);
3660     /* We create new startpoint, current node will become last one */
3662    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3663                                                 &n->pos, &n->pos, &n->n.pos);
3666     sp->closed        = FALSE;
3668     //Unlink to make a head and tail
3669     sp->first         = new_path;
3670     sp->last          = n;
3671     n->n.other        = NULL;
3672     new_path->p.other = NULL;
3675 /**
3676  * Returns area in triangle given by points; may be negative.
3677  */
3678 inline double
3679 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3681     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]);
3684 /**
3685  * Return new node in subpath with given properties.
3686  * \param pos Position of node.
3687  * \param ppos Handle position in previous direction
3688  * \param npos Handle position in previous direction
3689  */
3690 Inkscape::NodePath::Node *
3691 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)
3693     g_assert(sp);
3694     g_assert(sp->nodepath);
3695     g_assert(sp->nodepath->desktop);
3697     if (nodechunk == NULL)
3698         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3700     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3702     n->subpath  = sp;
3704     if (type != Inkscape::NodePath::NODE_NONE) {
3705         // use the type from sodipodi:nodetypes
3706         n->type = type;
3707     } else {
3708         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3709             // points are (almost) collinear
3710             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3711                 // endnode, or a node with a retracted handle
3712                 n->type = Inkscape::NodePath::NODE_CUSP;
3713             } else {
3714                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3715             }
3716         } else {
3717             n->type = Inkscape::NodePath::NODE_CUSP;
3718         }
3719     }
3721     n->code     = code;
3722     n->selected = FALSE;
3723     n->pos      = *pos;
3724     n->p.pos    = *ppos;
3725     n->n.pos    = *npos;
3727     n->dragging_out = NULL;
3729     Inkscape::NodePath::Node *prev;
3730     if (next) {
3731         //g_assert(g_list_find(sp->nodes, next));
3732         prev = next->p.other;
3733     } else {
3734         prev = sp->last;
3735     }
3737     if (prev)
3738         prev->n.other = n;
3739     else
3740         sp->first = n;
3742     if (next)
3743         next->p.other = n;
3744     else
3745         sp->last = n;
3747     n->p.other = prev;
3748     n->n.other = next;
3750     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"));
3751     sp_knot_set_position(n->knot, pos, 0);
3753     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3754     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3755     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3756     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3757     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3758     sp_knot_update_ctrl(n->knot);
3760     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3761     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3762     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3763     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3764     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3765     sp_knot_show(n->knot);
3767     // We only create handle knots and lines on demand
3768     n->p.knot = NULL;
3769     n->p.line = NULL;
3770     n->n.knot = NULL;
3771     n->n.line = NULL;
3773     sp->nodes = g_list_prepend(sp->nodes, n);
3775     return n;
3778 /**
3779  * Destroy node and its knots, link neighbors in subpath.
3780  */
3781 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3783     g_assert(node);
3784     g_assert(node->subpath);
3785     g_assert(SP_IS_KNOT(node->knot));
3787    Inkscape::NodePath::SubPath *sp = node->subpath;
3789     if (node->selected) { // first, deselect
3790         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3791         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3792     }
3794     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3796     g_object_unref(G_OBJECT(node->knot));
3797     if (node->p.knot)
3798         g_object_unref(G_OBJECT(node->p.knot));
3799     if (node->n.knot)
3800         g_object_unref(G_OBJECT(node->n.knot));
3802     if (node->p.line)
3803         gtk_object_destroy(GTK_OBJECT(node->p.line));
3804     if (node->n.line)
3805         gtk_object_destroy(GTK_OBJECT(node->n.line));
3807     if (sp->nodes) { // there are others nodes on the subpath
3808         if (sp->closed) {
3809             if (sp->first == node) {
3810                 g_assert(sp->last == node);
3811                 sp->first = node->n.other;
3812                 sp->last = sp->first;
3813             }
3814             node->p.other->n.other = node->n.other;
3815             node->n.other->p.other = node->p.other;
3816         } else {
3817             if (sp->first == node) {
3818                 sp->first = node->n.other;
3819                 sp->first->code = NR_MOVETO;
3820             }
3821             if (sp->last == node) sp->last = node->p.other;
3822             if (node->p.other) node->p.other->n.other = node->n.other;
3823             if (node->n.other) node->n.other->p.other = node->p.other;
3824         }
3825     } else { // this was the last node on subpath
3826         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3827     }
3829     g_mem_chunk_free(nodechunk, node);
3832 /**
3833  * Returns one of the node's two sides.
3834  * \param which Indicates which side.
3835  * \return Pointer to previous node side if which==-1, next if which==1.
3836  */
3837 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3839     g_assert(node);
3841     switch (which) {
3842         case -1:
3843             return &node->p;
3844         case 1:
3845             return &node->n;
3846         default:
3847             break;
3848     }
3850     g_assert_not_reached();
3852     return NULL;
3855 /**
3856  * Return the other side of the node, given one of its sides.
3857  */
3858 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
3860     g_assert(node);
3862     if (me == &node->p) return &node->n;
3863     if (me == &node->n) return &node->p;
3865     g_assert_not_reached();
3867     return NULL;
3870 /**
3871  * Return NRPathcode on the given side of the node.
3872  */
3873 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3875     g_assert(node);
3877     if (me == &node->p) {
3878         if (node->p.other) return (NRPathcode)node->code;
3879         return NR_MOVETO;
3880     }
3882     if (me == &node->n) {
3883         if (node->n.other) return (NRPathcode)node->n.other->code;
3884         return NR_MOVETO;
3885     }
3887     g_assert_not_reached();
3889     return NR_END;
3892 /**
3893  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3894  */
3895 Inkscape::NodePath::Node *
3896 sp_nodepath_get_node_by_index(int index)
3898     Inkscape::NodePath::Node *e = NULL;
3900     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3901     if (!nodepath) {
3902         return e;
3903     }
3905     //find segment
3906     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3908         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3909         int n = g_list_length(sp->nodes);
3910         if (sp->closed) {
3911             n++;
3912         }
3914         //if the piece belongs to this subpath grab it
3915         //otherwise move onto the next subpath
3916         if (index < n) {
3917             e = sp->first;
3918             for (int i = 0; i < index; ++i) {
3919                 e = e->n.other;
3920             }
3921             break;
3922         } else {
3923             if (sp->closed) {
3924                 index -= (n+1);
3925             } else {
3926                 index -= n;
3927             }
3928         }
3929     }
3931     return e;
3934 /**
3935  * Returns plain text meaning of node type.
3936  */
3937 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3939     unsigned retracted = 0;
3940     bool endnode = false;
3942     for (int which = -1; which <= 1; which += 2) {
3943         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3944         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3945             retracted ++;
3946         if (!side->other)
3947             endnode = true;
3948     }
3950     if (retracted == 0) {
3951         if (endnode) {
3952                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3953                 return _("end node");
3954         } else {
3955             switch (node->type) {
3956                 case Inkscape::NodePath::NODE_CUSP:
3957                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3958                     return _("cusp");
3959                 case Inkscape::NodePath::NODE_SMOOTH:
3960                     // TRANSLATORS: "smooth" is an adjective here
3961                     return _("smooth");
3962                 case Inkscape::NodePath::NODE_SYMM:
3963                     return _("symmetric");
3964             }
3965         }
3966     } else if (retracted == 1) {
3967         if (endnode) {
3968             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3969             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3970         } else {
3971             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3972         }
3973     } else {
3974         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3975     }
3977     return NULL;
3980 /**
3981  * Handles content of statusbar as long as node tool is active.
3982  */
3983 void
3984 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3986     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");
3987     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3989     gint total_nodes = sp_nodepath_get_node_count(nodepath);
3990     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
3991     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
3992     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
3994     SPDesktop *desktop = NULL;
3995     if (nodepath) {
3996         desktop = nodepath->desktop;
3997     } else {
3998         desktop = SP_ACTIVE_DESKTOP;
3999     }
4001     SPEventContext *ec = desktop->event_context;
4002     if (!ec) return;
4003     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4004     if (!mc) return;
4006     if (selected_nodes == 0) {
4007         Inkscape::Selection *sel = desktop->selection;
4008         if (!sel || sel->isEmpty()) {
4009             mc->setF(Inkscape::NORMAL_MESSAGE,
4010                      _("Select a single object to edit its nodes or handles."));
4011         } else {
4012             if (nodepath) {
4013             mc->setF(Inkscape::NORMAL_MESSAGE,
4014                      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.",
4015                               "<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.",
4016                               total_nodes),
4017                      total_nodes);
4018             } else {
4019                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4020                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4021                 } else {
4022                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4023                 }
4024             }
4025         }
4026     } else if (nodepath && selected_nodes == 1) {
4027         mc->setF(Inkscape::NORMAL_MESSAGE,
4028                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4029                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4030                           total_nodes),
4031                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4032     } else {
4033         if (selected_subpaths > 1) {
4034             mc->setF(Inkscape::NORMAL_MESSAGE,
4035                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4036                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4037                               total_nodes),
4038                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4039         } else {
4040             mc->setF(Inkscape::NORMAL_MESSAGE,
4041                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4042                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4043                               total_nodes),
4044                      selected_nodes, total_nodes, when_selected);
4045         }
4046     }
4050 /*
4051   Local Variables:
4052   mode:c++
4053   c-file-style:"stroustrup"
4054   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4055   indent-tabs-mode:nil
4056   fill-column:99
4057   End:
4058 */
4059 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :