Code

use spatial/linear distance method depending on whether selection is in more than...
[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)
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;
190     // we need to update item's transform from the repr here,
191     // because they may be out of sync when we respond
192     // to a change in repr by regenerating nodepath     --bb
193     sp_object_read_attr(SP_OBJECT(item), "transform");
195     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
196     np->d2i  = np->i2d.inverse();
197     np->repr = repr;
199     // create the subpath(s) from the bpath
200     NArtBpath *b = bpath;
201     while (b->code != NR_END) {
202         b = subpath_from_bpath(np, b, typestr + (b - bpath));
203     }
205     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
206     np->subpaths = g_list_reverse(np->subpaths);
208     g_free(typestr);
209     sp_curve_unref(curve);
211     // create the livarot representation from the same item
212     np->livarot_path = Path_for_item(item, true, true);
213     if (np->livarot_path)
214         np->livarot_path->ConvertWithBackData(0.01);
216     return np;
219 /**
220  * Destroys nodepath's subpaths, then itself, also tell context about it.
221  */
222 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
224     if (!np)  //soft fail, like delete
225         return;
227     while (np->subpaths) {
228         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
229     }
231     //Inform the context that made me, if any, that I am gone.
232     if (np->nodeContext)
233         np->nodeContext->nodepath = NULL;
235     g_assert(!np->selected);
237     if (np->livarot_path) {
238         delete np->livarot_path;
239         np->livarot_path = NULL;
240     }
242     np->desktop = NULL;
244     g_free(np);
248 /**
249  *  Return the node count of a given NodeSubPath.
250  */
251 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
253     if (!subpath)
254         return 0;
255     gint nodeCount = g_list_length(subpath->nodes);
256     return nodeCount;
259 /**
260  *  Return the node count of a given NodePath.
261  */
262 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
264     if (!np)
265         return 0;
266     gint nodeCount = 0;
267     for (GList *item = np->subpaths ; item ; item=item->next) {
268        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
269         nodeCount += g_list_length(subpath->nodes);
270     }
271     return nodeCount;
274 /**
275  *  Return the subpath count of a given NodePath.
276  */
277 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
279     if (!np)
280         return 0;
281     return g_list_length (np->subpaths);
284 /**
285  *  Return the selected node count of a given NodePath.
286  */
287 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
289     if (!np)
290         return 0;
291     return g_list_length (np->selected);
294 /**
295  *  Return the number of subpaths where nodes are selected in a given NodePath.
296  */
297 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
299     if (!np)
300         return 0;
301     if (!np->selected)
302         return 0;
303     if (!np->selected->next)
304         return 1;
305     gint count = 0;
306     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
307         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
308         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
309             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
310             if (node->selected) {
311                 count ++;
312                 break;
313             }
314         }
315     }
316     return count;
318  
319 /**
320  * Clean up a nodepath after editing.
321  *
322  * Currently we are deleting trivial subpaths.
323  */
324 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
326     GList *badSubPaths = NULL;
328     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
329     for (GList *l = nodepath->subpaths; l ; l=l->next) {
330        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
331        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
332             badSubPaths = g_list_append(badSubPaths, sp);
333     }
335     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
336     //also removes the subpath from nodepath->subpaths
337     for (GList *l = badSubPaths; l ; l=l->next) {
338        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
339         sp_nodepath_subpath_destroy(sp);
340     }
342     g_list_free(badSubPaths);
345 /**
346  * Create new nodepath from b, make it subpath of np.
347  * \param t The node type.
348  * \todo Fixme: t should be a proper type, rather than gchar
349  */
350 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
352     NR::Point ppos, pos, npos;
354     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
356     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
357     bool const closed = (b->code == NR_MOVETO);
359     pos = NR::Point(b->x3, b->y3) * np->i2d;
360     if (b[1].code == NR_CURVETO) {
361         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
362     } else {
363         npos = pos;
364     }
365     Inkscape::NodePath::Node *n;
366     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
367     g_assert(sp->first == n);
368     g_assert(sp->last  == n);
370     b++;
371     t++;
372     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
373         pos = NR::Point(b->x3, b->y3) * np->i2d;
374         if (b->code == NR_CURVETO) {
375             ppos = NR::Point(b->x2, b->y2) * np->i2d;
376         } else {
377             ppos = pos;
378         }
379         if (b[1].code == NR_CURVETO) {
380             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
381         } else {
382             npos = pos;
383         }
384         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
385         b++;
386         t++;
387     }
389     if (closed) sp_nodepath_subpath_close(sp);
391     return b;
394 /**
395  * Convert from sodipodi:nodetypes to new style type string.
396  */
397 static gchar *parse_nodetypes(gchar const *types, gint length)
399     g_assert(length > 0);
401     gchar *typestr = g_new(gchar, length + 1);
403     gint pos = 0;
405     if (types) {
406         for (gint i = 0; types[i] && ( i < length ); i++) {
407             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
408             if (types[i] != '\0') {
409                 switch (types[i]) {
410                     case 's':
411                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
412                         break;
413                     case 'z':
414                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
415                         break;
416                     case 'c':
417                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
418                         break;
419                     default:
420                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
421                         break;
422                 }
423             }
424         }
425     }
427     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
429     return typestr;
432 /**
433  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
434  * updated but repr is not (for speed). Used during curve and node drag.
435  */
436 static void update_object(Inkscape::NodePath::Path *np)
438     g_assert(np);
440     SPCurve *curve = create_curve(np);
442     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
444     sp_curve_unref(curve);
447 /**
448  * Update XML path node with data from path object.
449  */
450 static void update_repr_internal(Inkscape::NodePath::Path *np)
452     g_assert(np);
454     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
456     SPCurve *curve = create_curve(np);
457     gchar *typestr = create_typestr(np);
458     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
460     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
461         np->local_change++;
462         repr->setAttribute("d", svgpath);
463     }
465     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
466         np->local_change++;
467         repr->setAttribute("sodipodi:nodetypes", typestr);
468     }
470     g_free(svgpath);
471     g_free(typestr);
472     sp_curve_unref(curve);
475 /**
476  * Update XML path node with data from path object, commit changes forever.
477  */
478 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np)
480     update_repr_internal(np);
481     sp_document_done(sp_desktop_document(np->desktop));
483     if (np->livarot_path) {
484         delete np->livarot_path;
485         np->livarot_path = NULL;
486     }
488     if (np->path && SP_IS_ITEM(np->path)) {
489         np->livarot_path = Path_for_item (np->path, true, true);
490         if (np->livarot_path)
491             np->livarot_path->ConvertWithBackData(0.01);
492     }
495 /**
496  * Update XML path node with data from path object, commit changes with undo.
497  */
498 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key)
500     update_repr_internal(np);
501     sp_document_maybe_done(sp_desktop_document(np->desktop), key);
503     if (np->livarot_path) {
504         delete np->livarot_path;
505         np->livarot_path = NULL;
506     }
508     if (np->path && SP_IS_ITEM(np->path)) {
509         np->livarot_path = Path_for_item (np->path, true, true);
510         if (np->livarot_path)
511             np->livarot_path->ConvertWithBackData(0.01);
512     }
515 /**
516  * Make duplicate of path, replace corresponding XML node in tree, commit.
517  */
518 static void stamp_repr(Inkscape::NodePath::Path *np)
520     g_assert(np);
522     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
523     Inkscape::XML::Node *new_repr = old_repr->duplicate();
525     // remember the position of the item
526     gint pos = old_repr->position();
527     // remember parent
528     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
530     SPCurve *curve = create_curve(np);
531     gchar *typestr = create_typestr(np);
533     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
535     new_repr->setAttribute("d", svgpath);
536     new_repr->setAttribute("sodipodi:nodetypes", typestr);
538     // add the new repr to the parent
539     parent->appendChild(new_repr);
540     // move to the saved position
541     new_repr->setPosition(pos > 0 ? pos : 0);
543     sp_document_done(sp_desktop_document(np->desktop));
545     Inkscape::GC::release(new_repr);
546     g_free(svgpath);
547     g_free(typestr);
548     sp_curve_unref(curve);
551 /**
552  * Create curve from path.
553  */
554 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
556     SPCurve *curve = sp_curve_new();
558     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
559        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
560         sp_curve_moveto(curve,
561                         sp->first->pos * np->d2i);
562        Inkscape::NodePath::Node *n = sp->first->n.other;
563         while (n) {
564             NR::Point const end_pt = n->pos * np->d2i;
565             switch (n->code) {
566                 case NR_LINETO:
567                     sp_curve_lineto(curve, end_pt);
568                     break;
569                 case NR_CURVETO:
570                     sp_curve_curveto(curve,
571                                      n->p.other->n.pos * np->d2i,
572                                      n->p.pos * np->d2i,
573                                      end_pt);
574                     break;
575                 default:
576                     g_assert_not_reached();
577                     break;
578             }
579             if (n != sp->last) {
580                 n = n->n.other;
581             } else {
582                 n = NULL;
583             }
584         }
585         if (sp->closed) {
586             sp_curve_closepath(curve);
587         }
588     }
590     return curve;
593 /**
594  * Convert path type string to sodipodi:nodetypes style.
595  */
596 static gchar *create_typestr(Inkscape::NodePath::Path *np)
598     gchar *typestr = g_new(gchar, 32);
599     gint len = 32;
600     gint pos = 0;
602     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
603        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
605         if (pos >= len) {
606             typestr = g_renew(gchar, typestr, len + 32);
607             len += 32;
608         }
610         typestr[pos++] = 'c';
612        Inkscape::NodePath::Node *n;
613         n = sp->first->n.other;
614         while (n) {
615             gchar code;
617             switch (n->type) {
618                 case Inkscape::NodePath::NODE_CUSP:
619                     code = 'c';
620                     break;
621                 case Inkscape::NodePath::NODE_SMOOTH:
622                     code = 's';
623                     break;
624                 case Inkscape::NodePath::NODE_SYMM:
625                     code = 'z';
626                     break;
627                 default:
628                     g_assert_not_reached();
629                     code = '\0';
630                     break;
631             }
633             if (pos >= len) {
634                 typestr = g_renew(gchar, typestr, len + 32);
635                 len += 32;
636             }
638             typestr[pos++] = code;
640             if (n != sp->last) {
641                 n = n->n.other;
642             } else {
643                 n = NULL;
644             }
645         }
646     }
648     if (pos >= len) {
649         typestr = g_renew(gchar, typestr, len + 1);
650         len += 1;
651     }
653     typestr[pos++] = '\0';
655     return typestr;
658 /**
659  * Returns current path in context.
660  */
661 static Inkscape::NodePath::Path *sp_nodepath_current()
663     if (!SP_ACTIVE_DESKTOP) {
664         return NULL;
665     }
667     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
669     if (!SP_IS_NODE_CONTEXT(event_context)) {
670         return NULL;
671     }
673     return SP_NODE_CONTEXT(event_context)->nodepath;
678 /**
679  \brief Fills node and handle positions for three nodes, splitting line
680   marked by end at distance t.
681  */
682 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
684     g_assert(new_path != NULL);
685     g_assert(end      != NULL);
687     g_assert(end->p.other == new_path);
688    Inkscape::NodePath::Node *start = new_path->p.other;
689     g_assert(start);
691     if (end->code == NR_LINETO) {
692         new_path->type =Inkscape::NodePath::NODE_CUSP;
693         new_path->code = NR_LINETO;
694         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
695     } else {
696         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
697         new_path->code = NR_CURVETO;
698         gdouble s      = 1 - t;
699         for (int dim = 0; dim < 2; dim++) {
700             NR::Coord const f000 = start->pos[dim];
701             NR::Coord const f001 = start->n.pos[dim];
702             NR::Coord const f011 = end->p.pos[dim];
703             NR::Coord const f111 = end->pos[dim];
704             NR::Coord const f00t = s * f000 + t * f001;
705             NR::Coord const f01t = s * f001 + t * f011;
706             NR::Coord const f11t = s * f011 + t * f111;
707             NR::Coord const f0tt = s * f00t + t * f01t;
708             NR::Coord const f1tt = s * f01t + t * f11t;
709             NR::Coord const fttt = s * f0tt + t * f1tt;
710             start->n.pos[dim]    = f00t;
711             new_path->p.pos[dim] = f0tt;
712             new_path->pos[dim]   = fttt;
713             new_path->n.pos[dim] = f1tt;
714             end->p.pos[dim]      = f11t;
715         }
716     }
719 /**
720  * Adds new node on direct line between two nodes, activates handles of all
721  * three nodes.
722  */
723 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
725     g_assert(end);
726     g_assert(end->subpath);
727     g_assert(g_list_find(end->subpath->nodes, end));
729    Inkscape::NodePath::Node *start = end->p.other;
730     g_assert( start->n.other == end );
731    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
732                                                end,
733                                               Inkscape::NodePath::NODE_SMOOTH,
734                                                (NRPathcode)end->code,
735                                                &start->pos, &start->pos, &start->n.pos);
736     sp_nodepath_line_midpoint(newnode, end, t);
738     sp_node_update_handles(start);
739     sp_node_update_handles(newnode);
740     sp_node_update_handles(end);
742     return newnode;
745 /**
746 \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
747 */
748 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
750     g_assert(node);
751     g_assert(node->subpath);
752     g_assert(g_list_find(node->subpath->nodes, node));
754    Inkscape::NodePath::SubPath *sp = node->subpath;
755     Inkscape::NodePath::Path *np    = sp->nodepath;
757     if (sp->closed) {
758         sp_nodepath_subpath_open(sp, node);
759         return sp->first;
760     } else {
761         // no break for end nodes
762         if (node == sp->first) return NULL;
763         if (node == sp->last ) return NULL;
765         // create a new subpath
766        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
768         // duplicate the break node as start of the new subpath
769        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
771         while (node->n.other) { // copy the remaining nodes into the new subpath
772            Inkscape::NodePath::Node *n  = node->n.other;
773            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);
774             if (n->selected) {
775                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
776             }
777             sp_nodepath_node_destroy(n); // remove the point on the original subpath
778         }
780         return newnode;
781     }
784 /**
785  * Duplicate node and connect to neighbours.
786  */
787 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
789     g_assert(node);
790     g_assert(node->subpath);
791     g_assert(g_list_find(node->subpath->nodes, node));
793    Inkscape::NodePath::SubPath *sp = node->subpath;
795     NRPathcode code = (NRPathcode) node->code;
796     if (code == NR_MOVETO) { // if node is the endnode,
797         node->code = NR_LINETO; // new one is inserted before it, so change that to line
798     }
800     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
802     if (!node->n.other || !node->p.other) // if node is an endnode, select it
803         return node;
804     else
805         return newnode; // otherwise select the newly created node
808 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
810     node->p.pos = (node->pos + (node->pos - node->n.pos));
813 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
815     node->n.pos = (node->pos + (node->pos - node->p.pos));
818 /**
819  * Change line type at node, with side effects on neighbours.
820  */
821 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
823     g_assert(end);
824     g_assert(end->subpath);
825     g_assert(end->p.other);
827     if (end->code == static_cast< guint > ( code ) )
828         return;
830    Inkscape::NodePath::Node *start = end->p.other;
832     end->code = code;
834     if (code == NR_LINETO) {
835         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
836         if (end->n.other) {
837             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
838         }
839         sp_node_adjust_handle(start, -1);
840         sp_node_adjust_handle(end, 1);
841     } else {
842         NR::Point delta = end->pos - start->pos;
843         start->n.pos = start->pos + delta / 3;
844         end->p.pos = end->pos - delta / 3;
845         sp_node_adjust_handle(start, 1);
846         sp_node_adjust_handle(end, -1);
847     }
849     sp_node_update_handles(start);
850     sp_node_update_handles(end);
853 /**
854  * Change node type, and its handles accordingly.
855  */
856 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
858     g_assert(node);
859     g_assert(node->subpath);
861     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
862         return node;
864     if ((node->p.other != NULL) && (node->n.other != NULL)) {
865         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
866             type =Inkscape::NodePath::NODE_CUSP;
867         }
868     }
870     node->type = type;
872     if (node->type == Inkscape::NodePath::NODE_CUSP) {
873         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
874         node->knot->setSize (node->selected? 11 : 9);
875         sp_knot_update_ctrl(node->knot);
876     } else {
877         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
878         node->knot->setSize (node->selected? 9 : 7);
879         sp_knot_update_ctrl(node->knot);
880     }
882     // if one of handles is mouseovered, preserve its position
883     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
884         sp_node_adjust_handle(node, 1);
885     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
886         sp_node_adjust_handle(node, -1);
887     } else {
888         sp_node_adjust_handles(node);
889     }
891     sp_node_update_handles(node);
893     sp_nodepath_update_statusbar(node->subpath->nodepath);
895     return node;
898 /**
899  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
900  * adjacent segments from lines to curves.
901 */
902 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
904     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
905         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
906             // convert adjacent segment BEFORE to curve
907             node->code = NR_CURVETO;
908             NR::Point delta;
909             if (node->n.other != NULL)
910                 delta = node->n.other->pos - node->p.other->pos;
911             else
912                 delta = node->pos - node->p.other->pos;
913             node->p.pos = node->pos - delta / 4;
914             sp_node_update_handles(node);
915         }
917         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
918             // convert adjacent segment AFTER to curve
919             node->n.other->code = NR_CURVETO;
920             NR::Point delta;
921             if (node->p.other != NULL)
922                 delta = node->p.other->pos - node->n.other->pos;
923             else
924                 delta = node->pos - node->n.other->pos;
925             node->n.pos = node->pos - delta / 4;
926             sp_node_update_handles(node);
927         }
928     }
930     sp_nodepath_set_node_type (node, type);
933 /**
934  * Move node to point, and adjust its and neighbouring handles.
935  */
936 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
938     NR::Point delta = p - node->pos;
939     node->pos = p;
941     node->p.pos += delta;
942     node->n.pos += delta;
944     if (node->p.other) {
945         if (node->code == NR_LINETO) {
946             sp_node_adjust_handle(node, 1);
947             sp_node_adjust_handle(node->p.other, -1);
948         }
949     }
950     if (node->n.other) {
951         if (node->n.other->code == NR_LINETO) {
952             sp_node_adjust_handle(node, -1);
953             sp_node_adjust_handle(node->n.other, 1);
954         }
955     }
957     // this function is only called from batch movers that will update display at the end
958     // themselves, so here we just move all the knots without emitting move signals, for speed
959     sp_node_update_handles(node, false);
962 /**
963  * Call sp_node_moveto() for node selection and handle possible snapping.
964  */
965 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
966                                             bool const snap = true)
968     NR::Coord best = NR_HUGE;
969     NR::Point delta(dx, dy);
970     NR::Point best_pt = delta;
972     if (snap) {
973         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
974         
975         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
976             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
977             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
978             if (s.getDistance() < best) {
979                 best = s.getDistance();
980                 best_pt = s.getPoint() - n->pos;
981             }
982         }
983     }
985     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
986        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
987         sp_node_moveto(n, n->pos + best_pt);
988     }
990     // do not update repr here so that node dragging is acceptably fast
991     update_object(nodepath);
994 /**
995 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
996 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
997 near x = 0.
998  */
999 double
1000 sculpt_profile (double x, double alpha)
1002     if (x >= 1)
1003         return 0;
1004     return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1007 double
1008 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1010     // extremely primitive for now, don't have time to look for the real one
1011     double lower = NR::L2(b - a);
1012     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1013     return (lower + upper)/2;
1016 void
1017 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1019     n->pos = n->origin + delta;
1020     n->n.pos = n->n.origin + delta_n;
1021     n->p.pos = n->p.origin + delta_p;
1022     sp_node_adjust_handles(n);
1023     sp_node_update_handles(n, false);
1026 /**
1027  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1028  * on how far they are from the dragged node n.
1029  */
1030 static void 
1031 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1033     g_assert (n);
1034     g_assert (nodepath);
1035     g_assert (n->subpath->nodepath == nodepath);
1037     double pressure = n->knot->pressure;
1038     if (pressure == 0)
1039         pressure = 0.5; // default
1040     pressure = CLAMP (pressure, 0.2, 0.8);
1042     // map pressure to alpha = 1/5 ... 5
1043     double alpha = 1 - 2 * fabs(pressure - 0.5);
1044     if (pressure > 0.5)
1045         alpha = 1/alpha;
1047     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1048         // Only one subpath has selected nodes:
1049         // use linear mode, where the distance from n to node being dragged is calculated along the path
1051         double n_sel_range = 0, p_sel_range = 0;
1052         guint n_nodes = 0, p_nodes = 0;
1053         guint n_sel_nodes = 0, p_sel_nodes = 0;
1055         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1056         {
1057             double n_range = 0, p_range = 0;
1058             bool n_going = true, p_going = true;
1059             Inkscape::NodePath::Node *n_node = n;
1060             Inkscape::NodePath::Node *p_node = n;
1061             do {
1062                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1063                 if (n_node && n_going)
1064                     n_node = n_node->n.other;
1065                 if (n_node == NULL) {
1066                     n_going = false;
1067                 } else {
1068                     n_nodes ++;
1069                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1070                     if (n_node->selected) {
1071                         n_sel_nodes ++;
1072                         n_sel_range = n_range;
1073                     }
1074                     if (n_node == p_node) {
1075                         n_going = false;
1076                         p_going = false;
1077                     }
1078                 }
1079                 if (p_node && p_going)
1080                     p_node = p_node->p.other;
1081                 if (p_node == NULL) {
1082                     p_going = false;
1083                 } else {
1084                     p_nodes ++;
1085                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1086                     if (p_node->selected) {
1087                         p_sel_nodes ++;
1088                         p_sel_range = p_range;
1089                     }
1090                     if (p_node == n_node) {
1091                         n_going = false;
1092                         p_going = false;
1093                     }
1094                 }
1095             } while (n_going || p_going);
1096         }
1098         // Second pass: actually move nodes in this subpath
1099         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1100         {
1101             double n_range = 0, p_range = 0;
1102             bool n_going = true, p_going = true;
1103             Inkscape::NodePath::Node *n_node = n;
1104             Inkscape::NodePath::Node *p_node = n;
1105             do {
1106                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1107                 if (n_node && n_going)
1108                     n_node = n_node->n.other;
1109                 if (n_node == NULL) {
1110                     n_going = false;
1111                 } else {
1112                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1113                     if (n_node->selected) {
1114                         sp_nodepath_move_node_and_handles (n_node, 
1115                                                            sculpt_profile (n_range / n_sel_range, alpha) * delta,
1116                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha) * delta,
1117                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha) * delta);
1118                     }
1119                     if (n_node == p_node) {
1120                         n_going = false;
1121                         p_going = false;
1122                     }
1123                 }
1124                 if (p_node && p_going)
1125                     p_node = p_node->p.other;
1126                 if (p_node == NULL) {
1127                     p_going = false;
1128                 } else {
1129                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1130                     if (p_node->selected) {
1131                         sp_nodepath_move_node_and_handles (p_node, 
1132                                                            sculpt_profile (p_range / p_sel_range, alpha) * delta,
1133                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha) * delta,
1134                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha) * delta);
1135                     }
1136                     if (p_node == n_node) {
1137                         n_going = false;
1138                         p_going = false;
1139                     }
1140                 }
1141             } while (n_going || p_going);
1142         }
1144     } else {
1145         // Multiple subpaths have selected nodes:
1146         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1147         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1148         // fix the pear-like shape when sculpting e.g. a ring
1150         // First pass: calculate range
1151         gdouble direct_range = 0;
1152         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1153             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1154             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1155                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1156                 if (node->selected) {
1157                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1158                 }
1159             }
1160         }
1162         // Second pass: actually move nodes
1163         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1164             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1165             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1166                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1167                 if (node->selected) {
1168                     sp_nodepath_move_node_and_handles (node, 
1169                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha) * delta,
1170                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha) * delta,
1171                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha) * delta);
1172                 }
1173             }
1174         }
1175     }
1177     // do not update repr here so that node dragging is acceptably fast
1178     update_object(nodepath);
1182 /**
1183  * Move node selection to point, adjust its and neighbouring handles,
1184  * handle possible snapping, and commit the change with possible undo.
1185  */
1186 void
1187 sp_node_selected_move(gdouble dx, gdouble dy)
1189     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1190     if (!nodepath) return;
1192     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1194     if (dx == 0) {
1195         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1196     } else if (dy == 0) {
1197         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1198     } else {
1199         sp_nodepath_update_repr(nodepath);
1200     }
1203 /**
1204  * Move node selection off screen and commit the change.
1205  */
1206 void
1207 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1209     // borrowed from sp_selection_move_screen in selection-chemistry.c
1210     // we find out the current zoom factor and divide deltas by it
1211     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1213     gdouble zoom = desktop->current_zoom();
1214     gdouble zdx = dx / zoom;
1215     gdouble zdy = dy / zoom;
1217     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1218     if (!nodepath) return;
1220     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1222     if (dx == 0) {
1223         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1224     } else if (dy == 0) {
1225         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1226     } else {
1227         sp_nodepath_update_repr(nodepath);
1228     }
1231 /** If they don't yet exist, creates knot and line for the given side of the node */
1232 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1234     if (!side->knot) {
1235         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"));
1237         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1238         side->knot->setSize (7);
1239         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1240         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1241         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1242         sp_knot_update_ctrl(side->knot);
1244         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1245         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1246         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1247         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1248         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1249         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1250     }
1252     if (!side->line) {
1253         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1254                                         SP_TYPE_CTRLLINE, NULL);
1255     }
1258 /**
1259  * Ensure the given handle of the node is visible/invisible, update its screen position
1260  */
1261 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1263     g_assert(node != NULL);
1265    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1266     NRPathcode code = sp_node_path_code_from_side(node, side);
1268     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1270     if (show_handle) {
1271         if (!side->knot) { // No handle knot at all
1272             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1273             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1274             side->knot->pos = side->pos;
1275             if (side->knot->item) 
1276                 SP_CTRL(side->knot->item)->moveto(side->pos);
1277             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1278             sp_knot_show(side->knot);
1279         } else {
1280             if (side->knot->pos != side->pos) { // only if it's really moved
1281                 if (fire_move_signals) {
1282                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1283                 } else {
1284                     sp_knot_moveto(side->knot, &side->pos);
1285                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1286                 }
1287             }
1288             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1289                 sp_knot_show(side->knot);
1290             }
1291         }
1292         sp_canvas_item_show(side->line);
1293     } else {
1294         if (side->knot) {
1295             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1296                 sp_knot_hide(side->knot);
1297             }
1298         }
1299         if (side->line) {
1300             sp_canvas_item_hide(side->line);
1301         }
1302     }
1305 /**
1306  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1307  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1308  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1309  * updated; otherwise, just move the knots silently (used in batch moves).
1310  */
1311 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1313     g_assert(node != NULL);
1315     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1316         sp_knot_show(node->knot);
1317     }
1319     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1320         if (fire_move_signals)
1321             sp_knot_set_position(node->knot, &node->pos, 0);
1322         else 
1323             sp_knot_moveto(node->knot, &node->pos);
1324     }
1326     gboolean show_handles = node->selected;
1327     if (node->p.other != NULL) {
1328         if (node->p.other->selected) show_handles = TRUE;
1329     }
1330     if (node->n.other != NULL) {
1331         if (node->n.other->selected) show_handles = TRUE;
1332     }
1334     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1335     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1338 /**
1339  * Call sp_node_update_handles() for all nodes on subpath.
1340  */
1341 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1343     g_assert(subpath != NULL);
1345     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1346         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1347     }
1350 /**
1351  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1352  */
1353 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1355     g_assert(nodepath != NULL);
1357     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1358         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1359     }
1362 /**
1363  * Adds all selected nodes in nodepath to list.
1364  */
1365 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1367     StlConv<Node *>::list(l, selected);
1368 /// \todo this adds a copying, rework when the selection becomes a stl list
1371 /**
1372  * Align selected nodes on the specified axis.
1373  */
1374 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1376     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1377         return;
1378     }
1380     if ( !nodepath->selected->next ) { // only one node selected
1381         return;
1382     }
1383    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1384     NR::Point dest(pNode->pos);
1385     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1386         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1387         if (pNode) {
1388             dest[axis] = pNode->pos[axis];
1389             sp_node_moveto(pNode, dest);
1390         }
1391     }
1393     sp_nodepath_update_repr(nodepath);
1396 /// Helper struct.
1397 struct NodeSort
1399    Inkscape::NodePath::Node *_node;
1400     NR::Coord _coord;
1401     /// \todo use vectorof pointers instead of calling copy ctor
1402     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1403         _node(node), _coord(node->pos[axis])
1404     {}
1406 };
1408 static bool operator<(NodeSort const &a, NodeSort const &b)
1410     return (a._coord < b._coord);
1413 /**
1414  * Distribute selected nodes on the specified axis.
1415  */
1416 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1418     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1419         return;
1420     }
1422     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1423         return;
1424     }
1426    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1427     std::vector<NodeSort> sorted;
1428     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1429         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1430         if (pNode) {
1431             NodeSort n(pNode, axis);
1432             sorted.push_back(n);
1433             //dest[axis] = pNode->pos[axis];
1434             //sp_node_moveto(pNode, dest);
1435         }
1436     }
1437     std::sort(sorted.begin(), sorted.end());
1438     unsigned int len = sorted.size();
1439     //overall bboxes span
1440     float dist = (sorted.back()._coord -
1441                   sorted.front()._coord);
1442     //new distance between each bbox
1443     float step = (dist) / (len - 1);
1444     float pos = sorted.front()._coord;
1445     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1446           it < sorted.end();
1447           it ++ )
1448     {
1449         NR::Point dest((*it)._node->pos);
1450         dest[axis] = pos;
1451         sp_node_moveto((*it)._node, dest);
1452         pos += step;
1453     }
1455     sp_nodepath_update_repr(nodepath);
1459 /**
1460  * Call sp_nodepath_line_add_node() for all selected segments.
1461  */
1462 void
1463 sp_node_selected_add_node(void)
1465     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1466     if (!nodepath) {
1467         return;
1468     }
1470     GList *nl = NULL;
1472     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1473        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1474         g_assert(t->selected);
1475         if (t->p.other && t->p.other->selected) {
1476             nl = g_list_prepend(nl, t);
1477         }
1478     }
1480     while (nl) {
1481        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1482        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1483         sp_nodepath_node_select(n, TRUE, FALSE);
1484         nl = g_list_remove(nl, t);
1485     }
1487     /** \todo fixme: adjust ? */
1488     sp_nodepath_update_handles(nodepath);
1490     sp_nodepath_update_repr(nodepath);
1492     sp_nodepath_update_statusbar(nodepath);
1495 /**
1496  * Select segment nearest to point
1497  */
1498 void
1499 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1501     if (!nodepath) {
1502         return;
1503     }
1505     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1507     //find segment to segment
1508     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1510     gboolean force = FALSE;
1511     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1512         force = TRUE;
1513     }
1514     sp_nodepath_node_select(e, (gboolean) toggle, force);
1515     if (e->p.other)
1516         sp_nodepath_node_select(e->p.other, TRUE, force);
1518     sp_nodepath_update_handles(nodepath);
1520     sp_nodepath_update_statusbar(nodepath);
1523 /**
1524  * Add a node nearest to point
1525  */
1526 void
1527 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1529     if (!nodepath) {
1530         return;
1531     }
1533     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1535     //find segment to split
1536     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1538     //don't know why but t seems to flip for lines
1539     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1540         position.t = 1.0 - position.t;
1541     }
1542     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1543     sp_nodepath_node_select(n, FALSE, TRUE);
1545     /* fixme: adjust ? */
1546     sp_nodepath_update_handles(nodepath);
1548     sp_nodepath_update_repr(nodepath);
1550     sp_nodepath_update_statusbar(nodepath);
1553 /*
1554  * Adjusts a segment so that t moves by a certain delta for dragging
1555  * converts lines to curves
1556  *
1557  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1558  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1559  */
1560 void
1561 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1563     /* feel good is an arbitrary parameter that distributes the delta between handles
1564      * if t of the drag point is less than 1/6 distance form the endpoint only
1565      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1566      */
1567     double feel_good;
1568     if (t <= 1.0 / 6.0)
1569         feel_good = 0;
1570     else if (t <= 0.5)
1571         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1572     else if (t <= 5.0 / 6.0)
1573         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1574     else
1575         feel_good = 1;
1577     //if we're dragging a line convert it to a curve
1578     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1579         sp_nodepath_set_line_type(e, NR_CURVETO);
1580     }
1582     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1583     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1584     e->p.other->n.pos += offsetcoord0;
1585     e->p.pos += offsetcoord1;
1587     // adjust handles of adjacent nodes where necessary
1588     sp_node_adjust_handle(e,1);
1589     sp_node_adjust_handle(e->p.other,-1);
1591     sp_nodepath_update_handles(e->subpath->nodepath);
1593     update_object(e->subpath->nodepath);
1595     sp_nodepath_update_statusbar(e->subpath->nodepath);
1599 /**
1600  * Call sp_nodepath_break() for all selected segments.
1601  */
1602 void sp_node_selected_break()
1604     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1605     if (!nodepath) return;
1607     GList *temp = NULL;
1608     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1609        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1610        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1611         if (nn == NULL) continue; // no break, no new node
1612         temp = g_list_prepend(temp, nn);
1613     }
1615     if (temp) {
1616         sp_nodepath_deselect(nodepath);
1617     }
1618     for (GList *l = temp; l != NULL; l = l->next) {
1619         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1620     }
1622     sp_nodepath_update_handles(nodepath);
1624     sp_nodepath_update_repr(nodepath);
1627 /**
1628  * Duplicate the selected node(s).
1629  */
1630 void sp_node_selected_duplicate()
1632     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1633     if (!nodepath) {
1634         return;
1635     }
1637     GList *temp = NULL;
1638     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1639        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1640        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1641         if (nn == NULL) continue; // could not duplicate
1642         temp = g_list_prepend(temp, nn);
1643     }
1645     if (temp) {
1646         sp_nodepath_deselect(nodepath);
1647     }
1648     for (GList *l = temp; l != NULL; l = l->next) {
1649         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1650     }
1652     sp_nodepath_update_handles(nodepath);
1654     sp_nodepath_update_repr(nodepath);
1657 /**
1658  *  Join two nodes by merging them into one.
1659  */
1660 void sp_node_selected_join()
1662     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1663     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1665     if (g_list_length(nodepath->selected) != 2) {
1666         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1667         return;
1668     }
1670    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1671    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1673     g_assert(a != b);
1674     g_assert(a->p.other || a->n.other);
1675     g_assert(b->p.other || b->n.other);
1677     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1678         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1679         return;
1680     }
1682     /* a and b are endpoints */
1684     NR::Point c;
1685     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1686         c = a->pos;
1687     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1688         c = b->pos;
1689     } else {
1690         c = (a->pos + b->pos) / 2;
1691     }
1693     if (a->subpath == b->subpath) {
1694        Inkscape::NodePath::SubPath *sp = a->subpath;
1695         sp_nodepath_subpath_close(sp);
1696         sp_node_moveto (sp->first, c);
1698         sp_nodepath_update_handles(sp->nodepath);
1699         sp_nodepath_update_repr(nodepath);
1700         return;
1701     }
1703     /* a and b are separate subpaths */
1704    Inkscape::NodePath::SubPath *sa = a->subpath;
1705    Inkscape::NodePath::SubPath *sb = b->subpath;
1706     NR::Point p;
1707    Inkscape::NodePath::Node *n;
1708     NRPathcode code;
1709     if (a == sa->first) {
1710         p = sa->first->n.pos;
1711         code = (NRPathcode)sa->first->n.other->code;
1712        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1713         n = sa->last;
1714         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1715         n = n->p.other;
1716         while (n) {
1717             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1718             n = n->p.other;
1719             if (n == sa->first) n = NULL;
1720         }
1721         sp_nodepath_subpath_destroy(sa);
1722         sa = t;
1723     } else if (a == sa->last) {
1724         p = sa->last->p.pos;
1725         code = (NRPathcode)sa->last->code;
1726         sp_nodepath_node_destroy(sa->last);
1727     } else {
1728         code = NR_END;
1729         g_assert_not_reached();
1730     }
1732     if (b == sb->first) {
1733         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1734         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1735             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1736         }
1737     } else if (b == sb->last) {
1738         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1739         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1740             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1741         }
1742     } else {
1743         g_assert_not_reached();
1744     }
1745     /* and now destroy sb */
1747     sp_nodepath_subpath_destroy(sb);
1749     sp_nodepath_update_handles(sa->nodepath);
1751     sp_nodepath_update_repr(nodepath);
1753     sp_nodepath_update_statusbar(nodepath);
1756 /**
1757  *  Join two nodes by adding a segment between them.
1758  */
1759 void sp_node_selected_join_segment()
1761     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1762     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1764     if (g_list_length(nodepath->selected) != 2) {
1765         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1766         return;
1767     }
1769    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1770    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1772     g_assert(a != b);
1773     g_assert(a->p.other || a->n.other);
1774     g_assert(b->p.other || b->n.other);
1776     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1777         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1778         return;
1779     }
1781     if (a->subpath == b->subpath) {
1782        Inkscape::NodePath::SubPath *sp = a->subpath;
1784         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1785         sp->closed = TRUE;
1787         sp->first->p.other = sp->last;
1788         sp->last->n.other  = sp->first;
1790         sp_node_handle_mirror_p_to_n(sp->last);
1791         sp_node_handle_mirror_n_to_p(sp->first);
1793         sp->first->code = sp->last->code;
1794         sp->first       = sp->last;
1796         sp_nodepath_update_handles(sp->nodepath);
1798         sp_nodepath_update_repr(nodepath);
1800         return;
1801     }
1803     /* a and b are separate subpaths */
1804    Inkscape::NodePath::SubPath *sa = a->subpath;
1805    Inkscape::NodePath::SubPath *sb = b->subpath;
1807    Inkscape::NodePath::Node *n;
1808     NR::Point p;
1809     NRPathcode code;
1810     if (a == sa->first) {
1811         code = (NRPathcode) sa->first->n.other->code;
1812        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1813         n = sa->last;
1814         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1815         for (n = n->p.other; n != NULL; n = n->p.other) {
1816             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1817         }
1818         sp_nodepath_subpath_destroy(sa);
1819         sa = t;
1820     } else if (a == sa->last) {
1821         code = (NRPathcode)sa->last->code;
1822     } else {
1823         code = NR_END;
1824         g_assert_not_reached();
1825     }
1827     if (b == sb->first) {
1828         n = sb->first;
1829         sp_node_handle_mirror_p_to_n(sa->last);
1830         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1831         sp_node_handle_mirror_n_to_p(sa->last);
1832         for (n = n->n.other; n != NULL; n = n->n.other) {
1833             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1834         }
1835     } else if (b == sb->last) {
1836         n = sb->last;
1837         sp_node_handle_mirror_p_to_n(sa->last);
1838         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1839         sp_node_handle_mirror_n_to_p(sa->last);
1840         for (n = n->p.other; n != NULL; n = n->p.other) {
1841             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1842         }
1843     } else {
1844         g_assert_not_reached();
1845     }
1846     /* and now destroy sb */
1848     sp_nodepath_subpath_destroy(sb);
1850     sp_nodepath_update_handles(sa->nodepath);
1852     sp_nodepath_update_repr(nodepath);
1855 /**
1856  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1857  */
1858 void sp_node_delete_preserve(GList *nodes_to_delete)
1860     GSList *nodepaths = NULL;
1861     
1862     while (nodes_to_delete) {
1863         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1864         Inkscape::NodePath::SubPath *sp = node->subpath;
1865         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1866         Inkscape::NodePath::Node *sample_cursor = NULL;
1867         Inkscape::NodePath::Node *sample_end = NULL;
1868         Inkscape::NodePath::Node *delete_cursor = node;
1869         bool just_delete = false;
1870         
1871         //find the start of this contiguous selection
1872         //move left to the first node that is not selected
1873         //or the start of the non-closed path
1874         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1875             delete_cursor = curr;
1876         }
1878         //just delete at the beginning of an open path
1879         if (!delete_cursor->p.other) {
1880             sample_cursor = delete_cursor;
1881             just_delete = true;
1882         } else {
1883             sample_cursor = delete_cursor->p.other;
1884         }
1885         
1886         //calculate points for each segment
1887         int rate = 5;
1888         float period = 1.0 / rate;
1889         std::vector<NR::Point> data;
1890         if (!just_delete) {
1891             data.push_back(sample_cursor->pos);
1892             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1893                 //just delete at the end of an open path
1894                 if (!sp->closed && curr->n.other == sp->last) {
1895                     just_delete = true;
1896                     break;
1897                 }
1898                 
1899                 //sample points on the contiguous selected segment
1900                 NR::Point *bez;
1901                 bez = new NR::Point [4];
1902                 bez[0] = curr->pos;
1903                 bez[1] = curr->n.pos;
1904                 bez[2] = curr->n.other->p.pos;
1905                 bez[3] = curr->n.other->pos;
1906                 for (int i=1; i<rate; i++) {
1907                     gdouble t = i * period;
1908                     NR::Point p = bezier_pt(3, bez, t);
1909                     data.push_back(p);
1910                 }
1911                 data.push_back(curr->n.other->pos);
1913                 sample_end = curr->n.other;
1914                 //break if we've come full circle or hit the end of the selection
1915                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1916                     break;
1917                 }
1918             }
1919         }
1921         if (!just_delete) {
1922             //calculate the best fitting single segment and adjust the endpoints
1923             NR::Point *adata;
1924             adata = new NR::Point [data.size()];
1925             copy(data.begin(), data.end(), adata);
1926             
1927             NR::Point *bez;
1928             bez = new NR::Point [4];
1929             //would decreasing error create a better fitting approximation?
1930             gdouble error = 1.0;
1931             gint ret;
1932             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1934             //adjust endpoints
1935             sample_cursor->n.pos = bez[1];
1936             sample_end->p.pos = bez[2];
1937         }
1938        
1939         //destroy this contiguous selection
1940         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
1941             Inkscape::NodePath::Node *temp = delete_cursor;
1942             if (delete_cursor->n.other == delete_cursor) {
1943                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
1944                 delete_cursor = NULL; 
1945             } else {
1946                 delete_cursor = delete_cursor->n.other;
1947             }
1948             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
1949             sp_nodepath_node_destroy(temp);
1950         }
1952         sp_nodepath_update_handles(nodepath);
1954         if (!g_slist_find(nodepaths, nodepath))
1955             nodepaths = g_slist_prepend (nodepaths, nodepath);
1956     }
1958     for (GSList *i = nodepaths; i; i = i->next) {
1959         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
1960         // different nodepaths will give us one undo event per nodepath
1961         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
1963         // if the entire nodepath is removed, delete the selected object.
1964         if (nodepath->subpaths == NULL ||
1965             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
1966             //at least 2
1967             sp_nodepath_get_node_count(nodepath) < 2) {
1968             SPDocument *document = sp_desktop_document (nodepath->desktop);
1969             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
1970             //delete this nodepath's object, not the entire selection! (though at this time, this
1971             //does not matter)
1972             sp_selection_delete();
1973             sp_document_done (document);
1974         } else {
1975             sp_nodepath_update_repr(nodepath);
1976             sp_nodepath_update_statusbar(nodepath);
1977         }
1978     }
1980     g_slist_free (nodepaths);
1983 /**
1984  * Delete one or more selected nodes.
1985  */
1986 void sp_node_selected_delete()
1988     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1989     if (!nodepath) return;
1990     if (!nodepath->selected) return;
1992     /** \todo fixme: do it the right way */
1993     while (nodepath->selected) {
1994        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1995         sp_nodepath_node_destroy(node);
1996     }
1999     //clean up the nodepath (such as for trivial subpaths)
2000     sp_nodepath_cleanup(nodepath);
2002     sp_nodepath_update_handles(nodepath);
2004     // if the entire nodepath is removed, delete the selected object.
2005     if (nodepath->subpaths == NULL ||
2006         sp_nodepath_get_node_count(nodepath) < 2) {
2007         SPDocument *document = sp_desktop_document (nodepath->desktop);
2008         sp_selection_delete();
2009         sp_document_done (document);
2010         return;
2011     }
2013     sp_nodepath_update_repr(nodepath);
2015     sp_nodepath_update_statusbar(nodepath);
2018 /**
2019  * Delete one or more segments between two selected nodes.
2020  * This is the code for 'split'.
2021  */
2022 void
2023 sp_node_selected_delete_segment(void)
2025    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2026    Inkscape::NodePath::Node *curr, *next;     //Iterators
2028     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2029     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2031     if (g_list_length(nodepath->selected) != 2) {
2032         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2033                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2034         return;
2035     }
2037     //Selected nodes, not inclusive
2038    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2039    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2041     if ( ( a==b)                       ||  //same node
2042          (a->subpath  != b->subpath )  ||  //not the same path
2043          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2044          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2045     {
2046         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2047                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2048         return;
2049     }
2051     //###########################################
2052     //# BEGIN EDITS
2053     //###########################################
2054     //##################################
2055     //# CLOSED PATH
2056     //##################################
2057     if (a->subpath->closed) {
2060         gboolean reversed = FALSE;
2062         //Since we can go in a circle, we need to find the shorter distance.
2063         //  a->b or b->a
2064         start = end = NULL;
2065         int distance    = 0;
2066         int minDistance = 0;
2067         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2068             if (curr==b) {
2069                 //printf("a to b:%d\n", distance);
2070                 start = a;//go from a to b
2071                 end   = b;
2072                 minDistance = distance;
2073                 //printf("A to B :\n");
2074                 break;
2075             }
2076             distance++;
2077         }
2079         //try again, the other direction
2080         distance = 0;
2081         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2082             if (curr==a) {
2083                 //printf("b to a:%d\n", distance);
2084                 if (distance < minDistance) {
2085                     start    = b;  //we go from b to a
2086                     end      = a;
2087                     reversed = TRUE;
2088                     //printf("B to A\n");
2089                 }
2090                 break;
2091             }
2092             distance++;
2093         }
2096         //Copy everything from 'end' to 'start' to a new subpath
2097        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2098         for (curr=end ; curr ; curr=curr->n.other) {
2099             NRPathcode code = (NRPathcode) curr->code;
2100             if (curr == end)
2101                 code = NR_MOVETO;
2102             sp_nodepath_node_new(t, NULL,
2103                                  (Inkscape::NodePath::NodeType)curr->type, code,
2104                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2105             if (curr == start)
2106                 break;
2107         }
2108         sp_nodepath_subpath_destroy(a->subpath);
2111     }
2115     //##################################
2116     //# OPEN PATH
2117     //##################################
2118     else {
2120         //We need to get the direction of the list between A and B
2121         //Can we walk from a to b?
2122         start = end = NULL;
2123         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2124             if (curr==b) {
2125                 start = a;  //did it!  we go from a to b
2126                 end   = b;
2127                 //printf("A to B\n");
2128                 break;
2129             }
2130         }
2131         if (!start) {//didn't work?  let's try the other direction
2132             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2133                 if (curr==a) {
2134                     start = b;  //did it!  we go from b to a
2135                     end   = a;
2136                     //printf("B to A\n");
2137                     break;
2138                 }
2139             }
2140         }
2141         if (!start) {
2142             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2143                                                      _("Cannot find path between nodes."));
2144             return;
2145         }
2149         //Copy everything after 'end' to a new subpath
2150        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2151         for (curr=end ; curr ; curr=curr->n.other) {
2152             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2153                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2154         }
2156         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2157         for (curr = start->n.other ; curr  ; curr=next) {
2158             next = curr->n.other;
2159             sp_nodepath_node_destroy(curr);
2160         }
2162     }
2163     //###########################################
2164     //# END EDITS
2165     //###########################################
2167     //clean up the nodepath (such as for trivial subpaths)
2168     sp_nodepath_cleanup(nodepath);
2170     sp_nodepath_update_handles(nodepath);
2172     sp_nodepath_update_repr(nodepath);
2174     sp_nodepath_update_statusbar(nodepath);
2177 /**
2178  * Call sp_nodepath_set_line() for all selected segments.
2179  */
2180 void
2181 sp_node_selected_set_line_type(NRPathcode code)
2183     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2184     if (nodepath == NULL) return;
2186     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2187        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2188         g_assert(n->selected);
2189         if (n->p.other && n->p.other->selected) {
2190             sp_nodepath_set_line_type(n, code);
2191         }
2192     }
2194     sp_nodepath_update_repr(nodepath);
2197 /**
2198  * Call sp_nodepath_convert_node_type() for all selected nodes.
2199  */
2200 void
2201 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2203     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2204     if (nodepath == NULL) return;
2206     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2207         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2208     }
2210     sp_nodepath_update_repr(nodepath);
2213 /**
2214  * Change select status of node, update its own and neighbour handles.
2215  */
2216 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2218     node->selected = selected;
2220     if (selected) {
2221         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2222         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2223         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2224         sp_knot_update_ctrl(node->knot);
2225     } else {
2226         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2227         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2228         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2229         sp_knot_update_ctrl(node->knot);
2230     }
2232     sp_node_update_handles(node);
2233     if (node->n.other) sp_node_update_handles(node->n.other);
2234     if (node->p.other) sp_node_update_handles(node->p.other);
2237 /**
2238 \brief Select a node
2239 \param node     The node to select
2240 \param incremental   If true, add to selection, otherwise deselect others
2241 \param override   If true, always select this node, otherwise toggle selected status
2242 */
2243 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2245     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2247     if (incremental) {
2248         if (override) {
2249             if (!g_list_find(nodepath->selected, node)) {
2250                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2251             }
2252             sp_node_set_selected(node, TRUE);
2253         } else { // toggle
2254             if (node->selected) {
2255                 g_assert(g_list_find(nodepath->selected, node));
2256                 nodepath->selected = g_list_remove(nodepath->selected, node);
2257             } else {
2258                 g_assert(!g_list_find(nodepath->selected, node));
2259                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2260             }
2261             sp_node_set_selected(node, !node->selected);
2262         }
2263     } else {
2264         sp_nodepath_deselect(nodepath);
2265         nodepath->selected = g_list_prepend(nodepath->selected, node);
2266         sp_node_set_selected(node, TRUE);
2267     }
2269     sp_nodepath_update_statusbar(nodepath);
2273 /**
2274 \brief Deselect all nodes in the nodepath
2275 */
2276 void
2277 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2279     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2281     while (nodepath->selected) {
2282         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2283         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2284     }
2285     sp_nodepath_update_statusbar(nodepath);
2288 /**
2289 \brief Select or invert selection of all nodes in the nodepath
2290 */
2291 void
2292 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2294     if (!nodepath) return;
2296     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2297        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2298         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2299            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2300            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2301         }
2302     }
2305 /**
2306  * If nothing selected, does the same as sp_nodepath_select_all();
2307  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2308  * (i.e., similar to "select all in layer", with the "selected" subpaths
2309  * being treated as "layers" in the path).
2310  */
2311 void
2312 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2314     if (!nodepath) return;
2316     if (g_list_length (nodepath->selected) == 0) {
2317         sp_nodepath_select_all (nodepath, invert);
2318         return;
2319     }
2321     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2322     GSList *subpaths = NULL;
2324     for (GList *l = copy; l != NULL; l = l->next) {
2325         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2326         Inkscape::NodePath::SubPath *subpath = n->subpath;
2327         if (!g_slist_find (subpaths, subpath))
2328             subpaths = g_slist_prepend (subpaths, subpath);
2329     }
2331     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2332         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2333         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2334             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2335             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2336         }
2337     }
2339     g_slist_free (subpaths);
2340     g_list_free (copy);
2343 /**
2344  * \brief Select the node after the last selected; if none is selected,
2345  * select the first within path.
2346  */
2347 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2349     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2351    Inkscape::NodePath::Node *last = NULL;
2352     if (nodepath->selected) {
2353         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2354            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2355             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2356             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2357                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2358                 if (node->selected) {
2359                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2360                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2361                             if (spl->next) { // there's a next subpath
2362                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2363                                 last = subpath_next->first;
2364                             } else if (spl->prev) { // there's a previous subpath
2365                                 last = NULL; // to be set later to the first node of first subpath
2366                             } else {
2367                                 last = node->n.other;
2368                             }
2369                         } else {
2370                             last = node->n.other;
2371                         }
2372                     } else {
2373                         if (node->n.other) {
2374                             last = node->n.other;
2375                         } else {
2376                             if (spl->next) { // there's a next subpath
2377                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2378                                 last = subpath_next->first;
2379                             } else if (spl->prev) { // there's a previous subpath
2380                                 last = NULL; // to be set later to the first node of first subpath
2381                             } else {
2382                                 last = (Inkscape::NodePath::Node *) subpath->first;
2383                             }
2384                         }
2385                     }
2386                 }
2387             }
2388         }
2389         sp_nodepath_deselect(nodepath);
2390     }
2392     if (last) { // there's at least one more node after selected
2393         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2394     } else { // no more nodes, select the first one in first subpath
2395        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2396         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2397     }
2400 /**
2401  * \brief Select the node before the first selected; if none is selected,
2402  * select the last within path
2403  */
2404 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2406     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2408    Inkscape::NodePath::Node *last = NULL;
2409     if (nodepath->selected) {
2410         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2411            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2412             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2413                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2414                 if (node->selected) {
2415                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2416                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2417                             if (spl->prev) { // there's a prev subpath
2418                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2419                                 last = subpath_prev->last;
2420                             } else if (spl->next) { // there's a next subpath
2421                                 last = NULL; // to be set later to the last node of last subpath
2422                             } else {
2423                                 last = node->p.other;
2424                             }
2425                         } else {
2426                             last = node->p.other;
2427                         }
2428                     } else {
2429                         if (node->p.other) {
2430                             last = node->p.other;
2431                         } else {
2432                             if (spl->prev) { // there's a prev subpath
2433                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2434                                 last = subpath_prev->last;
2435                             } else if (spl->next) { // there's a next subpath
2436                                 last = NULL; // to be set later to the last node of last subpath
2437                             } else {
2438                                 last = (Inkscape::NodePath::Node *) subpath->last;
2439                             }
2440                         }
2441                     }
2442                 }
2443             }
2444         }
2445         sp_nodepath_deselect(nodepath);
2446     }
2448     if (last) { // there's at least one more node before selected
2449         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2450     } else { // no more nodes, select the last one in last subpath
2451         GList *spl = g_list_last(nodepath->subpaths);
2452        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2453         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2454     }
2457 /**
2458  * \brief Select all nodes that are within the rectangle.
2459  */
2460 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2462     if (!incremental) {
2463         sp_nodepath_deselect(nodepath);
2464     }
2466     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2467        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2468         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2469            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2471             if (b.contains(node->pos)) {
2472                 sp_nodepath_node_select(node, TRUE, TRUE);
2473             }
2474         }
2475     }
2479 /**
2480 \brief  Saves all nodes' and handles' current positions in their origin members
2481 */
2482 void
2483 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2485     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2486        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2487         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2488            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2489            n->origin = n->pos;
2490            n->p.origin = n->p.pos;
2491            n->n.origin = n->n.pos;
2492         }
2493     }
2494
2496 /**
2497 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2498 */
2499 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2501     if (!nodepath->selected) {
2502         return NULL;
2503     }
2505     GList *r = NULL;
2506     guint i = 0;
2507     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2508        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2509         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2510            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2511             i++;
2512             if (node->selected) {
2513                 r = g_list_append(r, GINT_TO_POINTER(i));
2514             }
2515         }
2516     }
2517     return r;
2520 /**
2521 \brief  Restores selection by selecting nodes whose positions are in the list
2522 */
2523 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2525     sp_nodepath_deselect(nodepath);
2527     guint i = 0;
2528     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2529        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2530         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2531            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2532             i++;
2533             if (g_list_find(r, GINT_TO_POINTER(i))) {
2534                 sp_nodepath_node_select(node, TRUE, TRUE);
2535             }
2536         }
2537     }
2541 /**
2542 \brief Adjusts handle according to node type and line code.
2543 */
2544 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2546     double len, otherlen, linelen;
2548     g_assert(node);
2550    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2551    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2553     /** \todo fixme: */
2554     if (me->other == NULL) return;
2555     if (other->other == NULL) return;
2557     /* I have line */
2559     NRPathcode mecode, ocode;
2560     if (which_adjust == 1) {
2561         mecode = (NRPathcode)me->other->code;
2562         ocode = (NRPathcode)node->code;
2563     } else {
2564         mecode = (NRPathcode)node->code;
2565         ocode = (NRPathcode)other->other->code;
2566     }
2568     if (mecode == NR_LINETO) return;
2570     /* I am curve */
2572     if (other->other == NULL) return;
2574     /* Other has line */
2576     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2578     NR::Point delta;
2579     if (ocode == NR_LINETO) {
2580         /* other is lineto, we are either smooth or symm */
2581        Inkscape::NodePath::Node *othernode = other->other;
2582         len = NR::L2(me->pos - node->pos);
2583         delta = node->pos - othernode->pos;
2584         linelen = NR::L2(delta);
2585         if (linelen < 1e-18) 
2586             return;
2587         me->pos = node->pos + (len / linelen)*delta;
2588         return;
2589     }
2591     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2593         me->pos = 2 * node->pos - other->pos;
2594         return;
2595     }
2597     /* We are smooth */
2599     len = NR::L2(me->pos - node->pos);
2600     delta = other->pos - node->pos;
2601     otherlen = NR::L2(delta);
2602     if (otherlen < 1e-18) return;
2604     me->pos = node->pos - (len / otherlen) * delta;
2607 /**
2608  \brief Adjusts both handles according to node type and line code
2609  */
2610 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2612     g_assert(node);
2614     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2616     /* we are either smooth or symm */
2618     if (node->p.other == NULL) return;
2620     if (node->n.other == NULL) return;
2622     if (node->code == NR_LINETO) {
2623         if (node->n.other->code == NR_LINETO) return;
2624         sp_node_adjust_handle(node, 1);
2625         return;
2626     }
2628     if (node->n.other->code == NR_LINETO) {
2629         if (node->code == NR_LINETO) return;
2630         sp_node_adjust_handle(node, -1);
2631         return;
2632     }
2634     /* both are curves */
2635     NR::Point const delta( node->n.pos - node->p.pos );
2637     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2638         node->p.pos = node->pos - delta / 2;
2639         node->n.pos = node->pos + delta / 2;
2640         return;
2641     }
2643     /* We are smooth */
2644     double plen = NR::L2(node->p.pos - node->pos);
2645     if (plen < 1e-18) return;
2646     double nlen = NR::L2(node->n.pos - node->pos);
2647     if (nlen < 1e-18) return;
2648     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2649     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2652 /**
2653  * Node event callback.
2654  */
2655 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2657     gboolean ret = FALSE;
2658     switch (event->type) {
2659         case GDK_ENTER_NOTIFY:
2660             active_node = n;
2661             break;
2662         case GDK_LEAVE_NOTIFY:
2663             active_node = NULL;
2664             break;
2665         case GDK_KEY_PRESS:
2666             switch (get_group0_keyval (&event->key)) {
2667                 case GDK_space:
2668                     if (event->key.state & GDK_BUTTON1_MASK) {
2669                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2670                         stamp_repr(nodepath);
2671                         ret = TRUE;
2672                     }
2673                     break;
2674                 default:
2675                     break;
2676             }
2677             break;
2678         default:
2679             break;
2680     }
2682     return ret;
2685 /**
2686  * Handle keypress on node; directly called.
2687  */
2688 gboolean node_key(GdkEvent *event)
2690     Inkscape::NodePath::Path *np;
2692     // there is no way to verify nodes so set active_node to nil when deleting!!
2693     if (active_node == NULL) return FALSE;
2695     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2696         gint ret = FALSE;
2697         switch (get_group0_keyval (&event->key)) {
2698             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2699             case GDK_BackSpace:
2700                 np = active_node->subpath->nodepath;
2701                 sp_nodepath_node_destroy(active_node);
2702                 sp_nodepath_update_repr(np);
2703                 active_node = NULL;
2704                 ret = TRUE;
2705                 break;
2706             case GDK_c:
2707                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2708                 ret = TRUE;
2709                 break;
2710             case GDK_s:
2711                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2712                 ret = TRUE;
2713                 break;
2714             case GDK_y:
2715                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2716                 ret = TRUE;
2717                 break;
2718             case GDK_b:
2719                 sp_nodepath_node_break(active_node);
2720                 ret = TRUE;
2721                 break;
2722         }
2723         return ret;
2724     }
2725     return FALSE;
2728 /**
2729  * Mouseclick on node callback.
2730  */
2731 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2733    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2735     if (state & GDK_CONTROL_MASK) {
2736         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2738         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2739             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2740                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2741             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2742                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2743             } else {
2744                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2745             }
2746             sp_nodepath_update_repr(nodepath);
2747             sp_nodepath_update_statusbar(nodepath);
2749         } else { //ctrl+alt+click: delete node
2750             GList *node_to_delete = NULL;
2751             node_to_delete = g_list_append(node_to_delete, n);
2752             sp_node_delete_preserve(node_to_delete);
2753         }
2755     } else {
2756         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2757     }
2760 /**
2761  * Mouse grabbed node callback.
2762  */
2763 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2765    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2767     if (!n->selected) {
2768         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2769     }
2771     sp_nodepath_remember_origins (n->subpath->nodepath);
2774 /**
2775  * Mouse ungrabbed node callback.
2776  */
2777 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2779    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2781    n->dragging_out = NULL;
2783    sp_nodepath_update_repr(n->subpath->nodepath);
2786 /**
2787  * The point on a line, given by its angle, closest to the given point.
2788  * \param p  A point.
2789  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2790  * \param closest  Pointer to the point struct where the result is stored.
2791  * \todo FIXME: use dot product perhaps?
2792  */
2793 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2795     if (a == HUGE_VAL) { // vertical
2796         *closest = NR::Point(0, (*p)[NR::Y]);
2797     } else {
2798         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2799         (*closest)[NR::Y] = a * (*closest)[NR::X];
2800     }
2803 /**
2804  * Distance from the point to a line given by its angle.
2805  * \param p  A point.
2806  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2807  */
2808 static double point_line_distance(NR::Point *p, double a)
2810     NR::Point c;
2811     point_line_closest(p, a, &c);
2812     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]));
2815 /**
2816  * Callback for node "request" signal.
2817  * \todo fixme: This goes to "moved" event? (lauris)
2818  */
2819 static gboolean
2820 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2822     double yn, xn, yp, xp;
2823     double an, ap, na, pa;
2824     double d_an, d_ap, d_na, d_pa;
2825     gboolean collinear = FALSE;
2826     NR::Point c;
2827     NR::Point pr;
2829    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2831    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2832    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2834        NR::Point mouse = (*p);
2836        if (!n->dragging_out) {
2837            // This is the first drag-out event; find out which handle to drag out
2838            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2839            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2841            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2842                return FALSE;
2844            Inkscape::NodePath::NodeSide *opposite;
2845            if (appr_p > appr_n) { // closer to p
2846                n->dragging_out = &n->p;
2847                opposite = &n->n;
2848                n->code = NR_CURVETO;
2849            } else if (appr_p < appr_n) { // closer to n
2850                n->dragging_out = &n->n;
2851                opposite = &n->p;
2852                n->n.other->code = NR_CURVETO;
2853            } else { // p and n nodes are the same
2854                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2855                    n->dragging_out = &n->p;
2856                    opposite = &n->n;
2857                    n->code = NR_CURVETO;
2858                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2859                    n->dragging_out = &n->n;
2860                    opposite = &n->p;
2861                    n->n.other->code = NR_CURVETO;
2862                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2863                    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);
2864                    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);
2865                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2866                        n->dragging_out = &n->n;
2867                        opposite = &n->p;
2868                        n->n.other->code = NR_CURVETO;
2869                    } else { // closer to other's n handle
2870                        n->dragging_out = &n->p;
2871                        opposite = &n->n;
2872                        n->code = NR_CURVETO;
2873                    }
2874                }
2875            }
2877            // if there's another handle, make sure the one we drag out starts parallel to it
2878            if (opposite->pos != n->pos) {
2879                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2880            }
2882            // knots might not be created yet!
2883            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2884            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2885        }
2887        // pass this on to the handle-moved callback
2888        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2889        sp_node_update_handles(n);
2890        return TRUE;
2891    }
2893     if (state & GDK_CONTROL_MASK) { // constrained motion
2895         // calculate relative distances of handles
2896         // n handle:
2897         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2898         xn = n->n.pos[NR::X] - n->pos[NR::X];
2899         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2900         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2901             if (n->n.other) { // if there is the next point
2902                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2903                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2904                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2905             }
2906         }
2907         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2908         if (yn < 0) { xn = -xn; yn = -yn; }
2910         // p handle:
2911         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2912         xp = n->p.pos[NR::X] - n->pos[NR::X];
2913         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2914         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2915             if (n->p.other) {
2916                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2917                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2918                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2919             }
2920         }
2921         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2922         if (yp < 0) { xp = -xp; yp = -yp; }
2924         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2925             // sliding on handles, only if at least one of the handles is non-vertical
2926             // (otherwise it's the same as ctrl+drag anyway)
2928             // calculate angles of the handles
2929             if (xn == 0) {
2930                 if (yn == 0) { // no handle, consider it the continuation of the other one
2931                     an = 0;
2932                     collinear = TRUE;
2933                 }
2934                 else an = 0; // vertical; set the angle to horizontal
2935             } else an = yn/xn;
2937             if (xp == 0) {
2938                 if (yp == 0) { // no handle, consider it the continuation of the other one
2939                     ap = an;
2940                 }
2941                 else ap = 0; // vertical; set the angle to horizontal
2942             } else  ap = yp/xp;
2944             if (collinear) an = ap;
2946             // angles of the perpendiculars; HUGE_VAL means vertical
2947             if (an == 0) na = HUGE_VAL; else na = -1/an;
2948             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2950             // mouse point relative to the node's original pos
2951             pr = (*p) - n->origin;
2953             // distances to the four lines (two handles and two perpendiculars)
2954             d_an = point_line_distance(&pr, an);
2955             d_na = point_line_distance(&pr, na);
2956             d_ap = point_line_distance(&pr, ap);
2957             d_pa = point_line_distance(&pr, pa);
2959             // find out which line is the closest, save its closest point in c
2960             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2961                 point_line_closest(&pr, an, &c);
2962             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2963                 point_line_closest(&pr, ap, &c);
2964             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2965                 point_line_closest(&pr, na, &c);
2966             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2967                 point_line_closest(&pr, pa, &c);
2968             }
2970             // move the node to the closest point
2971             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2972                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2973                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2975         } else {  // constraining to hor/vert
2977             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2978                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2979             } else { // snap to vert
2980                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2981             }
2982         }
2983     } else { // move freely
2984         if (state & GDK_MOD1_MASK) { // sculpt
2985             sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
2986         } else {
2987             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2988                                         (*p)[NR::X] - n->pos[NR::X],
2989                                         (*p)[NR::Y] - n->pos[NR::Y],
2990                                         (state & GDK_SHIFT_MASK) == 0);
2991         }
2992     }
2994     n->subpath->nodepath->desktop->scroll_to_point(p);
2996     return TRUE;
2999 /**
3000  * Node handle clicked callback.
3001  */
3002 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3004    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3006     if (state & GDK_CONTROL_MASK) { // "delete" handle
3007         if (n->p.knot == knot) {
3008             n->p.pos = n->pos;
3009         } else if (n->n.knot == knot) {
3010             n->n.pos = n->pos;
3011         }
3012         sp_node_update_handles(n);
3013         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3014         sp_nodepath_update_repr(nodepath);
3015         sp_nodepath_update_statusbar(nodepath);
3017     } else { // just select or add to selection, depending in Shift
3018         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3019     }
3022 /**
3023  * Node handle grabbed callback.
3024  */
3025 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3027    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3029     if (!n->selected) {
3030         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3031     }
3033     // remember the origin point of the handle
3034     if (n->p.knot == knot) {
3035         n->p.origin_radial = n->p.pos - n->pos;
3036     } else if (n->n.knot == knot) {
3037         n->n.origin_radial = n->n.pos - n->pos;
3038     } else {
3039         g_assert_not_reached();
3040     }
3044 /**
3045  * Node handle ungrabbed callback.
3046  */
3047 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3049    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3051     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3052     if (n->p.knot == knot) {
3053         n->p.origin_radial.a = 0;
3054         sp_knot_set_position(knot, &n->p.pos, state);
3055     } else if (n->n.knot == knot) {
3056         n->n.origin_radial.a = 0;
3057         sp_knot_set_position(knot, &n->n.pos, state);
3058     } else {
3059         g_assert_not_reached();
3060     }
3062     sp_nodepath_update_repr(n->subpath->nodepath);
3065 /**
3066  * Node handle "request" signal callback.
3067  */
3068 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3070     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3072     Inkscape::NodePath::NodeSide *me, *opposite;
3073     gint which;
3074     if (n->p.knot == knot) {
3075         me = &n->p;
3076         opposite = &n->n;
3077         which = -1;
3078     } else if (n->n.knot == knot) {
3079         me = &n->n;
3080         opposite = &n->p;
3081         which = 1;
3082     } else {
3083         me = opposite = NULL;
3084         which = 0;
3085         g_assert_not_reached();
3086     }
3088     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3090     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3092     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3093         /* We are smooth node adjacent with line */
3094         NR::Point const delta = *p - n->pos;
3095         NR::Coord const len = NR::L2(delta);
3096         Inkscape::NodePath::Node *othernode = opposite->other;
3097         NR::Point const ndelta = n->pos - othernode->pos;
3098         NR::Coord const linelen = NR::L2(ndelta);
3099         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3100             NR::Coord const scal = dot(delta, ndelta) / linelen;
3101             (*p) = n->pos + (scal / linelen) * ndelta;
3102         }
3103         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3104     } else {
3105         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3106     }
3108     sp_node_adjust_handle(n, -which);
3110     return FALSE;
3113 /**
3114  * Node handle moved callback.
3115  */
3116 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3118    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3120    Inkscape::NodePath::NodeSide *me;
3121    Inkscape::NodePath::NodeSide *other;
3122     if (n->p.knot == knot) {
3123         me = &n->p;
3124         other = &n->n;
3125     } else if (n->n.knot == knot) {
3126         me = &n->n;
3127         other = &n->p;
3128     } else {
3129         me = NULL;
3130         other = NULL;
3131         g_assert_not_reached();
3132     }
3134     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3135     Radial rme(me->pos - n->pos);
3136     Radial rother(other->pos - n->pos);
3137     Radial rnew(*p - n->pos);
3139     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3140         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3141         /* 0 interpreted as "no snapping". */
3143         // The closest PI/snaps angle, starting from zero.
3144         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3145         if (me->origin_radial.a == HUGE_VAL) {
3146             // ortho doesn't exist: original handle was zero length.
3147             rnew.a = a_snapped;
3148         } else {
3149             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3150              * its opposite and perpendiculars). */
3151             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3153             // Snap to the closest.
3154             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3155                        ? a_snapped
3156                        : a_ortho );
3157         }
3158     }
3160     if (state & GDK_MOD1_MASK) {
3161         // lock handle length
3162         rnew.r = me->origin_radial.r;
3163     }
3165     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3166         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3167         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3168         rother.a += rnew.a - rme.a;
3169         other->pos = NR::Point(rother) + n->pos;
3170         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3171         sp_knot_set_position(other->knot, &other->pos, 0);
3172     }
3174     me->pos = NR::Point(rnew) + n->pos;
3175     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3177     // this is what sp_knot_set_position does, but without emitting the signal:
3178     // we cannot emit a "moved" signal because we're now processing it
3179     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3181     knot->desktop->set_coordinate_status(me->pos);
3183     update_object(n->subpath->nodepath);
3185     /* status text */
3186     SPDesktop *desktop = n->subpath->nodepath->desktop;
3187     if (!desktop) return;
3188     SPEventContext *ec = desktop->event_context;
3189     if (!ec) return;
3190     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3191     if (!mc) return;
3193     double degrees = 180 / M_PI * rnew.a;
3194     if (degrees > 180) degrees -= 360;
3195     if (degrees < -180) degrees += 360;
3196     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3197         degrees = angle_to_compass (degrees);
3199     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3201     mc->setF(Inkscape::NORMAL_MESSAGE,
3202          _("<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);
3204     g_string_free(length, TRUE);
3207 /**
3208  * Node handle event callback.
3209  */
3210 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3212     gboolean ret = FALSE;
3213     switch (event->type) {
3214         case GDK_KEY_PRESS:
3215             switch (get_group0_keyval (&event->key)) {
3216                 case GDK_space:
3217                     if (event->key.state & GDK_BUTTON1_MASK) {
3218                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3219                         stamp_repr(nodepath);
3220                         ret = TRUE;
3221                     }
3222                     break;
3223                 default:
3224                     break;
3225             }
3226             break;
3227         default:
3228             break;
3229     }
3231     return ret;
3234 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3235                                  Radial &rme, Radial &rother, gboolean const both)
3237     rme.a += angle;
3238     if ( both
3239          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3240          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3241     {
3242         rother.a += angle;
3243     }
3246 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3247                                         Radial &rme, Radial &rother, gboolean const both)
3249     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3251     gdouble r;
3252     if ( both
3253          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3254          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3255     {
3256         r = MAX(rme.r, rother.r);
3257     } else {
3258         r = rme.r;
3259     }
3261     gdouble const weird_angle = atan2(norm_angle, r);
3262 /* Bulia says norm_angle is just the visible distance that the
3263  * object's end must travel on the screen.  Left as 'angle' for want of
3264  * a better name.*/
3266     rme.a += weird_angle;
3267     if ( both
3268          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3269          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3270     {
3271         rother.a += weird_angle;
3272     }
3275 /**
3276  * Rotate one node.
3277  */
3278 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3280     Inkscape::NodePath::NodeSide *me, *other;
3281     bool both = false;
3283     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3284     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3286     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3287         me = &(n->p);
3288         other = &(n->n);
3289     } else if (!n->p.other) {
3290         me = &(n->n);
3291         other = &(n->p);
3292     } else {
3293         if (which > 0) { // right handle
3294             if (xn > xp) {
3295                 me = &(n->n);
3296                 other = &(n->p);
3297             } else {
3298                 me = &(n->p);
3299                 other = &(n->n);
3300             }
3301         } else if (which < 0){ // left handle
3302             if (xn <= xp) {
3303                 me = &(n->n);
3304                 other = &(n->p);
3305             } else {
3306                 me = &(n->p);
3307                 other = &(n->n);
3308             }
3309         } else { // both handles
3310             me = &(n->n);
3311             other = &(n->p);
3312             both = true;
3313         }
3314     }
3316     Radial rme(me->pos - n->pos);
3317     Radial rother(other->pos - n->pos);
3319     if (screen) {
3320         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3321     } else {
3322         node_rotate_one_internal (*n, angle, rme, rother, both);
3323     }
3325     me->pos = n->pos + NR::Point(rme);
3327     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3328         other->pos =  n->pos + NR::Point(rother);
3329     }
3331     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3332     // so here we just move all the knots without emitting move signals, for speed
3333     sp_node_update_handles(n, false);
3336 /**
3337  * Rotate selected nodes.
3338  */
3339 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3341     if (!nodepath || !nodepath->selected) return;
3343     if (g_list_length(nodepath->selected) == 1) {
3344        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3345         node_rotate_one (n, angle, which, screen);
3346     } else {
3347        // rotate as an object:
3349         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3350         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3351         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3352             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3353             box.expandTo (n->pos); // contain all selected nodes
3354         }
3356         gdouble rot;
3357         if (screen) {
3358             gdouble const zoom = nodepath->desktop->current_zoom();
3359             gdouble const zmove = angle / zoom;
3360             gdouble const r = NR::L2(box.max() - box.midpoint());
3361             rot = atan2(zmove, r);
3362         } else {
3363             rot = angle;
3364         }
3366         NR::Matrix t =
3367             NR::Matrix (NR::translate(-box.midpoint())) *
3368             NR::Matrix (NR::rotate(rot)) *
3369             NR::Matrix (NR::translate(box.midpoint()));
3371         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3372             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3373             n->pos *= t;
3374             n->n.pos *= t;
3375             n->p.pos *= t;
3376             sp_node_update_handles(n, false);
3377         }
3378     }
3380     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3383 /**
3384  * Scale one node.
3385  */
3386 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3388     bool both = false;
3389     Inkscape::NodePath::NodeSide *me, *other;
3391     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3392     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3394     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3395         me = &(n->p);
3396         other = &(n->n);
3397         n->code = NR_CURVETO;
3398     } else if (!n->p.other) {
3399         me = &(n->n);
3400         other = &(n->p);
3401         if (n->n.other)
3402             n->n.other->code = NR_CURVETO;
3403     } else {
3404         if (which > 0) { // right handle
3405             if (xn > xp) {
3406                 me = &(n->n);
3407                 other = &(n->p);
3408                 if (n->n.other)
3409                     n->n.other->code = NR_CURVETO;
3410             } else {
3411                 me = &(n->p);
3412                 other = &(n->n);
3413                 n->code = NR_CURVETO;
3414             }
3415         } else if (which < 0){ // left handle
3416             if (xn <= xp) {
3417                 me = &(n->n);
3418                 other = &(n->p);
3419                 if (n->n.other)
3420                     n->n.other->code = NR_CURVETO;
3421             } else {
3422                 me = &(n->p);
3423                 other = &(n->n);
3424                 n->code = NR_CURVETO;
3425             }
3426         } else { // both handles
3427             me = &(n->n);
3428             other = &(n->p);
3429             both = true;
3430             n->code = NR_CURVETO;
3431             if (n->n.other)
3432                 n->n.other->code = NR_CURVETO;
3433         }
3434     }
3436     Radial rme(me->pos - n->pos);
3437     Radial rother(other->pos - n->pos);
3439     rme.r += grow;
3440     if (rme.r < 0) rme.r = 0;
3441     if (rme.a == HUGE_VAL) {
3442         if (me->other) { // if direction is unknown, initialize it towards the next node
3443             Radial rme_next(me->other->pos - n->pos);
3444             rme.a = rme_next.a;
3445         } else { // if there's no next, initialize to 0
3446             rme.a = 0;
3447         }
3448     }
3449     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3450         rother.r += grow;
3451         if (rother.r < 0) rother.r = 0;
3452         if (rother.a == HUGE_VAL) {
3453             rother.a = rme.a + M_PI;
3454         }
3455     }
3457     me->pos = n->pos + NR::Point(rme);
3459     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3460         other->pos = n->pos + NR::Point(rother);
3461     }
3463     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3464     // so here we just move all the knots without emitting move signals, for speed
3465     sp_node_update_handles(n, false);
3468 /**
3469  * Scale selected nodes.
3470  */
3471 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3473     if (!nodepath || !nodepath->selected) return;
3475     if (g_list_length(nodepath->selected) == 1) {
3476         // scale handles of the single selected node
3477         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3478         node_scale_one (n, grow, which);
3479     } else {
3480         // scale nodes as an "object":
3482         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3483         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3484         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3485             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3486             box.expandTo (n->pos); // contain all selected nodes
3487         }
3489         double scale = (box.maxExtent() + grow)/box.maxExtent();
3491         NR::Matrix t =
3492             NR::Matrix (NR::translate(-box.midpoint())) *
3493             NR::Matrix (NR::scale(scale, scale)) *
3494             NR::Matrix (NR::translate(box.midpoint()));
3496         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3497             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3498             n->pos *= t;
3499             n->n.pos *= t;
3500             n->p.pos *= t;
3501             sp_node_update_handles(n, false);
3502         }
3503     }
3505     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3508 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3510     if (!nodepath) return;
3511     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3514 /**
3515  * Flip selected nodes horizontally/vertically.
3516  */
3517 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3519     if (!nodepath || !nodepath->selected) return;
3521     if (g_list_length(nodepath->selected) == 1) {
3522         // flip handles of the single selected node
3523         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3524         double temp = n->p.pos[axis];
3525         n->p.pos[axis] = n->n.pos[axis];
3526         n->n.pos[axis] = temp;
3527         sp_node_update_handles(n, false);
3528     } else {
3529         // scale nodes as an "object":
3531         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3532         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3533         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3534             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3535             box.expandTo (n->pos); // contain all selected nodes
3536         }
3538         NR::Matrix t =
3539             NR::Matrix (NR::translate(-box.midpoint())) *
3540             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3541             NR::Matrix (NR::translate(box.midpoint()));
3543         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3544             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3545             n->pos *= t;
3546             n->n.pos *= t;
3547             n->p.pos *= t;
3548             sp_node_update_handles(n, false);
3549         }
3550     }
3552     sp_nodepath_update_repr(nodepath);
3555 //-----------------------------------------------
3556 /**
3557  * Return new subpath under given nodepath.
3558  */
3559 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3561     g_assert(nodepath);
3562     g_assert(nodepath->desktop);
3564    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3566     s->nodepath = nodepath;
3567     s->closed = FALSE;
3568     s->nodes = NULL;
3569     s->first = NULL;
3570     s->last = NULL;
3572     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3573     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3574     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3576     return s;
3579 /**
3580  * Destroy nodes in subpath, then subpath itself.
3581  */
3582 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3584     g_assert(subpath);
3585     g_assert(subpath->nodepath);
3586     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3588     while (subpath->nodes) {
3589         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3590     }
3592     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3594     g_free(subpath);
3597 /**
3598  * Link head to tail in subpath.
3599  */
3600 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3602     g_assert(!sp->closed);
3603     g_assert(sp->last != sp->first);
3604     g_assert(sp->first->code == NR_MOVETO);
3606     sp->closed = TRUE;
3608     //Link the head to the tail
3609     sp->first->p.other = sp->last;
3610     sp->last->n.other  = sp->first;
3611     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3612     sp->first          = sp->last;
3614     //Remove the extra end node
3615     sp_nodepath_node_destroy(sp->last->n.other);
3618 /**
3619  * Open closed (loopy) subpath at node.
3620  */
3621 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3623     g_assert(sp->closed);
3624     g_assert(n->subpath == sp);
3625     g_assert(sp->first == sp->last);
3627     /* We create new startpoint, current node will become last one */
3629    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3630                                                 &n->pos, &n->pos, &n->n.pos);
3633     sp->closed        = FALSE;
3635     //Unlink to make a head and tail
3636     sp->first         = new_path;
3637     sp->last          = n;
3638     n->n.other        = NULL;
3639     new_path->p.other = NULL;
3642 /**
3643  * Returns area in triangle given by points; may be negative.
3644  */
3645 inline double
3646 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3648     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]);
3651 /**
3652  * Return new node in subpath with given properties.
3653  * \param pos Position of node.
3654  * \param ppos Handle position in previous direction
3655  * \param npos Handle position in previous direction
3656  */
3657 Inkscape::NodePath::Node *
3658 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)
3660     g_assert(sp);
3661     g_assert(sp->nodepath);
3662     g_assert(sp->nodepath->desktop);
3664     if (nodechunk == NULL)
3665         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3667     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3669     n->subpath  = sp;
3671     if (type != Inkscape::NodePath::NODE_NONE) {
3672         // use the type from sodipodi:nodetypes
3673         n->type = type;
3674     } else {
3675         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3676             // points are (almost) collinear
3677             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3678                 // endnode, or a node with a retracted handle
3679                 n->type = Inkscape::NodePath::NODE_CUSP;
3680             } else {
3681                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3682             }
3683         } else {
3684             n->type = Inkscape::NodePath::NODE_CUSP;
3685         }
3686     }
3688     n->code     = code;
3689     n->selected = FALSE;
3690     n->pos      = *pos;
3691     n->p.pos    = *ppos;
3692     n->n.pos    = *npos;
3694     n->dragging_out = NULL;
3696     Inkscape::NodePath::Node *prev;
3697     if (next) {
3698         //g_assert(g_list_find(sp->nodes, next));
3699         prev = next->p.other;
3700     } else {
3701         prev = sp->last;
3702     }
3704     if (prev)
3705         prev->n.other = n;
3706     else
3707         sp->first = n;
3709     if (next)
3710         next->p.other = n;
3711     else
3712         sp->last = n;
3714     n->p.other = prev;
3715     n->n.other = next;
3717     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"));
3718     sp_knot_set_position(n->knot, pos, 0);
3720     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3721     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3722     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3723     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3724     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3725     sp_knot_update_ctrl(n->knot);
3727     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3728     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3729     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3730     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3731     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3732     sp_knot_show(n->knot);
3734     // We only create handle knots and lines on demand
3735     n->p.knot = NULL;
3736     n->p.line = NULL;
3737     n->n.knot = NULL;
3738     n->n.line = NULL;
3740     sp->nodes = g_list_prepend(sp->nodes, n);
3742     return n;
3745 /**
3746  * Destroy node and its knots, link neighbors in subpath.
3747  */
3748 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3750     g_assert(node);
3751     g_assert(node->subpath);
3752     g_assert(SP_IS_KNOT(node->knot));
3754    Inkscape::NodePath::SubPath *sp = node->subpath;
3756     if (node->selected) { // first, deselect
3757         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3758         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3759     }
3761     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3763     g_object_unref(G_OBJECT(node->knot));
3764     if (node->p.knot)
3765         g_object_unref(G_OBJECT(node->p.knot));
3766     if (node->n.knot)
3767         g_object_unref(G_OBJECT(node->n.knot));
3769     if (node->p.line)
3770         gtk_object_destroy(GTK_OBJECT(node->p.line));
3771     if (node->n.line)
3772         gtk_object_destroy(GTK_OBJECT(node->n.line));
3774     if (sp->nodes) { // there are others nodes on the subpath
3775         if (sp->closed) {
3776             if (sp->first == node) {
3777                 g_assert(sp->last == node);
3778                 sp->first = node->n.other;
3779                 sp->last = sp->first;
3780             }
3781             node->p.other->n.other = node->n.other;
3782             node->n.other->p.other = node->p.other;
3783         } else {
3784             if (sp->first == node) {
3785                 sp->first = node->n.other;
3786                 sp->first->code = NR_MOVETO;
3787             }
3788             if (sp->last == node) sp->last = node->p.other;
3789             if (node->p.other) node->p.other->n.other = node->n.other;
3790             if (node->n.other) node->n.other->p.other = node->p.other;
3791         }
3792     } else { // this was the last node on subpath
3793         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3794     }
3796     g_mem_chunk_free(nodechunk, node);
3799 /**
3800  * Returns one of the node's two sides.
3801  * \param which Indicates which side.
3802  * \return Pointer to previous node side if which==-1, next if which==1.
3803  */
3804 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3806     g_assert(node);
3808     switch (which) {
3809         case -1:
3810             return &node->p;
3811         case 1:
3812             return &node->n;
3813         default:
3814             break;
3815     }
3817     g_assert_not_reached();
3819     return NULL;
3822 /**
3823  * Return the other side of the node, given one of its sides.
3824  */
3825 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
3827     g_assert(node);
3829     if (me == &node->p) return &node->n;
3830     if (me == &node->n) return &node->p;
3832     g_assert_not_reached();
3834     return NULL;
3837 /**
3838  * Return NRPathcode on the given side of the node.
3839  */
3840 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3842     g_assert(node);
3844     if (me == &node->p) {
3845         if (node->p.other) return (NRPathcode)node->code;
3846         return NR_MOVETO;
3847     }
3849     if (me == &node->n) {
3850         if (node->n.other) return (NRPathcode)node->n.other->code;
3851         return NR_MOVETO;
3852     }
3854     g_assert_not_reached();
3856     return NR_END;
3859 /**
3860  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3861  */
3862 Inkscape::NodePath::Node *
3863 sp_nodepath_get_node_by_index(int index)
3865     Inkscape::NodePath::Node *e = NULL;
3867     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3868     if (!nodepath) {
3869         return e;
3870     }
3872     //find segment
3873     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3875         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3876         int n = g_list_length(sp->nodes);
3877         if (sp->closed) {
3878             n++;
3879         }
3881         //if the piece belongs to this subpath grab it
3882         //otherwise move onto the next subpath
3883         if (index < n) {
3884             e = sp->first;
3885             for (int i = 0; i < index; ++i) {
3886                 e = e->n.other;
3887             }
3888             break;
3889         } else {
3890             if (sp->closed) {
3891                 index -= (n+1);
3892             } else {
3893                 index -= n;
3894             }
3895         }
3896     }
3898     return e;
3901 /**
3902  * Returns plain text meaning of node type.
3903  */
3904 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3906     unsigned retracted = 0;
3907     bool endnode = false;
3909     for (int which = -1; which <= 1; which += 2) {
3910         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3911         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3912             retracted ++;
3913         if (!side->other)
3914             endnode = true;
3915     }
3917     if (retracted == 0) {
3918         if (endnode) {
3919                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3920                 return _("end node");
3921         } else {
3922             switch (node->type) {
3923                 case Inkscape::NodePath::NODE_CUSP:
3924                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3925                     return _("cusp");
3926                 case Inkscape::NodePath::NODE_SMOOTH:
3927                     // TRANSLATORS: "smooth" is an adjective here
3928                     return _("smooth");
3929                 case Inkscape::NodePath::NODE_SYMM:
3930                     return _("symmetric");
3931             }
3932         }
3933     } else if (retracted == 1) {
3934         if (endnode) {
3935             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3936             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3937         } else {
3938             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3939         }
3940     } else {
3941         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3942     }
3944     return NULL;
3947 /**
3948  * Handles content of statusbar as long as node tool is active.
3949  */
3950 void
3951 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3953     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");
3954     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3956     gint total_nodes = sp_nodepath_get_node_count(nodepath);
3957     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
3958     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
3959     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
3961     SPDesktop *desktop = NULL;
3962     if (nodepath) {
3963         desktop = nodepath->desktop;
3964     } else {
3965         desktop = SP_ACTIVE_DESKTOP;
3966     }
3968     SPEventContext *ec = desktop->event_context;
3969     if (!ec) return;
3970     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3971     if (!mc) return;
3973     if (selected_nodes == 0) {
3974         Inkscape::Selection *sel = desktop->selection;
3975         if (!sel || sel->isEmpty()) {
3976             mc->setF(Inkscape::NORMAL_MESSAGE,
3977                      _("Select a single object to edit its nodes or handles."));
3978         } else {
3979             if (nodepath) {
3980             mc->setF(Inkscape::NORMAL_MESSAGE,
3981                      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.",
3982                               "<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.",
3983                               total_nodes),
3984                      total_nodes);
3985             } else {
3986                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3987                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3988                 } else {
3989                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3990                 }
3991             }
3992         }
3993     } else if (nodepath && selected_nodes == 1) {
3994         mc->setF(Inkscape::NORMAL_MESSAGE,
3995                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3996                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3997                           total_nodes),
3998                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3999     } else {
4000         if (selected_subpaths > 1) {
4001             mc->setF(Inkscape::NORMAL_MESSAGE,
4002                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4003                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4004                               total_nodes),
4005                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4006         } else {
4007             mc->setF(Inkscape::NORMAL_MESSAGE,
4008                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4009                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4010                               total_nodes),
4011                      selected_nodes, total_nodes, when_selected);
4012         }
4013     }
4017 /*
4018   Local Variables:
4019   mode:c++
4020   c-file-style:"stroustrup"
4021   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4022   indent-tabs-mode:nil
4023   fill-column:99
4024   End:
4025 */
4026 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :