Code

make profile selectable via prefs, add elliptic (preliminary, needs perpendicular...
[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     }
1018 double
1019 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1021     // extremely primitive for now, don't have time to look for the real one
1022     double lower = NR::L2(b - a);
1023     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1024     return (lower + upper)/2;
1027 void
1028 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1030     n->pos = n->origin + delta;
1031     n->n.pos = n->n.origin + delta_n;
1032     n->p.pos = n->p.origin + delta_p;
1033     sp_node_adjust_handles(n);
1034     sp_node_update_handles(n, false);
1037 /**
1038  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1039  * on how far they are from the dragged node n.
1040  */
1041 static void 
1042 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1044     g_assert (n);
1045     g_assert (nodepath);
1046     g_assert (n->subpath->nodepath == nodepath);
1048     double pressure = n->knot->pressure;
1049     if (pressure == 0)
1050         pressure = 0.5; // default
1051     pressure = CLAMP (pressure, 0.2, 0.8);
1053     // map pressure to alpha = 1/5 ... 5
1054     double alpha = 1 - 2 * fabs(pressure - 0.5);
1055     if (pressure > 0.5)
1056         alpha = 1/alpha;
1058     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1060     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1061         // Only one subpath has selected nodes:
1062         // use linear mode, where the distance from n to node being dragged is calculated along the path
1064         double n_sel_range = 0, p_sel_range = 0;
1065         guint n_nodes = 0, p_nodes = 0;
1066         guint n_sel_nodes = 0, p_sel_nodes = 0;
1068         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1069         {
1070             double n_range = 0, p_range = 0;
1071             bool n_going = true, p_going = true;
1072             Inkscape::NodePath::Node *n_node = n;
1073             Inkscape::NodePath::Node *p_node = n;
1074             do {
1075                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1076                 if (n_node && n_going)
1077                     n_node = n_node->n.other;
1078                 if (n_node == NULL) {
1079                     n_going = false;
1080                 } else {
1081                     n_nodes ++;
1082                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1083                     if (n_node->selected) {
1084                         n_sel_nodes ++;
1085                         n_sel_range = n_range;
1086                     }
1087                     if (n_node == p_node) {
1088                         n_going = false;
1089                         p_going = false;
1090                     }
1091                 }
1092                 if (p_node && p_going)
1093                     p_node = p_node->p.other;
1094                 if (p_node == NULL) {
1095                     p_going = false;
1096                 } else {
1097                     p_nodes ++;
1098                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1099                     if (p_node->selected) {
1100                         p_sel_nodes ++;
1101                         p_sel_range = p_range;
1102                     }
1103                     if (p_node == n_node) {
1104                         n_going = false;
1105                         p_going = false;
1106                     }
1107                 }
1108             } while (n_going || p_going);
1109         }
1111         // Second pass: actually move nodes in this subpath
1112         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1113         {
1114             double n_range = 0, p_range = 0;
1115             bool n_going = true, p_going = true;
1116             Inkscape::NodePath::Node *n_node = n;
1117             Inkscape::NodePath::Node *p_node = n;
1118             do {
1119                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1120                 if (n_node && n_going)
1121                     n_node = n_node->n.other;
1122                 if (n_node == NULL) {
1123                     n_going = false;
1124                 } else {
1125                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1126                     if (n_node->selected) {
1127                         sp_nodepath_move_node_and_handles (n_node, 
1128                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1129                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1130                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1131                     }
1132                     if (n_node == p_node) {
1133                         n_going = false;
1134                         p_going = false;
1135                     }
1136                 }
1137                 if (p_node && p_going)
1138                     p_node = p_node->p.other;
1139                 if (p_node == NULL) {
1140                     p_going = false;
1141                 } else {
1142                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1143                     if (p_node->selected) {
1144                         sp_nodepath_move_node_and_handles (p_node, 
1145                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1146                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1147                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1148                     }
1149                     if (p_node == n_node) {
1150                         n_going = false;
1151                         p_going = false;
1152                     }
1153                 }
1154             } while (n_going || p_going);
1155         }
1157     } else {
1158         // Multiple subpaths have selected nodes:
1159         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1160         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1161         // fix the pear-like shape when sculpting e.g. a ring
1163         // First pass: calculate range
1164         gdouble direct_range = 0;
1165         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1166             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1167             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1168                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1169                 if (node->selected) {
1170                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1171                 }
1172             }
1173         }
1175         // Second pass: actually move nodes
1176         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1177             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1178             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1179                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1180                 if (node->selected) {
1181                     if (direct_range > 1e-6) {
1182                         sp_nodepath_move_node_and_handles (node,
1183                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1184                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1185                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1186                     } else {
1187                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1188                     }
1190                 }
1191             }
1192         }
1193     }
1195     // do not update repr here so that node dragging is acceptably fast
1196     update_object(nodepath);
1200 /**
1201  * Move node selection to point, adjust its and neighbouring handles,
1202  * handle possible snapping, and commit the change with possible undo.
1203  */
1204 void
1205 sp_node_selected_move(gdouble dx, gdouble dy)
1207     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1208     if (!nodepath) return;
1210     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1212     if (dx == 0) {
1213         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1214     } else if (dy == 0) {
1215         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1216     } else {
1217         sp_nodepath_update_repr(nodepath);
1218     }
1221 /**
1222  * Move node selection off screen and commit the change.
1223  */
1224 void
1225 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1227     // borrowed from sp_selection_move_screen in selection-chemistry.c
1228     // we find out the current zoom factor and divide deltas by it
1229     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1231     gdouble zoom = desktop->current_zoom();
1232     gdouble zdx = dx / zoom;
1233     gdouble zdy = dy / zoom;
1235     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1236     if (!nodepath) return;
1238     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1240     if (dx == 0) {
1241         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1242     } else if (dy == 0) {
1243         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1244     } else {
1245         sp_nodepath_update_repr(nodepath);
1246     }
1249 /** If they don't yet exist, creates knot and line for the given side of the node */
1250 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1252     if (!side->knot) {
1253         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"));
1255         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1256         side->knot->setSize (7);
1257         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1258         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1259         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1260         sp_knot_update_ctrl(side->knot);
1262         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1263         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1264         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1265         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1266         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1267         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1268     }
1270     if (!side->line) {
1271         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1272                                         SP_TYPE_CTRLLINE, NULL);
1273     }
1276 /**
1277  * Ensure the given handle of the node is visible/invisible, update its screen position
1278  */
1279 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1281     g_assert(node != NULL);
1283    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1284     NRPathcode code = sp_node_path_code_from_side(node, side);
1286     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1288     if (show_handle) {
1289         if (!side->knot) { // No handle knot at all
1290             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1291             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1292             side->knot->pos = side->pos;
1293             if (side->knot->item) 
1294                 SP_CTRL(side->knot->item)->moveto(side->pos);
1295             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1296             sp_knot_show(side->knot);
1297         } else {
1298             if (side->knot->pos != side->pos) { // only if it's really moved
1299                 if (fire_move_signals) {
1300                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1301                 } else {
1302                     sp_knot_moveto(side->knot, &side->pos);
1303                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1304                 }
1305             }
1306             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1307                 sp_knot_show(side->knot);
1308             }
1309         }
1310         sp_canvas_item_show(side->line);
1311     } else {
1312         if (side->knot) {
1313             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1314                 sp_knot_hide(side->knot);
1315             }
1316         }
1317         if (side->line) {
1318             sp_canvas_item_hide(side->line);
1319         }
1320     }
1323 /**
1324  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1325  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1326  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1327  * updated; otherwise, just move the knots silently (used in batch moves).
1328  */
1329 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1331     g_assert(node != NULL);
1333     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1334         sp_knot_show(node->knot);
1335     }
1337     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1338         if (fire_move_signals)
1339             sp_knot_set_position(node->knot, &node->pos, 0);
1340         else 
1341             sp_knot_moveto(node->knot, &node->pos);
1342     }
1344     gboolean show_handles = node->selected;
1345     if (node->p.other != NULL) {
1346         if (node->p.other->selected) show_handles = TRUE;
1347     }
1348     if (node->n.other != NULL) {
1349         if (node->n.other->selected) show_handles = TRUE;
1350     }
1352     if (node->subpath->nodepath->show_handles == false)
1353         show_handles = FALSE;
1355     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1356     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1359 /**
1360  * Call sp_node_update_handles() for all nodes on subpath.
1361  */
1362 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1364     g_assert(subpath != NULL);
1366     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1367         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1368     }
1371 /**
1372  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1373  */
1374 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1376     g_assert(nodepath != NULL);
1378     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1379         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1380     }
1383 void
1384 sp_nodepath_show_handles(bool show)
1386     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1387     if (nodepath == NULL) return;
1389     nodepath->show_handles = show;
1390     sp_nodepath_update_handles(nodepath);
1393 /**
1394  * Adds all selected nodes in nodepath to list.
1395  */
1396 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1398     StlConv<Node *>::list(l, selected);
1399 /// \todo this adds a copying, rework when the selection becomes a stl list
1402 /**
1403  * Align selected nodes on the specified axis.
1404  */
1405 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1407     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1408         return;
1409     }
1411     if ( !nodepath->selected->next ) { // only one node selected
1412         return;
1413     }
1414    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1415     NR::Point dest(pNode->pos);
1416     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1417         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1418         if (pNode) {
1419             dest[axis] = pNode->pos[axis];
1420             sp_node_moveto(pNode, dest);
1421         }
1422     }
1424     sp_nodepath_update_repr(nodepath);
1427 /// Helper struct.
1428 struct NodeSort
1430    Inkscape::NodePath::Node *_node;
1431     NR::Coord _coord;
1432     /// \todo use vectorof pointers instead of calling copy ctor
1433     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1434         _node(node), _coord(node->pos[axis])
1435     {}
1437 };
1439 static bool operator<(NodeSort const &a, NodeSort const &b)
1441     return (a._coord < b._coord);
1444 /**
1445  * Distribute selected nodes on the specified axis.
1446  */
1447 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1449     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1450         return;
1451     }
1453     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1454         return;
1455     }
1457    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1458     std::vector<NodeSort> sorted;
1459     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1460         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1461         if (pNode) {
1462             NodeSort n(pNode, axis);
1463             sorted.push_back(n);
1464             //dest[axis] = pNode->pos[axis];
1465             //sp_node_moveto(pNode, dest);
1466         }
1467     }
1468     std::sort(sorted.begin(), sorted.end());
1469     unsigned int len = sorted.size();
1470     //overall bboxes span
1471     float dist = (sorted.back()._coord -
1472                   sorted.front()._coord);
1473     //new distance between each bbox
1474     float step = (dist) / (len - 1);
1475     float pos = sorted.front()._coord;
1476     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1477           it < sorted.end();
1478           it ++ )
1479     {
1480         NR::Point dest((*it)._node->pos);
1481         dest[axis] = pos;
1482         sp_node_moveto((*it)._node, dest);
1483         pos += step;
1484     }
1486     sp_nodepath_update_repr(nodepath);
1490 /**
1491  * Call sp_nodepath_line_add_node() for all selected segments.
1492  */
1493 void
1494 sp_node_selected_add_node(void)
1496     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1497     if (!nodepath) {
1498         return;
1499     }
1501     GList *nl = NULL;
1503     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1504        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1505         g_assert(t->selected);
1506         if (t->p.other && t->p.other->selected) {
1507             nl = g_list_prepend(nl, t);
1508         }
1509     }
1511     while (nl) {
1512        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1513        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1514         sp_nodepath_node_select(n, TRUE, FALSE);
1515         nl = g_list_remove(nl, t);
1516     }
1518     /** \todo fixme: adjust ? */
1519     sp_nodepath_update_handles(nodepath);
1521     sp_nodepath_update_repr(nodepath);
1523     sp_nodepath_update_statusbar(nodepath);
1526 /**
1527  * Select segment nearest to point
1528  */
1529 void
1530 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1532     if (!nodepath) {
1533         return;
1534     }
1536     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1538     //find segment to segment
1539     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1541     gboolean force = FALSE;
1542     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1543         force = TRUE;
1544     }
1545     sp_nodepath_node_select(e, (gboolean) toggle, force);
1546     if (e->p.other)
1547         sp_nodepath_node_select(e->p.other, TRUE, force);
1549     sp_nodepath_update_handles(nodepath);
1551     sp_nodepath_update_statusbar(nodepath);
1554 /**
1555  * Add a node nearest to point
1556  */
1557 void
1558 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1560     if (!nodepath) {
1561         return;
1562     }
1564     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1566     //find segment to split
1567     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1569     //don't know why but t seems to flip for lines
1570     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1571         position.t = 1.0 - position.t;
1572     }
1573     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1574     sp_nodepath_node_select(n, FALSE, TRUE);
1576     /* fixme: adjust ? */
1577     sp_nodepath_update_handles(nodepath);
1579     sp_nodepath_update_repr(nodepath);
1581     sp_nodepath_update_statusbar(nodepath);
1584 /*
1585  * Adjusts a segment so that t moves by a certain delta for dragging
1586  * converts lines to curves
1587  *
1588  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1589  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1590  */
1591 void
1592 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1594     /* feel good is an arbitrary parameter that distributes the delta between handles
1595      * if t of the drag point is less than 1/6 distance form the endpoint only
1596      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1597      */
1598     double feel_good;
1599     if (t <= 1.0 / 6.0)
1600         feel_good = 0;
1601     else if (t <= 0.5)
1602         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1603     else if (t <= 5.0 / 6.0)
1604         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1605     else
1606         feel_good = 1;
1608     //if we're dragging a line convert it to a curve
1609     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1610         sp_nodepath_set_line_type(e, NR_CURVETO);
1611     }
1613     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1614     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1615     e->p.other->n.pos += offsetcoord0;
1616     e->p.pos += offsetcoord1;
1618     // adjust handles of adjacent nodes where necessary
1619     sp_node_adjust_handle(e,1);
1620     sp_node_adjust_handle(e->p.other,-1);
1622     sp_nodepath_update_handles(e->subpath->nodepath);
1624     update_object(e->subpath->nodepath);
1626     sp_nodepath_update_statusbar(e->subpath->nodepath);
1630 /**
1631  * Call sp_nodepath_break() for all selected segments.
1632  */
1633 void sp_node_selected_break()
1635     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1636     if (!nodepath) return;
1638     GList *temp = NULL;
1639     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1640        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1641        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1642         if (nn == NULL) continue; // no break, no new node
1643         temp = g_list_prepend(temp, nn);
1644     }
1646     if (temp) {
1647         sp_nodepath_deselect(nodepath);
1648     }
1649     for (GList *l = temp; l != NULL; l = l->next) {
1650         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1651     }
1653     sp_nodepath_update_handles(nodepath);
1655     sp_nodepath_update_repr(nodepath);
1658 /**
1659  * Duplicate the selected node(s).
1660  */
1661 void sp_node_selected_duplicate()
1663     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1664     if (!nodepath) {
1665         return;
1666     }
1668     GList *temp = NULL;
1669     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1670        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1671        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1672         if (nn == NULL) continue; // could not duplicate
1673         temp = g_list_prepend(temp, nn);
1674     }
1676     if (temp) {
1677         sp_nodepath_deselect(nodepath);
1678     }
1679     for (GList *l = temp; l != NULL; l = l->next) {
1680         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1681     }
1683     sp_nodepath_update_handles(nodepath);
1685     sp_nodepath_update_repr(nodepath);
1688 /**
1689  *  Join two nodes by merging them into one.
1690  */
1691 void sp_node_selected_join()
1693     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1694     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1696     if (g_list_length(nodepath->selected) != 2) {
1697         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1698         return;
1699     }
1701    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1702    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1704     g_assert(a != b);
1705     g_assert(a->p.other || a->n.other);
1706     g_assert(b->p.other || b->n.other);
1708     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1709         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1710         return;
1711     }
1713     /* a and b are endpoints */
1715     NR::Point c;
1716     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1717         c = a->pos;
1718     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1719         c = b->pos;
1720     } else {
1721         c = (a->pos + b->pos) / 2;
1722     }
1724     if (a->subpath == b->subpath) {
1725        Inkscape::NodePath::SubPath *sp = a->subpath;
1726         sp_nodepath_subpath_close(sp);
1727         sp_node_moveto (sp->first, c);
1729         sp_nodepath_update_handles(sp->nodepath);
1730         sp_nodepath_update_repr(nodepath);
1731         return;
1732     }
1734     /* a and b are separate subpaths */
1735    Inkscape::NodePath::SubPath *sa = a->subpath;
1736    Inkscape::NodePath::SubPath *sb = b->subpath;
1737     NR::Point p;
1738    Inkscape::NodePath::Node *n;
1739     NRPathcode code;
1740     if (a == sa->first) {
1741         p = sa->first->n.pos;
1742         code = (NRPathcode)sa->first->n.other->code;
1743        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1744         n = sa->last;
1745         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1746         n = n->p.other;
1747         while (n) {
1748             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1749             n = n->p.other;
1750             if (n == sa->first) n = NULL;
1751         }
1752         sp_nodepath_subpath_destroy(sa);
1753         sa = t;
1754     } else if (a == sa->last) {
1755         p = sa->last->p.pos;
1756         code = (NRPathcode)sa->last->code;
1757         sp_nodepath_node_destroy(sa->last);
1758     } else {
1759         code = NR_END;
1760         g_assert_not_reached();
1761     }
1763     if (b == sb->first) {
1764         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1765         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1766             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1767         }
1768     } else if (b == sb->last) {
1769         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1770         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1771             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1772         }
1773     } else {
1774         g_assert_not_reached();
1775     }
1776     /* and now destroy sb */
1778     sp_nodepath_subpath_destroy(sb);
1780     sp_nodepath_update_handles(sa->nodepath);
1782     sp_nodepath_update_repr(nodepath);
1784     sp_nodepath_update_statusbar(nodepath);
1787 /**
1788  *  Join two nodes by adding a segment between them.
1789  */
1790 void sp_node_selected_join_segment()
1792     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1793     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1795     if (g_list_length(nodepath->selected) != 2) {
1796         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1797         return;
1798     }
1800    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1801    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1803     g_assert(a != b);
1804     g_assert(a->p.other || a->n.other);
1805     g_assert(b->p.other || b->n.other);
1807     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1808         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1809         return;
1810     }
1812     if (a->subpath == b->subpath) {
1813        Inkscape::NodePath::SubPath *sp = a->subpath;
1815         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1816         sp->closed = TRUE;
1818         sp->first->p.other = sp->last;
1819         sp->last->n.other  = sp->first;
1821         sp_node_handle_mirror_p_to_n(sp->last);
1822         sp_node_handle_mirror_n_to_p(sp->first);
1824         sp->first->code = sp->last->code;
1825         sp->first       = sp->last;
1827         sp_nodepath_update_handles(sp->nodepath);
1829         sp_nodepath_update_repr(nodepath);
1831         return;
1832     }
1834     /* a and b are separate subpaths */
1835    Inkscape::NodePath::SubPath *sa = a->subpath;
1836    Inkscape::NodePath::SubPath *sb = b->subpath;
1838    Inkscape::NodePath::Node *n;
1839     NR::Point p;
1840     NRPathcode code;
1841     if (a == sa->first) {
1842         code = (NRPathcode) sa->first->n.other->code;
1843        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1844         n = sa->last;
1845         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1846         for (n = n->p.other; n != NULL; n = n->p.other) {
1847             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1848         }
1849         sp_nodepath_subpath_destroy(sa);
1850         sa = t;
1851     } else if (a == sa->last) {
1852         code = (NRPathcode)sa->last->code;
1853     } else {
1854         code = NR_END;
1855         g_assert_not_reached();
1856     }
1858     if (b == sb->first) {
1859         n = sb->first;
1860         sp_node_handle_mirror_p_to_n(sa->last);
1861         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1862         sp_node_handle_mirror_n_to_p(sa->last);
1863         for (n = n->n.other; n != NULL; n = n->n.other) {
1864             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1865         }
1866     } else if (b == sb->last) {
1867         n = sb->last;
1868         sp_node_handle_mirror_p_to_n(sa->last);
1869         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1870         sp_node_handle_mirror_n_to_p(sa->last);
1871         for (n = n->p.other; n != NULL; n = n->p.other) {
1872             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1873         }
1874     } else {
1875         g_assert_not_reached();
1876     }
1877     /* and now destroy sb */
1879     sp_nodepath_subpath_destroy(sb);
1881     sp_nodepath_update_handles(sa->nodepath);
1883     sp_nodepath_update_repr(nodepath);
1886 /**
1887  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1888  */
1889 void sp_node_delete_preserve(GList *nodes_to_delete)
1891     GSList *nodepaths = NULL;
1892     
1893     while (nodes_to_delete) {
1894         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1895         Inkscape::NodePath::SubPath *sp = node->subpath;
1896         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1897         Inkscape::NodePath::Node *sample_cursor = NULL;
1898         Inkscape::NodePath::Node *sample_end = NULL;
1899         Inkscape::NodePath::Node *delete_cursor = node;
1900         bool just_delete = false;
1901         
1902         //find the start of this contiguous selection
1903         //move left to the first node that is not selected
1904         //or the start of the non-closed path
1905         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1906             delete_cursor = curr;
1907         }
1909         //just delete at the beginning of an open path
1910         if (!delete_cursor->p.other) {
1911             sample_cursor = delete_cursor;
1912             just_delete = true;
1913         } else {
1914             sample_cursor = delete_cursor->p.other;
1915         }
1916         
1917         //calculate points for each segment
1918         int rate = 5;
1919         float period = 1.0 / rate;
1920         std::vector<NR::Point> data;
1921         if (!just_delete) {
1922             data.push_back(sample_cursor->pos);
1923             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1924                 //just delete at the end of an open path
1925                 if (!sp->closed && curr->n.other == sp->last) {
1926                     just_delete = true;
1927                     break;
1928                 }
1929                 
1930                 //sample points on the contiguous selected segment
1931                 NR::Point *bez;
1932                 bez = new NR::Point [4];
1933                 bez[0] = curr->pos;
1934                 bez[1] = curr->n.pos;
1935                 bez[2] = curr->n.other->p.pos;
1936                 bez[3] = curr->n.other->pos;
1937                 for (int i=1; i<rate; i++) {
1938                     gdouble t = i * period;
1939                     NR::Point p = bezier_pt(3, bez, t);
1940                     data.push_back(p);
1941                 }
1942                 data.push_back(curr->n.other->pos);
1944                 sample_end = curr->n.other;
1945                 //break if we've come full circle or hit the end of the selection
1946                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1947                     break;
1948                 }
1949             }
1950         }
1952         if (!just_delete) {
1953             //calculate the best fitting single segment and adjust the endpoints
1954             NR::Point *adata;
1955             adata = new NR::Point [data.size()];
1956             copy(data.begin(), data.end(), adata);
1957             
1958             NR::Point *bez;
1959             bez = new NR::Point [4];
1960             //would decreasing error create a better fitting approximation?
1961             gdouble error = 1.0;
1962             gint ret;
1963             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1965             //adjust endpoints
1966             sample_cursor->n.pos = bez[1];
1967             sample_end->p.pos = bez[2];
1968         }
1969        
1970         //destroy this contiguous selection
1971         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
1972             Inkscape::NodePath::Node *temp = delete_cursor;
1973             if (delete_cursor->n.other == delete_cursor) {
1974                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
1975                 delete_cursor = NULL; 
1976             } else {
1977                 delete_cursor = delete_cursor->n.other;
1978             }
1979             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
1980             sp_nodepath_node_destroy(temp);
1981         }
1983         sp_nodepath_update_handles(nodepath);
1985         if (!g_slist_find(nodepaths, nodepath))
1986             nodepaths = g_slist_prepend (nodepaths, nodepath);
1987     }
1989     for (GSList *i = nodepaths; i; i = i->next) {
1990         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
1991         // different nodepaths will give us one undo event per nodepath
1992         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
1994         // if the entire nodepath is removed, delete the selected object.
1995         if (nodepath->subpaths == NULL ||
1996             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
1997             //at least 2
1998             sp_nodepath_get_node_count(nodepath) < 2) {
1999             SPDocument *document = sp_desktop_document (nodepath->desktop);
2000             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2001             //delete this nodepath's object, not the entire selection! (though at this time, this
2002             //does not matter)
2003             sp_selection_delete();
2004             sp_document_done (document);
2005         } else {
2006             sp_nodepath_update_repr(nodepath);
2007             sp_nodepath_update_statusbar(nodepath);
2008         }
2009     }
2011     g_slist_free (nodepaths);
2014 /**
2015  * Delete one or more selected nodes.
2016  */
2017 void sp_node_selected_delete()
2019     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2020     if (!nodepath) return;
2021     if (!nodepath->selected) return;
2023     /** \todo fixme: do it the right way */
2024     while (nodepath->selected) {
2025        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2026         sp_nodepath_node_destroy(node);
2027     }
2030     //clean up the nodepath (such as for trivial subpaths)
2031     sp_nodepath_cleanup(nodepath);
2033     sp_nodepath_update_handles(nodepath);
2035     // if the entire nodepath is removed, delete the selected object.
2036     if (nodepath->subpaths == NULL ||
2037         sp_nodepath_get_node_count(nodepath) < 2) {
2038         SPDocument *document = sp_desktop_document (nodepath->desktop);
2039         sp_selection_delete();
2040         sp_document_done (document);
2041         return;
2042     }
2044     sp_nodepath_update_repr(nodepath);
2046     sp_nodepath_update_statusbar(nodepath);
2049 /**
2050  * Delete one or more segments between two selected nodes.
2051  * This is the code for 'split'.
2052  */
2053 void
2054 sp_node_selected_delete_segment(void)
2056    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2057    Inkscape::NodePath::Node *curr, *next;     //Iterators
2059     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2060     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2062     if (g_list_length(nodepath->selected) != 2) {
2063         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2064                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2065         return;
2066     }
2068     //Selected nodes, not inclusive
2069    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2070    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2072     if ( ( a==b)                       ||  //same node
2073          (a->subpath  != b->subpath )  ||  //not the same path
2074          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2075          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2076     {
2077         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2078                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2079         return;
2080     }
2082     //###########################################
2083     //# BEGIN EDITS
2084     //###########################################
2085     //##################################
2086     //# CLOSED PATH
2087     //##################################
2088     if (a->subpath->closed) {
2091         gboolean reversed = FALSE;
2093         //Since we can go in a circle, we need to find the shorter distance.
2094         //  a->b or b->a
2095         start = end = NULL;
2096         int distance    = 0;
2097         int minDistance = 0;
2098         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2099             if (curr==b) {
2100                 //printf("a to b:%d\n", distance);
2101                 start = a;//go from a to b
2102                 end   = b;
2103                 minDistance = distance;
2104                 //printf("A to B :\n");
2105                 break;
2106             }
2107             distance++;
2108         }
2110         //try again, the other direction
2111         distance = 0;
2112         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2113             if (curr==a) {
2114                 //printf("b to a:%d\n", distance);
2115                 if (distance < minDistance) {
2116                     start    = b;  //we go from b to a
2117                     end      = a;
2118                     reversed = TRUE;
2119                     //printf("B to A\n");
2120                 }
2121                 break;
2122             }
2123             distance++;
2124         }
2127         //Copy everything from 'end' to 'start' to a new subpath
2128        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2129         for (curr=end ; curr ; curr=curr->n.other) {
2130             NRPathcode code = (NRPathcode) curr->code;
2131             if (curr == end)
2132                 code = NR_MOVETO;
2133             sp_nodepath_node_new(t, NULL,
2134                                  (Inkscape::NodePath::NodeType)curr->type, code,
2135                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2136             if (curr == start)
2137                 break;
2138         }
2139         sp_nodepath_subpath_destroy(a->subpath);
2142     }
2146     //##################################
2147     //# OPEN PATH
2148     //##################################
2149     else {
2151         //We need to get the direction of the list between A and B
2152         //Can we walk from a to b?
2153         start = end = NULL;
2154         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2155             if (curr==b) {
2156                 start = a;  //did it!  we go from a to b
2157                 end   = b;
2158                 //printf("A to B\n");
2159                 break;
2160             }
2161         }
2162         if (!start) {//didn't work?  let's try the other direction
2163             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2164                 if (curr==a) {
2165                     start = b;  //did it!  we go from b to a
2166                     end   = a;
2167                     //printf("B to A\n");
2168                     break;
2169                 }
2170             }
2171         }
2172         if (!start) {
2173             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2174                                                      _("Cannot find path between nodes."));
2175             return;
2176         }
2180         //Copy everything after 'end' to a new subpath
2181        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2182         for (curr=end ; curr ; curr=curr->n.other) {
2183             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2184                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2185         }
2187         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2188         for (curr = start->n.other ; curr  ; curr=next) {
2189             next = curr->n.other;
2190             sp_nodepath_node_destroy(curr);
2191         }
2193     }
2194     //###########################################
2195     //# END EDITS
2196     //###########################################
2198     //clean up the nodepath (such as for trivial subpaths)
2199     sp_nodepath_cleanup(nodepath);
2201     sp_nodepath_update_handles(nodepath);
2203     sp_nodepath_update_repr(nodepath);
2205     sp_nodepath_update_statusbar(nodepath);
2208 /**
2209  * Call sp_nodepath_set_line() for all selected segments.
2210  */
2211 void
2212 sp_node_selected_set_line_type(NRPathcode code)
2214     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2215     if (nodepath == NULL) return;
2217     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2218        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2219         g_assert(n->selected);
2220         if (n->p.other && n->p.other->selected) {
2221             sp_nodepath_set_line_type(n, code);
2222         }
2223     }
2225     sp_nodepath_update_repr(nodepath);
2228 /**
2229  * Call sp_nodepath_convert_node_type() for all selected nodes.
2230  */
2231 void
2232 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2234     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2235     if (nodepath == NULL) return;
2237     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2238         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2239     }
2241     sp_nodepath_update_repr(nodepath);
2244 /**
2245  * Change select status of node, update its own and neighbour handles.
2246  */
2247 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2249     node->selected = selected;
2251     if (selected) {
2252         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2253         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2254         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2255         sp_knot_update_ctrl(node->knot);
2256     } else {
2257         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2258         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2259         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2260         sp_knot_update_ctrl(node->knot);
2261     }
2263     sp_node_update_handles(node);
2264     if (node->n.other) sp_node_update_handles(node->n.other);
2265     if (node->p.other) sp_node_update_handles(node->p.other);
2268 /**
2269 \brief Select a node
2270 \param node     The node to select
2271 \param incremental   If true, add to selection, otherwise deselect others
2272 \param override   If true, always select this node, otherwise toggle selected status
2273 */
2274 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2276     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2278     if (incremental) {
2279         if (override) {
2280             if (!g_list_find(nodepath->selected, node)) {
2281                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2282             }
2283             sp_node_set_selected(node, TRUE);
2284         } else { // toggle
2285             if (node->selected) {
2286                 g_assert(g_list_find(nodepath->selected, node));
2287                 nodepath->selected = g_list_remove(nodepath->selected, node);
2288             } else {
2289                 g_assert(!g_list_find(nodepath->selected, node));
2290                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2291             }
2292             sp_node_set_selected(node, !node->selected);
2293         }
2294     } else {
2295         sp_nodepath_deselect(nodepath);
2296         nodepath->selected = g_list_prepend(nodepath->selected, node);
2297         sp_node_set_selected(node, TRUE);
2298     }
2300     sp_nodepath_update_statusbar(nodepath);
2304 /**
2305 \brief Deselect all nodes in the nodepath
2306 */
2307 void
2308 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2310     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2312     while (nodepath->selected) {
2313         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2314         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2315     }
2316     sp_nodepath_update_statusbar(nodepath);
2319 /**
2320 \brief Select or invert selection of all nodes in the nodepath
2321 */
2322 void
2323 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2325     if (!nodepath) return;
2327     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2328        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2329         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2330            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2331            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2332         }
2333     }
2336 /**
2337  * If nothing selected, does the same as sp_nodepath_select_all();
2338  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2339  * (i.e., similar to "select all in layer", with the "selected" subpaths
2340  * being treated as "layers" in the path).
2341  */
2342 void
2343 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2345     if (!nodepath) return;
2347     if (g_list_length (nodepath->selected) == 0) {
2348         sp_nodepath_select_all (nodepath, invert);
2349         return;
2350     }
2352     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2353     GSList *subpaths = NULL;
2355     for (GList *l = copy; l != NULL; l = l->next) {
2356         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2357         Inkscape::NodePath::SubPath *subpath = n->subpath;
2358         if (!g_slist_find (subpaths, subpath))
2359             subpaths = g_slist_prepend (subpaths, subpath);
2360     }
2362     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2363         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2364         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2365             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2366             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2367         }
2368     }
2370     g_slist_free (subpaths);
2371     g_list_free (copy);
2374 /**
2375  * \brief Select the node after the last selected; if none is selected,
2376  * select the first within path.
2377  */
2378 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2380     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2382    Inkscape::NodePath::Node *last = NULL;
2383     if (nodepath->selected) {
2384         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2385            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2386             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2387             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2388                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2389                 if (node->selected) {
2390                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2391                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2392                             if (spl->next) { // there's a next subpath
2393                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2394                                 last = subpath_next->first;
2395                             } else if (spl->prev) { // there's a previous subpath
2396                                 last = NULL; // to be set later to the first node of first subpath
2397                             } else {
2398                                 last = node->n.other;
2399                             }
2400                         } else {
2401                             last = node->n.other;
2402                         }
2403                     } else {
2404                         if (node->n.other) {
2405                             last = node->n.other;
2406                         } else {
2407                             if (spl->next) { // there's a next subpath
2408                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2409                                 last = subpath_next->first;
2410                             } else if (spl->prev) { // there's a previous subpath
2411                                 last = NULL; // to be set later to the first node of first subpath
2412                             } else {
2413                                 last = (Inkscape::NodePath::Node *) subpath->first;
2414                             }
2415                         }
2416                     }
2417                 }
2418             }
2419         }
2420         sp_nodepath_deselect(nodepath);
2421     }
2423     if (last) { // there's at least one more node after selected
2424         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2425     } else { // no more nodes, select the first one in first subpath
2426        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2427         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2428     }
2431 /**
2432  * \brief Select the node before the first selected; if none is selected,
2433  * select the last within path
2434  */
2435 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2437     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2439    Inkscape::NodePath::Node *last = NULL;
2440     if (nodepath->selected) {
2441         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2442            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2443             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2444                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2445                 if (node->selected) {
2446                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2447                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2448                             if (spl->prev) { // there's a prev subpath
2449                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2450                                 last = subpath_prev->last;
2451                             } else if (spl->next) { // there's a next subpath
2452                                 last = NULL; // to be set later to the last node of last subpath
2453                             } else {
2454                                 last = node->p.other;
2455                             }
2456                         } else {
2457                             last = node->p.other;
2458                         }
2459                     } else {
2460                         if (node->p.other) {
2461                             last = node->p.other;
2462                         } else {
2463                             if (spl->prev) { // there's a prev subpath
2464                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2465                                 last = subpath_prev->last;
2466                             } else if (spl->next) { // there's a next subpath
2467                                 last = NULL; // to be set later to the last node of last subpath
2468                             } else {
2469                                 last = (Inkscape::NodePath::Node *) subpath->last;
2470                             }
2471                         }
2472                     }
2473                 }
2474             }
2475         }
2476         sp_nodepath_deselect(nodepath);
2477     }
2479     if (last) { // there's at least one more node before selected
2480         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2481     } else { // no more nodes, select the last one in last subpath
2482         GList *spl = g_list_last(nodepath->subpaths);
2483        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2484         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2485     }
2488 /**
2489  * \brief Select all nodes that are within the rectangle.
2490  */
2491 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2493     if (!incremental) {
2494         sp_nodepath_deselect(nodepath);
2495     }
2497     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2498        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2499         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2500            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2502             if (b.contains(node->pos)) {
2503                 sp_nodepath_node_select(node, TRUE, TRUE);
2504             }
2505         }
2506     }
2510 /**
2511 \brief  Saves all nodes' and handles' current positions in their origin members
2512 */
2513 void
2514 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2516     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2517        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2518         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2519            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2520            n->origin = n->pos;
2521            n->p.origin = n->p.pos;
2522            n->n.origin = n->n.pos;
2523         }
2524     }
2525
2527 /**
2528 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2529 */
2530 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2532     if (!nodepath->selected) {
2533         return NULL;
2534     }
2536     GList *r = NULL;
2537     guint i = 0;
2538     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2539        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2540         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2541            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2542             i++;
2543             if (node->selected) {
2544                 r = g_list_append(r, GINT_TO_POINTER(i));
2545             }
2546         }
2547     }
2548     return r;
2551 /**
2552 \brief  Restores selection by selecting nodes whose positions are in the list
2553 */
2554 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2556     sp_nodepath_deselect(nodepath);
2558     guint i = 0;
2559     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2560        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2561         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2562            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2563             i++;
2564             if (g_list_find(r, GINT_TO_POINTER(i))) {
2565                 sp_nodepath_node_select(node, TRUE, TRUE);
2566             }
2567         }
2568     }
2572 /**
2573 \brief Adjusts handle according to node type and line code.
2574 */
2575 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2577     double len, otherlen, linelen;
2579     g_assert(node);
2581    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2582    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2584     /** \todo fixme: */
2585     if (me->other == NULL) return;
2586     if (other->other == NULL) return;
2588     /* I have line */
2590     NRPathcode mecode, ocode;
2591     if (which_adjust == 1) {
2592         mecode = (NRPathcode)me->other->code;
2593         ocode = (NRPathcode)node->code;
2594     } else {
2595         mecode = (NRPathcode)node->code;
2596         ocode = (NRPathcode)other->other->code;
2597     }
2599     if (mecode == NR_LINETO) return;
2601     /* I am curve */
2603     if (other->other == NULL) return;
2605     /* Other has line */
2607     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2609     NR::Point delta;
2610     if (ocode == NR_LINETO) {
2611         /* other is lineto, we are either smooth or symm */
2612        Inkscape::NodePath::Node *othernode = other->other;
2613         len = NR::L2(me->pos - node->pos);
2614         delta = node->pos - othernode->pos;
2615         linelen = NR::L2(delta);
2616         if (linelen < 1e-18) 
2617             return;
2618         me->pos = node->pos + (len / linelen)*delta;
2619         return;
2620     }
2622     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2624         me->pos = 2 * node->pos - other->pos;
2625         return;
2626     }
2628     /* We are smooth */
2630     len = NR::L2(me->pos - node->pos);
2631     delta = other->pos - node->pos;
2632     otherlen = NR::L2(delta);
2633     if (otherlen < 1e-18) return;
2635     me->pos = node->pos - (len / otherlen) * delta;
2638 /**
2639  \brief Adjusts both handles according to node type and line code
2640  */
2641 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2643     g_assert(node);
2645     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2647     /* we are either smooth or symm */
2649     if (node->p.other == NULL) return;
2651     if (node->n.other == NULL) return;
2653     if (node->code == NR_LINETO) {
2654         if (node->n.other->code == NR_LINETO) return;
2655         sp_node_adjust_handle(node, 1);
2656         return;
2657     }
2659     if (node->n.other->code == NR_LINETO) {
2660         if (node->code == NR_LINETO) return;
2661         sp_node_adjust_handle(node, -1);
2662         return;
2663     }
2665     /* both are curves */
2666     NR::Point const delta( node->n.pos - node->p.pos );
2668     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2669         node->p.pos = node->pos - delta / 2;
2670         node->n.pos = node->pos + delta / 2;
2671         return;
2672     }
2674     /* We are smooth */
2675     double plen = NR::L2(node->p.pos - node->pos);
2676     if (plen < 1e-18) return;
2677     double nlen = NR::L2(node->n.pos - node->pos);
2678     if (nlen < 1e-18) return;
2679     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2680     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2683 /**
2684  * Node event callback.
2685  */
2686 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2688     gboolean ret = FALSE;
2689     switch (event->type) {
2690         case GDK_ENTER_NOTIFY:
2691             active_node = n;
2692             break;
2693         case GDK_LEAVE_NOTIFY:
2694             active_node = NULL;
2695             break;
2696         case GDK_KEY_PRESS:
2697             switch (get_group0_keyval (&event->key)) {
2698                 case GDK_space:
2699                     if (event->key.state & GDK_BUTTON1_MASK) {
2700                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2701                         stamp_repr(nodepath);
2702                         ret = TRUE;
2703                     }
2704                     break;
2705                 default:
2706                     break;
2707             }
2708             break;
2709         default:
2710             break;
2711     }
2713     return ret;
2716 /**
2717  * Handle keypress on node; directly called.
2718  */
2719 gboolean node_key(GdkEvent *event)
2721     Inkscape::NodePath::Path *np;
2723     // there is no way to verify nodes so set active_node to nil when deleting!!
2724     if (active_node == NULL) return FALSE;
2726     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2727         gint ret = FALSE;
2728         switch (get_group0_keyval (&event->key)) {
2729             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2730             case GDK_BackSpace:
2731                 np = active_node->subpath->nodepath;
2732                 sp_nodepath_node_destroy(active_node);
2733                 sp_nodepath_update_repr(np);
2734                 active_node = NULL;
2735                 ret = TRUE;
2736                 break;
2737             case GDK_c:
2738                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2739                 ret = TRUE;
2740                 break;
2741             case GDK_s:
2742                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2743                 ret = TRUE;
2744                 break;
2745             case GDK_y:
2746                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2747                 ret = TRUE;
2748                 break;
2749             case GDK_b:
2750                 sp_nodepath_node_break(active_node);
2751                 ret = TRUE;
2752                 break;
2753         }
2754         return ret;
2755     }
2756     return FALSE;
2759 /**
2760  * Mouseclick on node callback.
2761  */
2762 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2764    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2766     if (state & GDK_CONTROL_MASK) {
2767         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2769         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2770             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2771                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2772             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2773                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2774             } else {
2775                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2776             }
2777             sp_nodepath_update_repr(nodepath);
2778             sp_nodepath_update_statusbar(nodepath);
2780         } else { //ctrl+alt+click: delete node
2781             GList *node_to_delete = NULL;
2782             node_to_delete = g_list_append(node_to_delete, n);
2783             sp_node_delete_preserve(node_to_delete);
2784         }
2786     } else {
2787         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2788     }
2791 /**
2792  * Mouse grabbed node callback.
2793  */
2794 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2796    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2798     if (!n->selected) {
2799         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2800     }
2802     sp_nodepath_remember_origins (n->subpath->nodepath);
2805 /**
2806  * Mouse ungrabbed node callback.
2807  */
2808 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2810    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2812    n->dragging_out = NULL;
2814    sp_nodepath_update_repr(n->subpath->nodepath);
2817 /**
2818  * The point on a line, given by its angle, closest to the given point.
2819  * \param p  A point.
2820  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2821  * \param closest  Pointer to the point struct where the result is stored.
2822  * \todo FIXME: use dot product perhaps?
2823  */
2824 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2826     if (a == HUGE_VAL) { // vertical
2827         *closest = NR::Point(0, (*p)[NR::Y]);
2828     } else {
2829         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2830         (*closest)[NR::Y] = a * (*closest)[NR::X];
2831     }
2834 /**
2835  * Distance from the point to a line given by its angle.
2836  * \param p  A point.
2837  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2838  */
2839 static double point_line_distance(NR::Point *p, double a)
2841     NR::Point c;
2842     point_line_closest(p, a, &c);
2843     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]));
2846 /**
2847  * Callback for node "request" signal.
2848  * \todo fixme: This goes to "moved" event? (lauris)
2849  */
2850 static gboolean
2851 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2853     double yn, xn, yp, xp;
2854     double an, ap, na, pa;
2855     double d_an, d_ap, d_na, d_pa;
2856     gboolean collinear = FALSE;
2857     NR::Point c;
2858     NR::Point pr;
2860    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2862    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2863    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2865        NR::Point mouse = (*p);
2867        if (!n->dragging_out) {
2868            // This is the first drag-out event; find out which handle to drag out
2869            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2870            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2872            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2873                return FALSE;
2875            Inkscape::NodePath::NodeSide *opposite;
2876            if (appr_p > appr_n) { // closer to p
2877                n->dragging_out = &n->p;
2878                opposite = &n->n;
2879                n->code = NR_CURVETO;
2880            } else if (appr_p < appr_n) { // closer to n
2881                n->dragging_out = &n->n;
2882                opposite = &n->p;
2883                n->n.other->code = NR_CURVETO;
2884            } else { // p and n nodes are the same
2885                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2886                    n->dragging_out = &n->p;
2887                    opposite = &n->n;
2888                    n->code = NR_CURVETO;
2889                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2890                    n->dragging_out = &n->n;
2891                    opposite = &n->p;
2892                    n->n.other->code = NR_CURVETO;
2893                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2894                    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);
2895                    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);
2896                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2897                        n->dragging_out = &n->n;
2898                        opposite = &n->p;
2899                        n->n.other->code = NR_CURVETO;
2900                    } else { // closer to other's n handle
2901                        n->dragging_out = &n->p;
2902                        opposite = &n->n;
2903                        n->code = NR_CURVETO;
2904                    }
2905                }
2906            }
2908            // if there's another handle, make sure the one we drag out starts parallel to it
2909            if (opposite->pos != n->pos) {
2910                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2911            }
2913            // knots might not be created yet!
2914            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2915            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2916        }
2918        // pass this on to the handle-moved callback
2919        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2920        sp_node_update_handles(n);
2921        return TRUE;
2922    }
2924     if (state & GDK_CONTROL_MASK) { // constrained motion
2926         // calculate relative distances of handles
2927         // n handle:
2928         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2929         xn = n->n.pos[NR::X] - n->pos[NR::X];
2930         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2931         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2932             if (n->n.other) { // if there is the next point
2933                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2934                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2935                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2936             }
2937         }
2938         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2939         if (yn < 0) { xn = -xn; yn = -yn; }
2941         // p handle:
2942         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2943         xp = n->p.pos[NR::X] - n->pos[NR::X];
2944         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2945         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2946             if (n->p.other) {
2947                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2948                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2949                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2950             }
2951         }
2952         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2953         if (yp < 0) { xp = -xp; yp = -yp; }
2955         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2956             // sliding on handles, only if at least one of the handles is non-vertical
2957             // (otherwise it's the same as ctrl+drag anyway)
2959             // calculate angles of the handles
2960             if (xn == 0) {
2961                 if (yn == 0) { // no handle, consider it the continuation of the other one
2962                     an = 0;
2963                     collinear = TRUE;
2964                 }
2965                 else an = 0; // vertical; set the angle to horizontal
2966             } else an = yn/xn;
2968             if (xp == 0) {
2969                 if (yp == 0) { // no handle, consider it the continuation of the other one
2970                     ap = an;
2971                 }
2972                 else ap = 0; // vertical; set the angle to horizontal
2973             } else  ap = yp/xp;
2975             if (collinear) an = ap;
2977             // angles of the perpendiculars; HUGE_VAL means vertical
2978             if (an == 0) na = HUGE_VAL; else na = -1/an;
2979             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2981             // mouse point relative to the node's original pos
2982             pr = (*p) - n->origin;
2984             // distances to the four lines (two handles and two perpendiculars)
2985             d_an = point_line_distance(&pr, an);
2986             d_na = point_line_distance(&pr, na);
2987             d_ap = point_line_distance(&pr, ap);
2988             d_pa = point_line_distance(&pr, pa);
2990             // find out which line is the closest, save its closest point in c
2991             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2992                 point_line_closest(&pr, an, &c);
2993             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2994                 point_line_closest(&pr, ap, &c);
2995             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2996                 point_line_closest(&pr, na, &c);
2997             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2998                 point_line_closest(&pr, pa, &c);
2999             }
3001             // move the node to the closest point
3002             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3003                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3004                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3006         } else {  // constraining to hor/vert
3008             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3009                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3010             } else { // snap to vert
3011                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3012             }
3013         }
3014     } else { // move freely
3015         if (state & GDK_MOD1_MASK) { // sculpt
3016             sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3017         } else {
3018             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3019                                         (*p)[NR::X] - n->pos[NR::X],
3020                                         (*p)[NR::Y] - n->pos[NR::Y],
3021                                         (state & GDK_SHIFT_MASK) == 0);
3022         }
3023     }
3025     n->subpath->nodepath->desktop->scroll_to_point(p);
3027     return TRUE;
3030 /**
3031  * Node handle clicked callback.
3032  */
3033 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3035    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3037     if (state & GDK_CONTROL_MASK) { // "delete" handle
3038         if (n->p.knot == knot) {
3039             n->p.pos = n->pos;
3040         } else if (n->n.knot == knot) {
3041             n->n.pos = n->pos;
3042         }
3043         sp_node_update_handles(n);
3044         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3045         sp_nodepath_update_repr(nodepath);
3046         sp_nodepath_update_statusbar(nodepath);
3048     } else { // just select or add to selection, depending in Shift
3049         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3050     }
3053 /**
3054  * Node handle grabbed callback.
3055  */
3056 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3058    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3060     if (!n->selected) {
3061         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3062     }
3064     // remember the origin point of the handle
3065     if (n->p.knot == knot) {
3066         n->p.origin_radial = n->p.pos - n->pos;
3067     } else if (n->n.knot == knot) {
3068         n->n.origin_radial = n->n.pos - n->pos;
3069     } else {
3070         g_assert_not_reached();
3071     }
3075 /**
3076  * Node handle ungrabbed callback.
3077  */
3078 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3080    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3082     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3083     if (n->p.knot == knot) {
3084         n->p.origin_radial.a = 0;
3085         sp_knot_set_position(knot, &n->p.pos, state);
3086     } else if (n->n.knot == knot) {
3087         n->n.origin_radial.a = 0;
3088         sp_knot_set_position(knot, &n->n.pos, state);
3089     } else {
3090         g_assert_not_reached();
3091     }
3093     sp_nodepath_update_repr(n->subpath->nodepath);
3096 /**
3097  * Node handle "request" signal callback.
3098  */
3099 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3101     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3103     Inkscape::NodePath::NodeSide *me, *opposite;
3104     gint which;
3105     if (n->p.knot == knot) {
3106         me = &n->p;
3107         opposite = &n->n;
3108         which = -1;
3109     } else if (n->n.knot == knot) {
3110         me = &n->n;
3111         opposite = &n->p;
3112         which = 1;
3113     } else {
3114         me = opposite = NULL;
3115         which = 0;
3116         g_assert_not_reached();
3117     }
3119     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3121     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3123     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3124         /* We are smooth node adjacent with line */
3125         NR::Point const delta = *p - n->pos;
3126         NR::Coord const len = NR::L2(delta);
3127         Inkscape::NodePath::Node *othernode = opposite->other;
3128         NR::Point const ndelta = n->pos - othernode->pos;
3129         NR::Coord const linelen = NR::L2(ndelta);
3130         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3131             NR::Coord const scal = dot(delta, ndelta) / linelen;
3132             (*p) = n->pos + (scal / linelen) * ndelta;
3133         }
3134         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3135     } else {
3136         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3137     }
3139     sp_node_adjust_handle(n, -which);
3141     return FALSE;
3144 /**
3145  * Node handle moved callback.
3146  */
3147 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3149    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3151    Inkscape::NodePath::NodeSide *me;
3152    Inkscape::NodePath::NodeSide *other;
3153     if (n->p.knot == knot) {
3154         me = &n->p;
3155         other = &n->n;
3156     } else if (n->n.knot == knot) {
3157         me = &n->n;
3158         other = &n->p;
3159     } else {
3160         me = NULL;
3161         other = NULL;
3162         g_assert_not_reached();
3163     }
3165     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3166     Radial rme(me->pos - n->pos);
3167     Radial rother(other->pos - n->pos);
3168     Radial rnew(*p - n->pos);
3170     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3171         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3172         /* 0 interpreted as "no snapping". */
3174         // The closest PI/snaps angle, starting from zero.
3175         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3176         if (me->origin_radial.a == HUGE_VAL) {
3177             // ortho doesn't exist: original handle was zero length.
3178             rnew.a = a_snapped;
3179         } else {
3180             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3181              * its opposite and perpendiculars). */
3182             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3184             // Snap to the closest.
3185             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3186                        ? a_snapped
3187                        : a_ortho );
3188         }
3189     }
3191     if (state & GDK_MOD1_MASK) {
3192         // lock handle length
3193         rnew.r = me->origin_radial.r;
3194     }
3196     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3197         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3198         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3199         rother.a += rnew.a - rme.a;
3200         other->pos = NR::Point(rother) + n->pos;
3201         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3202         sp_knot_set_position(other->knot, &other->pos, 0);
3203     }
3205     me->pos = NR::Point(rnew) + n->pos;
3206     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3208     // this is what sp_knot_set_position does, but without emitting the signal:
3209     // we cannot emit a "moved" signal because we're now processing it
3210     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3212     knot->desktop->set_coordinate_status(me->pos);
3214     update_object(n->subpath->nodepath);
3216     /* status text */
3217     SPDesktop *desktop = n->subpath->nodepath->desktop;
3218     if (!desktop) return;
3219     SPEventContext *ec = desktop->event_context;
3220     if (!ec) return;
3221     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3222     if (!mc) return;
3224     double degrees = 180 / M_PI * rnew.a;
3225     if (degrees > 180) degrees -= 360;
3226     if (degrees < -180) degrees += 360;
3227     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3228         degrees = angle_to_compass (degrees);
3230     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3232     mc->setF(Inkscape::NORMAL_MESSAGE,
3233          _("<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);
3235     g_string_free(length, TRUE);
3238 /**
3239  * Node handle event callback.
3240  */
3241 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3243     gboolean ret = FALSE;
3244     switch (event->type) {
3245         case GDK_KEY_PRESS:
3246             switch (get_group0_keyval (&event->key)) {
3247                 case GDK_space:
3248                     if (event->key.state & GDK_BUTTON1_MASK) {
3249                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3250                         stamp_repr(nodepath);
3251                         ret = TRUE;
3252                     }
3253                     break;
3254                 default:
3255                     break;
3256             }
3257             break;
3258         default:
3259             break;
3260     }
3262     return ret;
3265 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3266                                  Radial &rme, Radial &rother, gboolean const both)
3268     rme.a += angle;
3269     if ( both
3270          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3271          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3272     {
3273         rother.a += angle;
3274     }
3277 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3278                                         Radial &rme, Radial &rother, gboolean const both)
3280     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3282     gdouble r;
3283     if ( both
3284          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3285          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3286     {
3287         r = MAX(rme.r, rother.r);
3288     } else {
3289         r = rme.r;
3290     }
3292     gdouble const weird_angle = atan2(norm_angle, r);
3293 /* Bulia says norm_angle is just the visible distance that the
3294  * object's end must travel on the screen.  Left as 'angle' for want of
3295  * a better name.*/
3297     rme.a += weird_angle;
3298     if ( both
3299          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3300          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3301     {
3302         rother.a += weird_angle;
3303     }
3306 /**
3307  * Rotate one node.
3308  */
3309 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3311     Inkscape::NodePath::NodeSide *me, *other;
3312     bool both = false;
3314     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3315     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3317     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3318         me = &(n->p);
3319         other = &(n->n);
3320     } else if (!n->p.other) {
3321         me = &(n->n);
3322         other = &(n->p);
3323     } else {
3324         if (which > 0) { // right handle
3325             if (xn > xp) {
3326                 me = &(n->n);
3327                 other = &(n->p);
3328             } else {
3329                 me = &(n->p);
3330                 other = &(n->n);
3331             }
3332         } else if (which < 0){ // left handle
3333             if (xn <= xp) {
3334                 me = &(n->n);
3335                 other = &(n->p);
3336             } else {
3337                 me = &(n->p);
3338                 other = &(n->n);
3339             }
3340         } else { // both handles
3341             me = &(n->n);
3342             other = &(n->p);
3343             both = true;
3344         }
3345     }
3347     Radial rme(me->pos - n->pos);
3348     Radial rother(other->pos - n->pos);
3350     if (screen) {
3351         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3352     } else {
3353         node_rotate_one_internal (*n, angle, rme, rother, both);
3354     }
3356     me->pos = n->pos + NR::Point(rme);
3358     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3359         other->pos =  n->pos + NR::Point(rother);
3360     }
3362     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3363     // so here we just move all the knots without emitting move signals, for speed
3364     sp_node_update_handles(n, false);
3367 /**
3368  * Rotate selected nodes.
3369  */
3370 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3372     if (!nodepath || !nodepath->selected) return;
3374     if (g_list_length(nodepath->selected) == 1) {
3375        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3376         node_rotate_one (n, angle, which, screen);
3377     } else {
3378        // rotate as an object:
3380         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3381         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3382         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3383             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3384             box.expandTo (n->pos); // contain all selected nodes
3385         }
3387         gdouble rot;
3388         if (screen) {
3389             gdouble const zoom = nodepath->desktop->current_zoom();
3390             gdouble const zmove = angle / zoom;
3391             gdouble const r = NR::L2(box.max() - box.midpoint());
3392             rot = atan2(zmove, r);
3393         } else {
3394             rot = angle;
3395         }
3397         NR::Matrix t =
3398             NR::Matrix (NR::translate(-box.midpoint())) *
3399             NR::Matrix (NR::rotate(rot)) *
3400             NR::Matrix (NR::translate(box.midpoint()));
3402         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3403             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3404             n->pos *= t;
3405             n->n.pos *= t;
3406             n->p.pos *= t;
3407             sp_node_update_handles(n, false);
3408         }
3409     }
3411     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3414 /**
3415  * Scale one node.
3416  */
3417 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3419     bool both = false;
3420     Inkscape::NodePath::NodeSide *me, *other;
3422     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3423     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3425     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3426         me = &(n->p);
3427         other = &(n->n);
3428         n->code = NR_CURVETO;
3429     } else if (!n->p.other) {
3430         me = &(n->n);
3431         other = &(n->p);
3432         if (n->n.other)
3433             n->n.other->code = NR_CURVETO;
3434     } else {
3435         if (which > 0) { // right handle
3436             if (xn > xp) {
3437                 me = &(n->n);
3438                 other = &(n->p);
3439                 if (n->n.other)
3440                     n->n.other->code = NR_CURVETO;
3441             } else {
3442                 me = &(n->p);
3443                 other = &(n->n);
3444                 n->code = NR_CURVETO;
3445             }
3446         } else if (which < 0){ // left handle
3447             if (xn <= xp) {
3448                 me = &(n->n);
3449                 other = &(n->p);
3450                 if (n->n.other)
3451                     n->n.other->code = NR_CURVETO;
3452             } else {
3453                 me = &(n->p);
3454                 other = &(n->n);
3455                 n->code = NR_CURVETO;
3456             }
3457         } else { // both handles
3458             me = &(n->n);
3459             other = &(n->p);
3460             both = true;
3461             n->code = NR_CURVETO;
3462             if (n->n.other)
3463                 n->n.other->code = NR_CURVETO;
3464         }
3465     }
3467     Radial rme(me->pos - n->pos);
3468     Radial rother(other->pos - n->pos);
3470     rme.r += grow;
3471     if (rme.r < 0) rme.r = 0;
3472     if (rme.a == HUGE_VAL) {
3473         if (me->other) { // if direction is unknown, initialize it towards the next node
3474             Radial rme_next(me->other->pos - n->pos);
3475             rme.a = rme_next.a;
3476         } else { // if there's no next, initialize to 0
3477             rme.a = 0;
3478         }
3479     }
3480     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3481         rother.r += grow;
3482         if (rother.r < 0) rother.r = 0;
3483         if (rother.a == HUGE_VAL) {
3484             rother.a = rme.a + M_PI;
3485         }
3486     }
3488     me->pos = n->pos + NR::Point(rme);
3490     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3491         other->pos = n->pos + NR::Point(rother);
3492     }
3494     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3495     // so here we just move all the knots without emitting move signals, for speed
3496     sp_node_update_handles(n, false);
3499 /**
3500  * Scale selected nodes.
3501  */
3502 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3504     if (!nodepath || !nodepath->selected) return;
3506     if (g_list_length(nodepath->selected) == 1) {
3507         // scale handles of the single selected node
3508         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3509         node_scale_one (n, grow, which);
3510     } else {
3511         // scale nodes as an "object":
3513         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3514         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3515         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3516             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3517             box.expandTo (n->pos); // contain all selected nodes
3518         }
3520         double scale = (box.maxExtent() + grow)/box.maxExtent();
3522         NR::Matrix t =
3523             NR::Matrix (NR::translate(-box.midpoint())) *
3524             NR::Matrix (NR::scale(scale, scale)) *
3525             NR::Matrix (NR::translate(box.midpoint()));
3527         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3528             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3529             n->pos *= t;
3530             n->n.pos *= t;
3531             n->p.pos *= t;
3532             sp_node_update_handles(n, false);
3533         }
3534     }
3536     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3539 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3541     if (!nodepath) return;
3542     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3545 /**
3546  * Flip selected nodes horizontally/vertically.
3547  */
3548 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3550     if (!nodepath || !nodepath->selected) return;
3552     if (g_list_length(nodepath->selected) == 1) {
3553         // flip handles of the single selected node
3554         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3555         double temp = n->p.pos[axis];
3556         n->p.pos[axis] = n->n.pos[axis];
3557         n->n.pos[axis] = temp;
3558         sp_node_update_handles(n, false);
3559     } else {
3560         // scale nodes as an "object":
3562         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3563         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3564         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3565             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3566             box.expandTo (n->pos); // contain all selected nodes
3567         }
3569         NR::Matrix t =
3570             NR::Matrix (NR::translate(-box.midpoint())) *
3571             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3572             NR::Matrix (NR::translate(box.midpoint()));
3574         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3575             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3576             n->pos *= t;
3577             n->n.pos *= t;
3578             n->p.pos *= t;
3579             sp_node_update_handles(n, false);
3580         }
3581     }
3583     sp_nodepath_update_repr(nodepath);
3586 //-----------------------------------------------
3587 /**
3588  * Return new subpath under given nodepath.
3589  */
3590 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3592     g_assert(nodepath);
3593     g_assert(nodepath->desktop);
3595    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3597     s->nodepath = nodepath;
3598     s->closed = FALSE;
3599     s->nodes = NULL;
3600     s->first = NULL;
3601     s->last = NULL;
3603     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3604     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3605     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3607     return s;
3610 /**
3611  * Destroy nodes in subpath, then subpath itself.
3612  */
3613 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3615     g_assert(subpath);
3616     g_assert(subpath->nodepath);
3617     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3619     while (subpath->nodes) {
3620         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3621     }
3623     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3625     g_free(subpath);
3628 /**
3629  * Link head to tail in subpath.
3630  */
3631 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3633     g_assert(!sp->closed);
3634     g_assert(sp->last != sp->first);
3635     g_assert(sp->first->code == NR_MOVETO);
3637     sp->closed = TRUE;
3639     //Link the head to the tail
3640     sp->first->p.other = sp->last;
3641     sp->last->n.other  = sp->first;
3642     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3643     sp->first          = sp->last;
3645     //Remove the extra end node
3646     sp_nodepath_node_destroy(sp->last->n.other);
3649 /**
3650  * Open closed (loopy) subpath at node.
3651  */
3652 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3654     g_assert(sp->closed);
3655     g_assert(n->subpath == sp);
3656     g_assert(sp->first == sp->last);
3658     /* We create new startpoint, current node will become last one */
3660    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3661                                                 &n->pos, &n->pos, &n->n.pos);
3664     sp->closed        = FALSE;
3666     //Unlink to make a head and tail
3667     sp->first         = new_path;
3668     sp->last          = n;
3669     n->n.other        = NULL;
3670     new_path->p.other = NULL;
3673 /**
3674  * Returns area in triangle given by points; may be negative.
3675  */
3676 inline double
3677 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3679     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]);
3682 /**
3683  * Return new node in subpath with given properties.
3684  * \param pos Position of node.
3685  * \param ppos Handle position in previous direction
3686  * \param npos Handle position in previous direction
3687  */
3688 Inkscape::NodePath::Node *
3689 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)
3691     g_assert(sp);
3692     g_assert(sp->nodepath);
3693     g_assert(sp->nodepath->desktop);
3695     if (nodechunk == NULL)
3696         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3698     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3700     n->subpath  = sp;
3702     if (type != Inkscape::NodePath::NODE_NONE) {
3703         // use the type from sodipodi:nodetypes
3704         n->type = type;
3705     } else {
3706         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3707             // points are (almost) collinear
3708             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3709                 // endnode, or a node with a retracted handle
3710                 n->type = Inkscape::NodePath::NODE_CUSP;
3711             } else {
3712                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3713             }
3714         } else {
3715             n->type = Inkscape::NodePath::NODE_CUSP;
3716         }
3717     }
3719     n->code     = code;
3720     n->selected = FALSE;
3721     n->pos      = *pos;
3722     n->p.pos    = *ppos;
3723     n->n.pos    = *npos;
3725     n->dragging_out = NULL;
3727     Inkscape::NodePath::Node *prev;
3728     if (next) {
3729         //g_assert(g_list_find(sp->nodes, next));
3730         prev = next->p.other;
3731     } else {
3732         prev = sp->last;
3733     }
3735     if (prev)
3736         prev->n.other = n;
3737     else
3738         sp->first = n;
3740     if (next)
3741         next->p.other = n;
3742     else
3743         sp->last = n;
3745     n->p.other = prev;
3746     n->n.other = next;
3748     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"));
3749     sp_knot_set_position(n->knot, pos, 0);
3751     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3752     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3753     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3754     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3755     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3756     sp_knot_update_ctrl(n->knot);
3758     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3759     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3760     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3761     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3762     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3763     sp_knot_show(n->knot);
3765     // We only create handle knots and lines on demand
3766     n->p.knot = NULL;
3767     n->p.line = NULL;
3768     n->n.knot = NULL;
3769     n->n.line = NULL;
3771     sp->nodes = g_list_prepend(sp->nodes, n);
3773     return n;
3776 /**
3777  * Destroy node and its knots, link neighbors in subpath.
3778  */
3779 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3781     g_assert(node);
3782     g_assert(node->subpath);
3783     g_assert(SP_IS_KNOT(node->knot));
3785    Inkscape::NodePath::SubPath *sp = node->subpath;
3787     if (node->selected) { // first, deselect
3788         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3789         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3790     }
3792     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3794     g_object_unref(G_OBJECT(node->knot));
3795     if (node->p.knot)
3796         g_object_unref(G_OBJECT(node->p.knot));
3797     if (node->n.knot)
3798         g_object_unref(G_OBJECT(node->n.knot));
3800     if (node->p.line)
3801         gtk_object_destroy(GTK_OBJECT(node->p.line));
3802     if (node->n.line)
3803         gtk_object_destroy(GTK_OBJECT(node->n.line));
3805     if (sp->nodes) { // there are others nodes on the subpath
3806         if (sp->closed) {
3807             if (sp->first == node) {
3808                 g_assert(sp->last == node);
3809                 sp->first = node->n.other;
3810                 sp->last = sp->first;
3811             }
3812             node->p.other->n.other = node->n.other;
3813             node->n.other->p.other = node->p.other;
3814         } else {
3815             if (sp->first == node) {
3816                 sp->first = node->n.other;
3817                 sp->first->code = NR_MOVETO;
3818             }
3819             if (sp->last == node) sp->last = node->p.other;
3820             if (node->p.other) node->p.other->n.other = node->n.other;
3821             if (node->n.other) node->n.other->p.other = node->p.other;
3822         }
3823     } else { // this was the last node on subpath
3824         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3825     }
3827     g_mem_chunk_free(nodechunk, node);
3830 /**
3831  * Returns one of the node's two sides.
3832  * \param which Indicates which side.
3833  * \return Pointer to previous node side if which==-1, next if which==1.
3834  */
3835 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3837     g_assert(node);
3839     switch (which) {
3840         case -1:
3841             return &node->p;
3842         case 1:
3843             return &node->n;
3844         default:
3845             break;
3846     }
3848     g_assert_not_reached();
3850     return NULL;
3853 /**
3854  * Return the other side of the node, given one of its sides.
3855  */
3856 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
3858     g_assert(node);
3860     if (me == &node->p) return &node->n;
3861     if (me == &node->n) return &node->p;
3863     g_assert_not_reached();
3865     return NULL;
3868 /**
3869  * Return NRPathcode on the given side of the node.
3870  */
3871 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3873     g_assert(node);
3875     if (me == &node->p) {
3876         if (node->p.other) return (NRPathcode)node->code;
3877         return NR_MOVETO;
3878     }
3880     if (me == &node->n) {
3881         if (node->n.other) return (NRPathcode)node->n.other->code;
3882         return NR_MOVETO;
3883     }
3885     g_assert_not_reached();
3887     return NR_END;
3890 /**
3891  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3892  */
3893 Inkscape::NodePath::Node *
3894 sp_nodepath_get_node_by_index(int index)
3896     Inkscape::NodePath::Node *e = NULL;
3898     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3899     if (!nodepath) {
3900         return e;
3901     }
3903     //find segment
3904     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3906         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3907         int n = g_list_length(sp->nodes);
3908         if (sp->closed) {
3909             n++;
3910         }
3912         //if the piece belongs to this subpath grab it
3913         //otherwise move onto the next subpath
3914         if (index < n) {
3915             e = sp->first;
3916             for (int i = 0; i < index; ++i) {
3917                 e = e->n.other;
3918             }
3919             break;
3920         } else {
3921             if (sp->closed) {
3922                 index -= (n+1);
3923             } else {
3924                 index -= n;
3925             }
3926         }
3927     }
3929     return e;
3932 /**
3933  * Returns plain text meaning of node type.
3934  */
3935 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3937     unsigned retracted = 0;
3938     bool endnode = false;
3940     for (int which = -1; which <= 1; which += 2) {
3941         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3942         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3943             retracted ++;
3944         if (!side->other)
3945             endnode = true;
3946     }
3948     if (retracted == 0) {
3949         if (endnode) {
3950                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3951                 return _("end node");
3952         } else {
3953             switch (node->type) {
3954                 case Inkscape::NodePath::NODE_CUSP:
3955                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3956                     return _("cusp");
3957                 case Inkscape::NodePath::NODE_SMOOTH:
3958                     // TRANSLATORS: "smooth" is an adjective here
3959                     return _("smooth");
3960                 case Inkscape::NodePath::NODE_SYMM:
3961                     return _("symmetric");
3962             }
3963         }
3964     } else if (retracted == 1) {
3965         if (endnode) {
3966             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3967             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3968         } else {
3969             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3970         }
3971     } else {
3972         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3973     }
3975     return NULL;
3978 /**
3979  * Handles content of statusbar as long as node tool is active.
3980  */
3981 void
3982 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3984     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");
3985     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3987     gint total_nodes = sp_nodepath_get_node_count(nodepath);
3988     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
3989     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
3990     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
3992     SPDesktop *desktop = NULL;
3993     if (nodepath) {
3994         desktop = nodepath->desktop;
3995     } else {
3996         desktop = SP_ACTIVE_DESKTOP;
3997     }
3999     SPEventContext *ec = desktop->event_context;
4000     if (!ec) return;
4001     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4002     if (!mc) return;
4004     if (selected_nodes == 0) {
4005         Inkscape::Selection *sel = desktop->selection;
4006         if (!sel || sel->isEmpty()) {
4007             mc->setF(Inkscape::NORMAL_MESSAGE,
4008                      _("Select a single object to edit its nodes or handles."));
4009         } else {
4010             if (nodepath) {
4011             mc->setF(Inkscape::NORMAL_MESSAGE,
4012                      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.",
4013                               "<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.",
4014                               total_nodes),
4015                      total_nodes);
4016             } else {
4017                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4018                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4019                 } else {
4020                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4021                 }
4022             }
4023         }
4024     } else if (nodepath && selected_nodes == 1) {
4025         mc->setF(Inkscape::NORMAL_MESSAGE,
4026                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4027                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4028                           total_nodes),
4029                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4030     } else {
4031         if (selected_subpaths > 1) {
4032             mc->setF(Inkscape::NORMAL_MESSAGE,
4033                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4034                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4035                               total_nodes),
4036                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4037         } else {
4038             mc->setF(Inkscape::NORMAL_MESSAGE,
4039                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4040                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4041                               total_nodes),
4042                      selected_nodes, total_nodes, when_selected);
4043         }
4044     }
4048 /*
4049   Local Variables:
4050   mode:c++
4051   c-file-style:"stroustrup"
4052   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4053   indent-tabs-mode:nil
4054   fill-column:99
4055   End:
4056 */
4057 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :