Code

b8de67ef0895e86cfe06dd2c6ff8b0e00b62da37
[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     double n_sel_range = 0, p_sel_range = 0;
1048     guint n_nodes = 0, p_nodes = 0;
1049     guint n_sel_nodes = 0, p_sel_nodes = 0;
1051     // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1052     {
1053         double n_range = 0, p_range = 0;
1054         bool n_going = true, p_going = true;
1055         Inkscape::NodePath::Node *n_node = n;
1056         Inkscape::NodePath::Node *p_node = n;
1057         do {
1058             // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1059             if (n_node && n_going)
1060                 n_node = n_node->n.other;
1061             if (n_node == NULL) {
1062                 n_going = false;
1063             } else {
1064                 n_nodes ++;
1065                 n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1066                 if (n_node->selected) {
1067                     n_sel_nodes ++;
1068                     n_sel_range = n_range;
1069                 }
1070                 if (n_node == p_node) {
1071                     n_going = false;
1072                     p_going = false;
1073                 }
1074             }
1075             if (p_node && p_going)
1076                 p_node = p_node->p.other;
1077             if (p_node == NULL) {
1078                 p_going = false;
1079             } else {
1080                 p_nodes ++;
1081                 p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1082                 if (p_node->selected) {
1083                     p_sel_nodes ++;
1084                     p_sel_range = p_range;
1085                 }
1086                 if (p_node == n_node) {
1087                     n_going = false;
1088                     p_going = false;
1089                 }
1090             }
1091         } while (n_going || p_going);
1092     }
1094     // Second pass: actually move nodes
1095     sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1096     {
1097         double n_range = 0, p_range = 0;
1098         bool n_going = true, p_going = true;
1099         Inkscape::NodePath::Node *n_node = n;
1100         Inkscape::NodePath::Node *p_node = n;
1101         do {
1102             // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1103             if (n_node && n_going)
1104                 n_node = n_node->n.other;
1105             if (n_node == NULL) {
1106                 n_going = false;
1107             } else {
1108                 n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1109                 if (n_node->selected) {
1110                     sp_nodepath_move_node_and_handles (n_node, 
1111                                       sculpt_profile (n_range / n_sel_range, alpha) * delta,
1112                                       sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha) * delta,
1113                                       sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha) * delta);
1114                 }
1115                 if (n_node == p_node) {
1116                     n_going = false;
1117                     p_going = false;
1118                 }
1119             }
1120             if (p_node && p_going)
1121                 p_node = p_node->p.other;
1122             if (p_node == NULL) {
1123                 p_going = false;
1124             } else {
1125                 p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1126                 if (p_node->selected) {
1127                     sp_nodepath_move_node_and_handles (p_node, 
1128                                       sculpt_profile (p_range / p_sel_range, alpha) * delta,
1129                                       sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha) * delta,
1130                                       sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha) * delta);
1131                 }
1132                 if (p_node == n_node) {
1133                     n_going = false;
1134                     p_going = false;
1135                 }
1136             }
1137         } while (n_going || p_going);
1138     }
1140     // do not update repr here so that node dragging is acceptably fast
1141     update_object(nodepath);
1145 /**
1146  * Move node selection to point, adjust its and neighbouring handles,
1147  * handle possible snapping, and commit the change with possible undo.
1148  */
1149 void
1150 sp_node_selected_move(gdouble dx, gdouble dy)
1152     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1153     if (!nodepath) return;
1155     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1157     if (dx == 0) {
1158         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1159     } else if (dy == 0) {
1160         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1161     } else {
1162         sp_nodepath_update_repr(nodepath);
1163     }
1166 /**
1167  * Move node selection off screen and commit the change.
1168  */
1169 void
1170 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1172     // borrowed from sp_selection_move_screen in selection-chemistry.c
1173     // we find out the current zoom factor and divide deltas by it
1174     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1176     gdouble zoom = desktop->current_zoom();
1177     gdouble zdx = dx / zoom;
1178     gdouble zdy = dy / zoom;
1180     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1181     if (!nodepath) return;
1183     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1185     if (dx == 0) {
1186         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1187     } else if (dy == 0) {
1188         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1189     } else {
1190         sp_nodepath_update_repr(nodepath);
1191     }
1194 /** If they don't yet exist, creates knot and line for the given side of the node */
1195 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1197     if (!side->knot) {
1198         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"));
1200         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1201         side->knot->setSize (7);
1202         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1203         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1204         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1205         sp_knot_update_ctrl(side->knot);
1207         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1208         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1209         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1210         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1211         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1212         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1213     }
1215     if (!side->line) {
1216         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1217                                         SP_TYPE_CTRLLINE, NULL);
1218     }
1221 /**
1222  * Ensure the given handle of the node is visible/invisible, update its screen position
1223  */
1224 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1226     g_assert(node != NULL);
1228    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1229     NRPathcode code = sp_node_path_code_from_side(node, side);
1231     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1233     if (show_handle) {
1234         if (!side->knot) { // No handle knot at all
1235             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1236             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1237             side->knot->pos = side->pos;
1238             if (side->knot->item) 
1239                 SP_CTRL(side->knot->item)->moveto(side->pos);
1240             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1241             sp_knot_show(side->knot);
1242         } else {
1243             if (side->knot->pos != side->pos) { // only if it's really moved
1244                 if (fire_move_signals) {
1245                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1246                 } else {
1247                     sp_knot_moveto(side->knot, &side->pos);
1248                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1249                 }
1250             }
1251             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1252                 sp_knot_show(side->knot);
1253             }
1254         }
1255         sp_canvas_item_show(side->line);
1256     } else {
1257         if (side->knot) {
1258             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1259                 sp_knot_hide(side->knot);
1260             }
1261         }
1262         if (side->line) {
1263             sp_canvas_item_hide(side->line);
1264         }
1265     }
1268 /**
1269  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1270  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1271  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1272  * updated; otherwise, just move the knots silently (used in batch moves).
1273  */
1274 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1276     g_assert(node != NULL);
1278     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1279         sp_knot_show(node->knot);
1280     }
1282     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1283         if (fire_move_signals)
1284             sp_knot_set_position(node->knot, &node->pos, 0);
1285         else 
1286             sp_knot_moveto(node->knot, &node->pos);
1287     }
1289     gboolean show_handles = node->selected;
1290     if (node->p.other != NULL) {
1291         if (node->p.other->selected) show_handles = TRUE;
1292     }
1293     if (node->n.other != NULL) {
1294         if (node->n.other->selected) show_handles = TRUE;
1295     }
1297     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1298     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1301 /**
1302  * Call sp_node_update_handles() for all nodes on subpath.
1303  */
1304 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1306     g_assert(subpath != NULL);
1308     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1309         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1310     }
1313 /**
1314  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1315  */
1316 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1318     g_assert(nodepath != NULL);
1320     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1321         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1322     }
1325 /**
1326  * Adds all selected nodes in nodepath to list.
1327  */
1328 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1330     StlConv<Node *>::list(l, selected);
1331 /// \todo this adds a copying, rework when the selection becomes a stl list
1334 /**
1335  * Align selected nodes on the specified axis.
1336  */
1337 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1339     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1340         return;
1341     }
1343     if ( !nodepath->selected->next ) { // only one node selected
1344         return;
1345     }
1346    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1347     NR::Point dest(pNode->pos);
1348     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1349         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1350         if (pNode) {
1351             dest[axis] = pNode->pos[axis];
1352             sp_node_moveto(pNode, dest);
1353         }
1354     }
1356     sp_nodepath_update_repr(nodepath);
1359 /// Helper struct.
1360 struct NodeSort
1362    Inkscape::NodePath::Node *_node;
1363     NR::Coord _coord;
1364     /// \todo use vectorof pointers instead of calling copy ctor
1365     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1366         _node(node), _coord(node->pos[axis])
1367     {}
1369 };
1371 static bool operator<(NodeSort const &a, NodeSort const &b)
1373     return (a._coord < b._coord);
1376 /**
1377  * Distribute selected nodes on the specified axis.
1378  */
1379 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1381     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1382         return;
1383     }
1385     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1386         return;
1387     }
1389    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1390     std::vector<NodeSort> sorted;
1391     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1392         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1393         if (pNode) {
1394             NodeSort n(pNode, axis);
1395             sorted.push_back(n);
1396             //dest[axis] = pNode->pos[axis];
1397             //sp_node_moveto(pNode, dest);
1398         }
1399     }
1400     std::sort(sorted.begin(), sorted.end());
1401     unsigned int len = sorted.size();
1402     //overall bboxes span
1403     float dist = (sorted.back()._coord -
1404                   sorted.front()._coord);
1405     //new distance between each bbox
1406     float step = (dist) / (len - 1);
1407     float pos = sorted.front()._coord;
1408     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1409           it < sorted.end();
1410           it ++ )
1411     {
1412         NR::Point dest((*it)._node->pos);
1413         dest[axis] = pos;
1414         sp_node_moveto((*it)._node, dest);
1415         pos += step;
1416     }
1418     sp_nodepath_update_repr(nodepath);
1422 /**
1423  * Call sp_nodepath_line_add_node() for all selected segments.
1424  */
1425 void
1426 sp_node_selected_add_node(void)
1428     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1429     if (!nodepath) {
1430         return;
1431     }
1433     GList *nl = NULL;
1435     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1436        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1437         g_assert(t->selected);
1438         if (t->p.other && t->p.other->selected) {
1439             nl = g_list_prepend(nl, t);
1440         }
1441     }
1443     while (nl) {
1444        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1445        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1446         sp_nodepath_node_select(n, TRUE, FALSE);
1447         nl = g_list_remove(nl, t);
1448     }
1450     /** \todo fixme: adjust ? */
1451     sp_nodepath_update_handles(nodepath);
1453     sp_nodepath_update_repr(nodepath);
1455     sp_nodepath_update_statusbar(nodepath);
1458 /**
1459  * Select segment nearest to point
1460  */
1461 void
1462 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1464     if (!nodepath) {
1465         return;
1466     }
1468     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1470     //find segment to segment
1471     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1473     gboolean force = FALSE;
1474     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1475         force = TRUE;
1476     }
1477     sp_nodepath_node_select(e, (gboolean) toggle, force);
1478     if (e->p.other)
1479         sp_nodepath_node_select(e->p.other, TRUE, force);
1481     sp_nodepath_update_handles(nodepath);
1483     sp_nodepath_update_statusbar(nodepath);
1486 /**
1487  * Add a node nearest to point
1488  */
1489 void
1490 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1492     if (!nodepath) {
1493         return;
1494     }
1496     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1498     //find segment to split
1499     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1501     //don't know why but t seems to flip for lines
1502     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1503         position.t = 1.0 - position.t;
1504     }
1505     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1506     sp_nodepath_node_select(n, FALSE, TRUE);
1508     /* fixme: adjust ? */
1509     sp_nodepath_update_handles(nodepath);
1511     sp_nodepath_update_repr(nodepath);
1513     sp_nodepath_update_statusbar(nodepath);
1516 /*
1517  * Adjusts a segment so that t moves by a certain delta for dragging
1518  * converts lines to curves
1519  *
1520  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1521  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1522  */
1523 void
1524 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1526     /* feel good is an arbitrary parameter that distributes the delta between handles
1527      * if t of the drag point is less than 1/6 distance form the endpoint only
1528      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1529      */
1530     double feel_good;
1531     if (t <= 1.0 / 6.0)
1532         feel_good = 0;
1533     else if (t <= 0.5)
1534         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1535     else if (t <= 5.0 / 6.0)
1536         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1537     else
1538         feel_good = 1;
1540     //if we're dragging a line convert it to a curve
1541     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1542         sp_nodepath_set_line_type(e, NR_CURVETO);
1543     }
1545     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1546     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1547     e->p.other->n.pos += offsetcoord0;
1548     e->p.pos += offsetcoord1;
1550     // adjust handles of adjacent nodes where necessary
1551     sp_node_adjust_handle(e,1);
1552     sp_node_adjust_handle(e->p.other,-1);
1554     sp_nodepath_update_handles(e->subpath->nodepath);
1556     update_object(e->subpath->nodepath);
1558     sp_nodepath_update_statusbar(e->subpath->nodepath);
1562 /**
1563  * Call sp_nodepath_break() for all selected segments.
1564  */
1565 void sp_node_selected_break()
1567     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1568     if (!nodepath) return;
1570     GList *temp = NULL;
1571     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1572        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1573        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1574         if (nn == NULL) continue; // no break, no new node
1575         temp = g_list_prepend(temp, nn);
1576     }
1578     if (temp) {
1579         sp_nodepath_deselect(nodepath);
1580     }
1581     for (GList *l = temp; l != NULL; l = l->next) {
1582         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1583     }
1585     sp_nodepath_update_handles(nodepath);
1587     sp_nodepath_update_repr(nodepath);
1590 /**
1591  * Duplicate the selected node(s).
1592  */
1593 void sp_node_selected_duplicate()
1595     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1596     if (!nodepath) {
1597         return;
1598     }
1600     GList *temp = NULL;
1601     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1602        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1603        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1604         if (nn == NULL) continue; // could not duplicate
1605         temp = g_list_prepend(temp, nn);
1606     }
1608     if (temp) {
1609         sp_nodepath_deselect(nodepath);
1610     }
1611     for (GList *l = temp; l != NULL; l = l->next) {
1612         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1613     }
1615     sp_nodepath_update_handles(nodepath);
1617     sp_nodepath_update_repr(nodepath);
1620 /**
1621  *  Join two nodes by merging them into one.
1622  */
1623 void sp_node_selected_join()
1625     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1626     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1628     if (g_list_length(nodepath->selected) != 2) {
1629         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1630         return;
1631     }
1633    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1634    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1636     g_assert(a != b);
1637     g_assert(a->p.other || a->n.other);
1638     g_assert(b->p.other || b->n.other);
1640     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1641         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1642         return;
1643     }
1645     /* a and b are endpoints */
1647     NR::Point c;
1648     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1649         c = a->pos;
1650     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1651         c = b->pos;
1652     } else {
1653         c = (a->pos + b->pos) / 2;
1654     }
1656     if (a->subpath == b->subpath) {
1657        Inkscape::NodePath::SubPath *sp = a->subpath;
1658         sp_nodepath_subpath_close(sp);
1659         sp_node_moveto (sp->first, c);
1661         sp_nodepath_update_handles(sp->nodepath);
1662         sp_nodepath_update_repr(nodepath);
1663         return;
1664     }
1666     /* a and b are separate subpaths */
1667    Inkscape::NodePath::SubPath *sa = a->subpath;
1668    Inkscape::NodePath::SubPath *sb = b->subpath;
1669     NR::Point p;
1670    Inkscape::NodePath::Node *n;
1671     NRPathcode code;
1672     if (a == sa->first) {
1673         p = sa->first->n.pos;
1674         code = (NRPathcode)sa->first->n.other->code;
1675        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1676         n = sa->last;
1677         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1678         n = n->p.other;
1679         while (n) {
1680             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1681             n = n->p.other;
1682             if (n == sa->first) n = NULL;
1683         }
1684         sp_nodepath_subpath_destroy(sa);
1685         sa = t;
1686     } else if (a == sa->last) {
1687         p = sa->last->p.pos;
1688         code = (NRPathcode)sa->last->code;
1689         sp_nodepath_node_destroy(sa->last);
1690     } else {
1691         code = NR_END;
1692         g_assert_not_reached();
1693     }
1695     if (b == sb->first) {
1696         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1697         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1698             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1699         }
1700     } else if (b == sb->last) {
1701         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1702         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1703             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1704         }
1705     } else {
1706         g_assert_not_reached();
1707     }
1708     /* and now destroy sb */
1710     sp_nodepath_subpath_destroy(sb);
1712     sp_nodepath_update_handles(sa->nodepath);
1714     sp_nodepath_update_repr(nodepath);
1716     sp_nodepath_update_statusbar(nodepath);
1719 /**
1720  *  Join two nodes by adding a segment between them.
1721  */
1722 void sp_node_selected_join_segment()
1724     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1725     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1727     if (g_list_length(nodepath->selected) != 2) {
1728         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1729         return;
1730     }
1732    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1733    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1735     g_assert(a != b);
1736     g_assert(a->p.other || a->n.other);
1737     g_assert(b->p.other || b->n.other);
1739     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1740         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1741         return;
1742     }
1744     if (a->subpath == b->subpath) {
1745        Inkscape::NodePath::SubPath *sp = a->subpath;
1747         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1748         sp->closed = TRUE;
1750         sp->first->p.other = sp->last;
1751         sp->last->n.other  = sp->first;
1753         sp_node_handle_mirror_p_to_n(sp->last);
1754         sp_node_handle_mirror_n_to_p(sp->first);
1756         sp->first->code = sp->last->code;
1757         sp->first       = sp->last;
1759         sp_nodepath_update_handles(sp->nodepath);
1761         sp_nodepath_update_repr(nodepath);
1763         return;
1764     }
1766     /* a and b are separate subpaths */
1767    Inkscape::NodePath::SubPath *sa = a->subpath;
1768    Inkscape::NodePath::SubPath *sb = b->subpath;
1770    Inkscape::NodePath::Node *n;
1771     NR::Point p;
1772     NRPathcode code;
1773     if (a == sa->first) {
1774         code = (NRPathcode) sa->first->n.other->code;
1775        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1776         n = sa->last;
1777         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1778         for (n = n->p.other; n != NULL; n = n->p.other) {
1779             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1780         }
1781         sp_nodepath_subpath_destroy(sa);
1782         sa = t;
1783     } else if (a == sa->last) {
1784         code = (NRPathcode)sa->last->code;
1785     } else {
1786         code = NR_END;
1787         g_assert_not_reached();
1788     }
1790     if (b == sb->first) {
1791         n = sb->first;
1792         sp_node_handle_mirror_p_to_n(sa->last);
1793         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1794         sp_node_handle_mirror_n_to_p(sa->last);
1795         for (n = n->n.other; n != NULL; n = n->n.other) {
1796             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1797         }
1798     } else if (b == sb->last) {
1799         n = sb->last;
1800         sp_node_handle_mirror_p_to_n(sa->last);
1801         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1802         sp_node_handle_mirror_n_to_p(sa->last);
1803         for (n = n->p.other; n != NULL; n = n->p.other) {
1804             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1805         }
1806     } else {
1807         g_assert_not_reached();
1808     }
1809     /* and now destroy sb */
1811     sp_nodepath_subpath_destroy(sb);
1813     sp_nodepath_update_handles(sa->nodepath);
1815     sp_nodepath_update_repr(nodepath);
1818 /**
1819  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1820  */
1821 void sp_node_delete_preserve(GList *nodes_to_delete)
1823     GSList *nodepaths = NULL;
1824     
1825     while (nodes_to_delete) {
1826         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1827         Inkscape::NodePath::SubPath *sp = node->subpath;
1828         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1829         Inkscape::NodePath::Node *sample_cursor = NULL;
1830         Inkscape::NodePath::Node *sample_end = NULL;
1831         Inkscape::NodePath::Node *delete_cursor = node;
1832         bool just_delete = false;
1833         
1834         //find the start of this contiguous selection
1835         //move left to the first node that is not selected
1836         //or the start of the non-closed path
1837         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1838             delete_cursor = curr;
1839         }
1841         //just delete at the beginning of an open path
1842         if (!delete_cursor->p.other) {
1843             sample_cursor = delete_cursor;
1844             just_delete = true;
1845         } else {
1846             sample_cursor = delete_cursor->p.other;
1847         }
1848         
1849         //calculate points for each segment
1850         int rate = 5;
1851         float period = 1.0 / rate;
1852         std::vector<NR::Point> data;
1853         if (!just_delete) {
1854             data.push_back(sample_cursor->pos);
1855             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1856                 //just delete at the end of an open path
1857                 if (!sp->closed && curr->n.other == sp->last) {
1858                     just_delete = true;
1859                     break;
1860                 }
1861                 
1862                 //sample points on the contiguous selected segment
1863                 NR::Point *bez;
1864                 bez = new NR::Point [4];
1865                 bez[0] = curr->pos;
1866                 bez[1] = curr->n.pos;
1867                 bez[2] = curr->n.other->p.pos;
1868                 bez[3] = curr->n.other->pos;
1869                 for (int i=1; i<rate; i++) {
1870                     gdouble t = i * period;
1871                     NR::Point p = bezier_pt(3, bez, t);
1872                     data.push_back(p);
1873                 }
1874                 data.push_back(curr->n.other->pos);
1876                 sample_end = curr->n.other;
1877                 //break if we've come full circle or hit the end of the selection
1878                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1879                     break;
1880                 }
1881             }
1882         }
1884         if (!just_delete) {
1885             //calculate the best fitting single segment and adjust the endpoints
1886             NR::Point *adata;
1887             adata = new NR::Point [data.size()];
1888             copy(data.begin(), data.end(), adata);
1889             
1890             NR::Point *bez;
1891             bez = new NR::Point [4];
1892             //would decreasing error create a better fitting approximation?
1893             gdouble error = 1.0;
1894             gint ret;
1895             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1897             //adjust endpoints
1898             sample_cursor->n.pos = bez[1];
1899             sample_end->p.pos = bez[2];
1900         }
1901        
1902         //destroy this contiguous selection
1903         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
1904             Inkscape::NodePath::Node *temp = delete_cursor;
1905             if (delete_cursor->n.other == delete_cursor) {
1906                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
1907                 delete_cursor = NULL; 
1908             } else {
1909                 delete_cursor = delete_cursor->n.other;
1910             }
1911             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
1912             sp_nodepath_node_destroy(temp);
1913         }
1915         sp_nodepath_update_handles(nodepath);
1917         if (!g_slist_find(nodepaths, nodepath))
1918             nodepaths = g_slist_prepend (nodepaths, nodepath);
1919     }
1921     for (GSList *i = nodepaths; i; i = i->next) {
1922         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
1923         // different nodepaths will give us one undo event per nodepath
1924         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
1926         // if the entire nodepath is removed, delete the selected object.
1927         if (nodepath->subpaths == NULL ||
1928             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
1929             //at least 2
1930             sp_nodepath_get_node_count(nodepath) < 2) {
1931             SPDocument *document = sp_desktop_document (nodepath->desktop);
1932             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
1933             //delete this nodepath's object, not the entire selection! (though at this time, this
1934             //does not matter)
1935             sp_selection_delete();
1936             sp_document_done (document);
1937         } else {
1938             sp_nodepath_update_repr(nodepath);
1939             sp_nodepath_update_statusbar(nodepath);
1940         }
1941     }
1943     g_slist_free (nodepaths);
1946 /**
1947  * Delete one or more selected nodes.
1948  */
1949 void sp_node_selected_delete()
1951     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1952     if (!nodepath) return;
1953     if (!nodepath->selected) return;
1955     /** \todo fixme: do it the right way */
1956     while (nodepath->selected) {
1957        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1958         sp_nodepath_node_destroy(node);
1959     }
1962     //clean up the nodepath (such as for trivial subpaths)
1963     sp_nodepath_cleanup(nodepath);
1965     sp_nodepath_update_handles(nodepath);
1967     // if the entire nodepath is removed, delete the selected object.
1968     if (nodepath->subpaths == NULL ||
1969         sp_nodepath_get_node_count(nodepath) < 2) {
1970         SPDocument *document = sp_desktop_document (nodepath->desktop);
1971         sp_selection_delete();
1972         sp_document_done (document);
1973         return;
1974     }
1976     sp_nodepath_update_repr(nodepath);
1978     sp_nodepath_update_statusbar(nodepath);
1981 /**
1982  * Delete one or more segments between two selected nodes.
1983  * This is the code for 'split'.
1984  */
1985 void
1986 sp_node_selected_delete_segment(void)
1988    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
1989    Inkscape::NodePath::Node *curr, *next;     //Iterators
1991     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1992     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1994     if (g_list_length(nodepath->selected) != 2) {
1995         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1996                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1997         return;
1998     }
2000     //Selected nodes, not inclusive
2001    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2002    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2004     if ( ( a==b)                       ||  //same node
2005          (a->subpath  != b->subpath )  ||  //not the same path
2006          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2007          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2008     {
2009         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2010                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2011         return;
2012     }
2014     //###########################################
2015     //# BEGIN EDITS
2016     //###########################################
2017     //##################################
2018     //# CLOSED PATH
2019     //##################################
2020     if (a->subpath->closed) {
2023         gboolean reversed = FALSE;
2025         //Since we can go in a circle, we need to find the shorter distance.
2026         //  a->b or b->a
2027         start = end = NULL;
2028         int distance    = 0;
2029         int minDistance = 0;
2030         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2031             if (curr==b) {
2032                 //printf("a to b:%d\n", distance);
2033                 start = a;//go from a to b
2034                 end   = b;
2035                 minDistance = distance;
2036                 //printf("A to B :\n");
2037                 break;
2038             }
2039             distance++;
2040         }
2042         //try again, the other direction
2043         distance = 0;
2044         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2045             if (curr==a) {
2046                 //printf("b to a:%d\n", distance);
2047                 if (distance < minDistance) {
2048                     start    = b;  //we go from b to a
2049                     end      = a;
2050                     reversed = TRUE;
2051                     //printf("B to A\n");
2052                 }
2053                 break;
2054             }
2055             distance++;
2056         }
2059         //Copy everything from 'end' to 'start' to a new subpath
2060        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2061         for (curr=end ; curr ; curr=curr->n.other) {
2062             NRPathcode code = (NRPathcode) curr->code;
2063             if (curr == end)
2064                 code = NR_MOVETO;
2065             sp_nodepath_node_new(t, NULL,
2066                                  (Inkscape::NodePath::NodeType)curr->type, code,
2067                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2068             if (curr == start)
2069                 break;
2070         }
2071         sp_nodepath_subpath_destroy(a->subpath);
2074     }
2078     //##################################
2079     //# OPEN PATH
2080     //##################################
2081     else {
2083         //We need to get the direction of the list between A and B
2084         //Can we walk from a to b?
2085         start = end = NULL;
2086         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2087             if (curr==b) {
2088                 start = a;  //did it!  we go from a to b
2089                 end   = b;
2090                 //printf("A to B\n");
2091                 break;
2092             }
2093         }
2094         if (!start) {//didn't work?  let's try the other direction
2095             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2096                 if (curr==a) {
2097                     start = b;  //did it!  we go from b to a
2098                     end   = a;
2099                     //printf("B to A\n");
2100                     break;
2101                 }
2102             }
2103         }
2104         if (!start) {
2105             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2106                                                      _("Cannot find path between nodes."));
2107             return;
2108         }
2112         //Copy everything after 'end' to a new subpath
2113        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2114         for (curr=end ; curr ; curr=curr->n.other) {
2115             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2116                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2117         }
2119         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2120         for (curr = start->n.other ; curr  ; curr=next) {
2121             next = curr->n.other;
2122             sp_nodepath_node_destroy(curr);
2123         }
2125     }
2126     //###########################################
2127     //# END EDITS
2128     //###########################################
2130     //clean up the nodepath (such as for trivial subpaths)
2131     sp_nodepath_cleanup(nodepath);
2133     sp_nodepath_update_handles(nodepath);
2135     sp_nodepath_update_repr(nodepath);
2137     sp_nodepath_update_statusbar(nodepath);
2140 /**
2141  * Call sp_nodepath_set_line() for all selected segments.
2142  */
2143 void
2144 sp_node_selected_set_line_type(NRPathcode code)
2146     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2147     if (nodepath == NULL) return;
2149     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2150        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2151         g_assert(n->selected);
2152         if (n->p.other && n->p.other->selected) {
2153             sp_nodepath_set_line_type(n, code);
2154         }
2155     }
2157     sp_nodepath_update_repr(nodepath);
2160 /**
2161  * Call sp_nodepath_convert_node_type() for all selected nodes.
2162  */
2163 void
2164 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2166     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2167     if (nodepath == NULL) return;
2169     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2170         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2171     }
2173     sp_nodepath_update_repr(nodepath);
2176 /**
2177  * Change select status of node, update its own and neighbour handles.
2178  */
2179 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2181     node->selected = selected;
2183     if (selected) {
2184         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2185         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2186         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2187         sp_knot_update_ctrl(node->knot);
2188     } else {
2189         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2190         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2191         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2192         sp_knot_update_ctrl(node->knot);
2193     }
2195     sp_node_update_handles(node);
2196     if (node->n.other) sp_node_update_handles(node->n.other);
2197     if (node->p.other) sp_node_update_handles(node->p.other);
2200 /**
2201 \brief Select a node
2202 \param node     The node to select
2203 \param incremental   If true, add to selection, otherwise deselect others
2204 \param override   If true, always select this node, otherwise toggle selected status
2205 */
2206 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2208     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2210     if (incremental) {
2211         if (override) {
2212             if (!g_list_find(nodepath->selected, node)) {
2213                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2214             }
2215             sp_node_set_selected(node, TRUE);
2216         } else { // toggle
2217             if (node->selected) {
2218                 g_assert(g_list_find(nodepath->selected, node));
2219                 nodepath->selected = g_list_remove(nodepath->selected, node);
2220             } else {
2221                 g_assert(!g_list_find(nodepath->selected, node));
2222                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2223             }
2224             sp_node_set_selected(node, !node->selected);
2225         }
2226     } else {
2227         sp_nodepath_deselect(nodepath);
2228         nodepath->selected = g_list_prepend(nodepath->selected, node);
2229         sp_node_set_selected(node, TRUE);
2230     }
2232     sp_nodepath_update_statusbar(nodepath);
2236 /**
2237 \brief Deselect all nodes in the nodepath
2238 */
2239 void
2240 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2242     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2244     while (nodepath->selected) {
2245         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2246         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2247     }
2248     sp_nodepath_update_statusbar(nodepath);
2251 /**
2252 \brief Select or invert selection of all nodes in the nodepath
2253 */
2254 void
2255 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2257     if (!nodepath) return;
2259     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2260        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2261         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2262            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2263            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2264         }
2265     }
2268 /**
2269  * If nothing selected, does the same as sp_nodepath_select_all();
2270  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2271  * (i.e., similar to "select all in layer", with the "selected" subpaths
2272  * being treated as "layers" in the path).
2273  */
2274 void
2275 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2277     if (!nodepath) return;
2279     if (g_list_length (nodepath->selected) == 0) {
2280         sp_nodepath_select_all (nodepath, invert);
2281         return;
2282     }
2284     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2285     GSList *subpaths = NULL;
2287     for (GList *l = copy; l != NULL; l = l->next) {
2288         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2289         Inkscape::NodePath::SubPath *subpath = n->subpath;
2290         if (!g_slist_find (subpaths, subpath))
2291             subpaths = g_slist_prepend (subpaths, subpath);
2292     }
2294     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2295         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2296         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2297             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2298             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2299         }
2300     }
2302     g_slist_free (subpaths);
2303     g_list_free (copy);
2306 /**
2307  * \brief Select the node after the last selected; if none is selected,
2308  * select the first within path.
2309  */
2310 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2312     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2314    Inkscape::NodePath::Node *last = NULL;
2315     if (nodepath->selected) {
2316         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2317            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2318             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2319             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2320                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2321                 if (node->selected) {
2322                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2323                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2324                             if (spl->next) { // there's a next subpath
2325                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2326                                 last = subpath_next->first;
2327                             } else if (spl->prev) { // there's a previous subpath
2328                                 last = NULL; // to be set later to the first node of first subpath
2329                             } else {
2330                                 last = node->n.other;
2331                             }
2332                         } else {
2333                             last = node->n.other;
2334                         }
2335                     } else {
2336                         if (node->n.other) {
2337                             last = node->n.other;
2338                         } else {
2339                             if (spl->next) { // there's a next subpath
2340                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2341                                 last = subpath_next->first;
2342                             } else if (spl->prev) { // there's a previous subpath
2343                                 last = NULL; // to be set later to the first node of first subpath
2344                             } else {
2345                                 last = (Inkscape::NodePath::Node *) subpath->first;
2346                             }
2347                         }
2348                     }
2349                 }
2350             }
2351         }
2352         sp_nodepath_deselect(nodepath);
2353     }
2355     if (last) { // there's at least one more node after selected
2356         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2357     } else { // no more nodes, select the first one in first subpath
2358        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2359         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2360     }
2363 /**
2364  * \brief Select the node before the first selected; if none is selected,
2365  * select the last within path
2366  */
2367 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2369     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2371    Inkscape::NodePath::Node *last = NULL;
2372     if (nodepath->selected) {
2373         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2374            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2375             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2376                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2377                 if (node->selected) {
2378                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2379                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2380                             if (spl->prev) { // there's a prev subpath
2381                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2382                                 last = subpath_prev->last;
2383                             } else if (spl->next) { // there's a next subpath
2384                                 last = NULL; // to be set later to the last node of last subpath
2385                             } else {
2386                                 last = node->p.other;
2387                             }
2388                         } else {
2389                             last = node->p.other;
2390                         }
2391                     } else {
2392                         if (node->p.other) {
2393                             last = node->p.other;
2394                         } else {
2395                             if (spl->prev) { // there's a prev subpath
2396                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2397                                 last = subpath_prev->last;
2398                             } else if (spl->next) { // there's a next subpath
2399                                 last = NULL; // to be set later to the last node of last subpath
2400                             } else {
2401                                 last = (Inkscape::NodePath::Node *) subpath->last;
2402                             }
2403                         }
2404                     }
2405                 }
2406             }
2407         }
2408         sp_nodepath_deselect(nodepath);
2409     }
2411     if (last) { // there's at least one more node before selected
2412         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2413     } else { // no more nodes, select the last one in last subpath
2414         GList *spl = g_list_last(nodepath->subpaths);
2415        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2416         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2417     }
2420 /**
2421  * \brief Select all nodes that are within the rectangle.
2422  */
2423 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2425     if (!incremental) {
2426         sp_nodepath_deselect(nodepath);
2427     }
2429     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2430        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2431         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2432            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2434             if (b.contains(node->pos)) {
2435                 sp_nodepath_node_select(node, TRUE, TRUE);
2436             }
2437         }
2438     }
2442 /**
2443 \brief  Saves all nodes' and handles' current positions in their origin members
2444 */
2445 void
2446 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2448     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2449        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2450         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2451            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2452            n->origin = n->pos;
2453            n->p.origin = n->p.pos;
2454            n->n.origin = n->n.pos;
2455         }
2456     }
2457
2459 /**
2460 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2461 */
2462 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2464     if (!nodepath->selected) {
2465         return NULL;
2466     }
2468     GList *r = NULL;
2469     guint i = 0;
2470     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2471        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2472         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2473            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2474             i++;
2475             if (node->selected) {
2476                 r = g_list_append(r, GINT_TO_POINTER(i));
2477             }
2478         }
2479     }
2480     return r;
2483 /**
2484 \brief  Restores selection by selecting nodes whose positions are in the list
2485 */
2486 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2488     sp_nodepath_deselect(nodepath);
2490     guint i = 0;
2491     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2492        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2493         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2494            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2495             i++;
2496             if (g_list_find(r, GINT_TO_POINTER(i))) {
2497                 sp_nodepath_node_select(node, TRUE, TRUE);
2498             }
2499         }
2500     }
2504 /**
2505 \brief Adjusts handle according to node type and line code.
2506 */
2507 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2509     double len, otherlen, linelen;
2511     g_assert(node);
2513    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2514    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2516     /** \todo fixme: */
2517     if (me->other == NULL) return;
2518     if (other->other == NULL) return;
2520     /* I have line */
2522     NRPathcode mecode, ocode;
2523     if (which_adjust == 1) {
2524         mecode = (NRPathcode)me->other->code;
2525         ocode = (NRPathcode)node->code;
2526     } else {
2527         mecode = (NRPathcode)node->code;
2528         ocode = (NRPathcode)other->other->code;
2529     }
2531     if (mecode == NR_LINETO) return;
2533     /* I am curve */
2535     if (other->other == NULL) return;
2537     /* Other has line */
2539     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2541     NR::Point delta;
2542     if (ocode == NR_LINETO) {
2543         /* other is lineto, we are either smooth or symm */
2544        Inkscape::NodePath::Node *othernode = other->other;
2545         len = NR::L2(me->pos - node->pos);
2546         delta = node->pos - othernode->pos;
2547         linelen = NR::L2(delta);
2548         if (linelen < 1e-18) 
2549             return;
2550         me->pos = node->pos + (len / linelen)*delta;
2551         return;
2552     }
2554     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2556         me->pos = 2 * node->pos - other->pos;
2557         return;
2558     }
2560     /* We are smooth */
2562     len = NR::L2(me->pos - node->pos);
2563     delta = other->pos - node->pos;
2564     otherlen = NR::L2(delta);
2565     if (otherlen < 1e-18) return;
2567     me->pos = node->pos - (len / otherlen) * delta;
2570 /**
2571  \brief Adjusts both handles according to node type and line code
2572  */
2573 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2575     g_assert(node);
2577     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2579     /* we are either smooth or symm */
2581     if (node->p.other == NULL) return;
2583     if (node->n.other == NULL) return;
2585     if (node->code == NR_LINETO) {
2586         if (node->n.other->code == NR_LINETO) return;
2587         sp_node_adjust_handle(node, 1);
2588         return;
2589     }
2591     if (node->n.other->code == NR_LINETO) {
2592         if (node->code == NR_LINETO) return;
2593         sp_node_adjust_handle(node, -1);
2594         return;
2595     }
2597     /* both are curves */
2598     NR::Point const delta( node->n.pos - node->p.pos );
2600     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2601         node->p.pos = node->pos - delta / 2;
2602         node->n.pos = node->pos + delta / 2;
2603         return;
2604     }
2606     /* We are smooth */
2607     double plen = NR::L2(node->p.pos - node->pos);
2608     if (plen < 1e-18) return;
2609     double nlen = NR::L2(node->n.pos - node->pos);
2610     if (nlen < 1e-18) return;
2611     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2612     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2615 /**
2616  * Node event callback.
2617  */
2618 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2620     gboolean ret = FALSE;
2621     switch (event->type) {
2622         case GDK_ENTER_NOTIFY:
2623             active_node = n;
2624             break;
2625         case GDK_LEAVE_NOTIFY:
2626             active_node = NULL;
2627             break;
2628         case GDK_KEY_PRESS:
2629             switch (get_group0_keyval (&event->key)) {
2630                 case GDK_space:
2631                     if (event->key.state & GDK_BUTTON1_MASK) {
2632                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2633                         stamp_repr(nodepath);
2634                         ret = TRUE;
2635                     }
2636                     break;
2637                 default:
2638                     break;
2639             }
2640             break;
2641         default:
2642             break;
2643     }
2645     return ret;
2648 /**
2649  * Handle keypress on node; directly called.
2650  */
2651 gboolean node_key(GdkEvent *event)
2653     Inkscape::NodePath::Path *np;
2655     // there is no way to verify nodes so set active_node to nil when deleting!!
2656     if (active_node == NULL) return FALSE;
2658     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2659         gint ret = FALSE;
2660         switch (get_group0_keyval (&event->key)) {
2661             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2662             case GDK_BackSpace:
2663                 np = active_node->subpath->nodepath;
2664                 sp_nodepath_node_destroy(active_node);
2665                 sp_nodepath_update_repr(np);
2666                 active_node = NULL;
2667                 ret = TRUE;
2668                 break;
2669             case GDK_c:
2670                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2671                 ret = TRUE;
2672                 break;
2673             case GDK_s:
2674                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2675                 ret = TRUE;
2676                 break;
2677             case GDK_y:
2678                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2679                 ret = TRUE;
2680                 break;
2681             case GDK_b:
2682                 sp_nodepath_node_break(active_node);
2683                 ret = TRUE;
2684                 break;
2685         }
2686         return ret;
2687     }
2688     return FALSE;
2691 /**
2692  * Mouseclick on node callback.
2693  */
2694 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2696    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2698     if (state & GDK_CONTROL_MASK) {
2699         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2701         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2702             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2703                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2704             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2705                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2706             } else {
2707                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2708             }
2709             sp_nodepath_update_repr(nodepath);
2710             sp_nodepath_update_statusbar(nodepath);
2712         } else { //ctrl+alt+click: delete node
2713             GList *node_to_delete = NULL;
2714             node_to_delete = g_list_append(node_to_delete, n);
2715             sp_node_delete_preserve(node_to_delete);
2716         }
2718     } else {
2719         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2720     }
2723 /**
2724  * Mouse grabbed node callback.
2725  */
2726 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2728    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2730     if (!n->selected) {
2731         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2732     }
2734     sp_nodepath_remember_origins (n->subpath->nodepath);
2737 /**
2738  * Mouse ungrabbed node callback.
2739  */
2740 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2742    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2744    n->dragging_out = NULL;
2746    sp_nodepath_update_repr(n->subpath->nodepath);
2749 /**
2750  * The point on a line, given by its angle, closest to the given point.
2751  * \param p  A point.
2752  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2753  * \param closest  Pointer to the point struct where the result is stored.
2754  * \todo FIXME: use dot product perhaps?
2755  */
2756 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2758     if (a == HUGE_VAL) { // vertical
2759         *closest = NR::Point(0, (*p)[NR::Y]);
2760     } else {
2761         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2762         (*closest)[NR::Y] = a * (*closest)[NR::X];
2763     }
2766 /**
2767  * Distance from the point to a line given by its angle.
2768  * \param p  A point.
2769  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2770  */
2771 static double point_line_distance(NR::Point *p, double a)
2773     NR::Point c;
2774     point_line_closest(p, a, &c);
2775     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]));
2778 /**
2779  * Callback for node "request" signal.
2780  * \todo fixme: This goes to "moved" event? (lauris)
2781  */
2782 static gboolean
2783 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2785     double yn, xn, yp, xp;
2786     double an, ap, na, pa;
2787     double d_an, d_ap, d_na, d_pa;
2788     gboolean collinear = FALSE;
2789     NR::Point c;
2790     NR::Point pr;
2792    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2794    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2795    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2797        NR::Point mouse = (*p);
2799        if (!n->dragging_out) {
2800            // This is the first drag-out event; find out which handle to drag out
2801            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2802            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2804            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2805                return FALSE;
2807            Inkscape::NodePath::NodeSide *opposite;
2808            if (appr_p > appr_n) { // closer to p
2809                n->dragging_out = &n->p;
2810                opposite = &n->n;
2811                n->code = NR_CURVETO;
2812            } else if (appr_p < appr_n) { // closer to n
2813                n->dragging_out = &n->n;
2814                opposite = &n->p;
2815                n->n.other->code = NR_CURVETO;
2816            } else { // p and n nodes are the same
2817                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2818                    n->dragging_out = &n->p;
2819                    opposite = &n->n;
2820                    n->code = NR_CURVETO;
2821                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2822                    n->dragging_out = &n->n;
2823                    opposite = &n->p;
2824                    n->n.other->code = NR_CURVETO;
2825                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2826                    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);
2827                    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);
2828                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2829                        n->dragging_out = &n->n;
2830                        opposite = &n->p;
2831                        n->n.other->code = NR_CURVETO;
2832                    } else { // closer to other's n handle
2833                        n->dragging_out = &n->p;
2834                        opposite = &n->n;
2835                        n->code = NR_CURVETO;
2836                    }
2837                }
2838            }
2840            // if there's another handle, make sure the one we drag out starts parallel to it
2841            if (opposite->pos != n->pos) {
2842                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2843            }
2845            // knots might not be created yet!
2846            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2847            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2848        }
2850        // pass this on to the handle-moved callback
2851        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2852        sp_node_update_handles(n);
2853        return TRUE;
2854    }
2856     if (state & GDK_CONTROL_MASK) { // constrained motion
2858         // calculate relative distances of handles
2859         // n handle:
2860         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2861         xn = n->n.pos[NR::X] - n->pos[NR::X];
2862         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2863         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2864             if (n->n.other) { // if there is the next point
2865                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2866                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2867                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2868             }
2869         }
2870         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2871         if (yn < 0) { xn = -xn; yn = -yn; }
2873         // p handle:
2874         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2875         xp = n->p.pos[NR::X] - n->pos[NR::X];
2876         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2877         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2878             if (n->p.other) {
2879                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2880                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2881                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2882             }
2883         }
2884         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2885         if (yp < 0) { xp = -xp; yp = -yp; }
2887         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2888             // sliding on handles, only if at least one of the handles is non-vertical
2889             // (otherwise it's the same as ctrl+drag anyway)
2891             // calculate angles of the handles
2892             if (xn == 0) {
2893                 if (yn == 0) { // no handle, consider it the continuation of the other one
2894                     an = 0;
2895                     collinear = TRUE;
2896                 }
2897                 else an = 0; // vertical; set the angle to horizontal
2898             } else an = yn/xn;
2900             if (xp == 0) {
2901                 if (yp == 0) { // no handle, consider it the continuation of the other one
2902                     ap = an;
2903                 }
2904                 else ap = 0; // vertical; set the angle to horizontal
2905             } else  ap = yp/xp;
2907             if (collinear) an = ap;
2909             // angles of the perpendiculars; HUGE_VAL means vertical
2910             if (an == 0) na = HUGE_VAL; else na = -1/an;
2911             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2913             // mouse point relative to the node's original pos
2914             pr = (*p) - n->origin;
2916             // distances to the four lines (two handles and two perpendiculars)
2917             d_an = point_line_distance(&pr, an);
2918             d_na = point_line_distance(&pr, na);
2919             d_ap = point_line_distance(&pr, ap);
2920             d_pa = point_line_distance(&pr, pa);
2922             // find out which line is the closest, save its closest point in c
2923             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2924                 point_line_closest(&pr, an, &c);
2925             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2926                 point_line_closest(&pr, ap, &c);
2927             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2928                 point_line_closest(&pr, na, &c);
2929             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2930                 point_line_closest(&pr, pa, &c);
2931             }
2933             // move the node to the closest point
2934             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2935                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2936                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2938         } else {  // constraining to hor/vert
2940             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2941                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2942             } else { // snap to vert
2943                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2944             }
2945         }
2946     } else { // move freely
2947         if (state & GDK_MOD1_MASK) { // sculpt
2948             sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
2949         } else {
2950             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2951                                         (*p)[NR::X] - n->pos[NR::X],
2952                                         (*p)[NR::Y] - n->pos[NR::Y],
2953                                         (state & GDK_SHIFT_MASK) == 0);
2954         }
2955     }
2957     n->subpath->nodepath->desktop->scroll_to_point(p);
2959     return TRUE;
2962 /**
2963  * Node handle clicked callback.
2964  */
2965 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
2967    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2969     if (state & GDK_CONTROL_MASK) { // "delete" handle
2970         if (n->p.knot == knot) {
2971             n->p.pos = n->pos;
2972         } else if (n->n.knot == knot) {
2973             n->n.pos = n->pos;
2974         }
2975         sp_node_update_handles(n);
2976         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2977         sp_nodepath_update_repr(nodepath);
2978         sp_nodepath_update_statusbar(nodepath);
2980     } else { // just select or add to selection, depending in Shift
2981         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2982     }
2985 /**
2986  * Node handle grabbed callback.
2987  */
2988 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
2990    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2992     if (!n->selected) {
2993         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2994     }
2996     // remember the origin point of the handle
2997     if (n->p.knot == knot) {
2998         n->p.origin_radial = n->p.pos - n->pos;
2999     } else if (n->n.knot == knot) {
3000         n->n.origin_radial = n->n.pos - n->pos;
3001     } else {
3002         g_assert_not_reached();
3003     }
3007 /**
3008  * Node handle ungrabbed callback.
3009  */
3010 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3012    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3014     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3015     if (n->p.knot == knot) {
3016         n->p.origin_radial.a = 0;
3017         sp_knot_set_position(knot, &n->p.pos, state);
3018     } else if (n->n.knot == knot) {
3019         n->n.origin_radial.a = 0;
3020         sp_knot_set_position(knot, &n->n.pos, state);
3021     } else {
3022         g_assert_not_reached();
3023     }
3025     sp_nodepath_update_repr(n->subpath->nodepath);
3028 /**
3029  * Node handle "request" signal callback.
3030  */
3031 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3033     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3035     Inkscape::NodePath::NodeSide *me, *opposite;
3036     gint which;
3037     if (n->p.knot == knot) {
3038         me = &n->p;
3039         opposite = &n->n;
3040         which = -1;
3041     } else if (n->n.knot == knot) {
3042         me = &n->n;
3043         opposite = &n->p;
3044         which = 1;
3045     } else {
3046         me = opposite = NULL;
3047         which = 0;
3048         g_assert_not_reached();
3049     }
3051     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3053     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3055     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3056         /* We are smooth node adjacent with line */
3057         NR::Point const delta = *p - n->pos;
3058         NR::Coord const len = NR::L2(delta);
3059         Inkscape::NodePath::Node *othernode = opposite->other;
3060         NR::Point const ndelta = n->pos - othernode->pos;
3061         NR::Coord const linelen = NR::L2(ndelta);
3062         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3063             NR::Coord const scal = dot(delta, ndelta) / linelen;
3064             (*p) = n->pos + (scal / linelen) * ndelta;
3065         }
3066         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3067     } else {
3068         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3069     }
3071     sp_node_adjust_handle(n, -which);
3073     return FALSE;
3076 /**
3077  * Node handle moved callback.
3078  */
3079 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3081    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3083    Inkscape::NodePath::NodeSide *me;
3084    Inkscape::NodePath::NodeSide *other;
3085     if (n->p.knot == knot) {
3086         me = &n->p;
3087         other = &n->n;
3088     } else if (n->n.knot == knot) {
3089         me = &n->n;
3090         other = &n->p;
3091     } else {
3092         me = NULL;
3093         other = NULL;
3094         g_assert_not_reached();
3095     }
3097     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3098     Radial rme(me->pos - n->pos);
3099     Radial rother(other->pos - n->pos);
3100     Radial rnew(*p - n->pos);
3102     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3103         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3104         /* 0 interpreted as "no snapping". */
3106         // The closest PI/snaps angle, starting from zero.
3107         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3108         if (me->origin_radial.a == HUGE_VAL) {
3109             // ortho doesn't exist: original handle was zero length.
3110             rnew.a = a_snapped;
3111         } else {
3112             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3113              * its opposite and perpendiculars). */
3114             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3116             // Snap to the closest.
3117             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3118                        ? a_snapped
3119                        : a_ortho );
3120         }
3121     }
3123     if (state & GDK_MOD1_MASK) {
3124         // lock handle length
3125         rnew.r = me->origin_radial.r;
3126     }
3128     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3129         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3130         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3131         rother.a += rnew.a - rme.a;
3132         other->pos = NR::Point(rother) + n->pos;
3133         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3134         sp_knot_set_position(other->knot, &other->pos, 0);
3135     }
3137     me->pos = NR::Point(rnew) + n->pos;
3138     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3140     // this is what sp_knot_set_position does, but without emitting the signal:
3141     // we cannot emit a "moved" signal because we're now processing it
3142     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3144     knot->desktop->set_coordinate_status(me->pos);
3146     update_object(n->subpath->nodepath);
3148     /* status text */
3149     SPDesktop *desktop = n->subpath->nodepath->desktop;
3150     if (!desktop) return;
3151     SPEventContext *ec = desktop->event_context;
3152     if (!ec) return;
3153     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3154     if (!mc) return;
3156     double degrees = 180 / M_PI * rnew.a;
3157     if (degrees > 180) degrees -= 360;
3158     if (degrees < -180) degrees += 360;
3159     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3160         degrees = angle_to_compass (degrees);
3162     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3164     mc->setF(Inkscape::NORMAL_MESSAGE,
3165          _("<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);
3167     g_string_free(length, TRUE);
3170 /**
3171  * Node handle event callback.
3172  */
3173 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3175     gboolean ret = FALSE;
3176     switch (event->type) {
3177         case GDK_KEY_PRESS:
3178             switch (get_group0_keyval (&event->key)) {
3179                 case GDK_space:
3180                     if (event->key.state & GDK_BUTTON1_MASK) {
3181                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3182                         stamp_repr(nodepath);
3183                         ret = TRUE;
3184                     }
3185                     break;
3186                 default:
3187                     break;
3188             }
3189             break;
3190         default:
3191             break;
3192     }
3194     return ret;
3197 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3198                                  Radial &rme, Radial &rother, gboolean const both)
3200     rme.a += angle;
3201     if ( both
3202          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3203          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3204     {
3205         rother.a += angle;
3206     }
3209 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3210                                         Radial &rme, Radial &rother, gboolean const both)
3212     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3214     gdouble r;
3215     if ( both
3216          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3217          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3218     {
3219         r = MAX(rme.r, rother.r);
3220     } else {
3221         r = rme.r;
3222     }
3224     gdouble const weird_angle = atan2(norm_angle, r);
3225 /* Bulia says norm_angle is just the visible distance that the
3226  * object's end must travel on the screen.  Left as 'angle' for want of
3227  * a better name.*/
3229     rme.a += weird_angle;
3230     if ( both
3231          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3232          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3233     {
3234         rother.a += weird_angle;
3235     }
3238 /**
3239  * Rotate one node.
3240  */
3241 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3243     Inkscape::NodePath::NodeSide *me, *other;
3244     bool both = false;
3246     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3247     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3249     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3250         me = &(n->p);
3251         other = &(n->n);
3252     } else if (!n->p.other) {
3253         me = &(n->n);
3254         other = &(n->p);
3255     } else {
3256         if (which > 0) { // right handle
3257             if (xn > xp) {
3258                 me = &(n->n);
3259                 other = &(n->p);
3260             } else {
3261                 me = &(n->p);
3262                 other = &(n->n);
3263             }
3264         } else if (which < 0){ // left handle
3265             if (xn <= xp) {
3266                 me = &(n->n);
3267                 other = &(n->p);
3268             } else {
3269                 me = &(n->p);
3270                 other = &(n->n);
3271             }
3272         } else { // both handles
3273             me = &(n->n);
3274             other = &(n->p);
3275             both = true;
3276         }
3277     }
3279     Radial rme(me->pos - n->pos);
3280     Radial rother(other->pos - n->pos);
3282     if (screen) {
3283         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3284     } else {
3285         node_rotate_one_internal (*n, angle, rme, rother, both);
3286     }
3288     me->pos = n->pos + NR::Point(rme);
3290     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3291         other->pos =  n->pos + NR::Point(rother);
3292     }
3294     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3295     // so here we just move all the knots without emitting move signals, for speed
3296     sp_node_update_handles(n, false);
3299 /**
3300  * Rotate selected nodes.
3301  */
3302 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3304     if (!nodepath || !nodepath->selected) return;
3306     if (g_list_length(nodepath->selected) == 1) {
3307        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3308         node_rotate_one (n, angle, which, screen);
3309     } else {
3310        // rotate as an object:
3312         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3313         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3314         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3315             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3316             box.expandTo (n->pos); // contain all selected nodes
3317         }
3319         gdouble rot;
3320         if (screen) {
3321             gdouble const zoom = nodepath->desktop->current_zoom();
3322             gdouble const zmove = angle / zoom;
3323             gdouble const r = NR::L2(box.max() - box.midpoint());
3324             rot = atan2(zmove, r);
3325         } else {
3326             rot = angle;
3327         }
3329         NR::Matrix t =
3330             NR::Matrix (NR::translate(-box.midpoint())) *
3331             NR::Matrix (NR::rotate(rot)) *
3332             NR::Matrix (NR::translate(box.midpoint()));
3334         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3335             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3336             n->pos *= t;
3337             n->n.pos *= t;
3338             n->p.pos *= t;
3339             sp_node_update_handles(n, false);
3340         }
3341     }
3343     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3346 /**
3347  * Scale one node.
3348  */
3349 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3351     bool both = false;
3352     Inkscape::NodePath::NodeSide *me, *other;
3354     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3355     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3357     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3358         me = &(n->p);
3359         other = &(n->n);
3360         n->code = NR_CURVETO;
3361     } else if (!n->p.other) {
3362         me = &(n->n);
3363         other = &(n->p);
3364         if (n->n.other)
3365             n->n.other->code = NR_CURVETO;
3366     } else {
3367         if (which > 0) { // right handle
3368             if (xn > xp) {
3369                 me = &(n->n);
3370                 other = &(n->p);
3371                 if (n->n.other)
3372                     n->n.other->code = NR_CURVETO;
3373             } else {
3374                 me = &(n->p);
3375                 other = &(n->n);
3376                 n->code = NR_CURVETO;
3377             }
3378         } else if (which < 0){ // left handle
3379             if (xn <= xp) {
3380                 me = &(n->n);
3381                 other = &(n->p);
3382                 if (n->n.other)
3383                     n->n.other->code = NR_CURVETO;
3384             } else {
3385                 me = &(n->p);
3386                 other = &(n->n);
3387                 n->code = NR_CURVETO;
3388             }
3389         } else { // both handles
3390             me = &(n->n);
3391             other = &(n->p);
3392             both = true;
3393             n->code = NR_CURVETO;
3394             if (n->n.other)
3395                 n->n.other->code = NR_CURVETO;
3396         }
3397     }
3399     Radial rme(me->pos - n->pos);
3400     Radial rother(other->pos - n->pos);
3402     rme.r += grow;
3403     if (rme.r < 0) rme.r = 0;
3404     if (rme.a == HUGE_VAL) {
3405         if (me->other) { // if direction is unknown, initialize it towards the next node
3406             Radial rme_next(me->other->pos - n->pos);
3407             rme.a = rme_next.a;
3408         } else { // if there's no next, initialize to 0
3409             rme.a = 0;
3410         }
3411     }
3412     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3413         rother.r += grow;
3414         if (rother.r < 0) rother.r = 0;
3415         if (rother.a == HUGE_VAL) {
3416             rother.a = rme.a + M_PI;
3417         }
3418     }
3420     me->pos = n->pos + NR::Point(rme);
3422     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3423         other->pos = n->pos + NR::Point(rother);
3424     }
3426     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3427     // so here we just move all the knots without emitting move signals, for speed
3428     sp_node_update_handles(n, false);
3431 /**
3432  * Scale selected nodes.
3433  */
3434 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3436     if (!nodepath || !nodepath->selected) return;
3438     if (g_list_length(nodepath->selected) == 1) {
3439         // scale handles of the single selected node
3440         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3441         node_scale_one (n, grow, which);
3442     } else {
3443         // scale nodes as an "object":
3445         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3446         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3447         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3448             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3449             box.expandTo (n->pos); // contain all selected nodes
3450         }
3452         double scale = (box.maxExtent() + grow)/box.maxExtent();
3454         NR::Matrix t =
3455             NR::Matrix (NR::translate(-box.midpoint())) *
3456             NR::Matrix (NR::scale(scale, scale)) *
3457             NR::Matrix (NR::translate(box.midpoint()));
3459         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3460             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3461             n->pos *= t;
3462             n->n.pos *= t;
3463             n->p.pos *= t;
3464             sp_node_update_handles(n, false);
3465         }
3466     }
3468     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3471 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3473     if (!nodepath) return;
3474     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3477 /**
3478  * Flip selected nodes horizontally/vertically.
3479  */
3480 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3482     if (!nodepath || !nodepath->selected) return;
3484     if (g_list_length(nodepath->selected) == 1) {
3485         // flip handles of the single selected node
3486         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3487         double temp = n->p.pos[axis];
3488         n->p.pos[axis] = n->n.pos[axis];
3489         n->n.pos[axis] = temp;
3490         sp_node_update_handles(n, false);
3491     } else {
3492         // scale nodes as an "object":
3494         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3495         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3496         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3497             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3498             box.expandTo (n->pos); // contain all selected nodes
3499         }
3501         NR::Matrix t =
3502             NR::Matrix (NR::translate(-box.midpoint())) *
3503             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3504             NR::Matrix (NR::translate(box.midpoint()));
3506         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3507             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3508             n->pos *= t;
3509             n->n.pos *= t;
3510             n->p.pos *= t;
3511             sp_node_update_handles(n, false);
3512         }
3513     }
3515     sp_nodepath_update_repr(nodepath);
3518 //-----------------------------------------------
3519 /**
3520  * Return new subpath under given nodepath.
3521  */
3522 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3524     g_assert(nodepath);
3525     g_assert(nodepath->desktop);
3527    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3529     s->nodepath = nodepath;
3530     s->closed = FALSE;
3531     s->nodes = NULL;
3532     s->first = NULL;
3533     s->last = NULL;
3535     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3536     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3537     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3539     return s;
3542 /**
3543  * Destroy nodes in subpath, then subpath itself.
3544  */
3545 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3547     g_assert(subpath);
3548     g_assert(subpath->nodepath);
3549     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3551     while (subpath->nodes) {
3552         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3553     }
3555     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3557     g_free(subpath);
3560 /**
3561  * Link head to tail in subpath.
3562  */
3563 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3565     g_assert(!sp->closed);
3566     g_assert(sp->last != sp->first);
3567     g_assert(sp->first->code == NR_MOVETO);
3569     sp->closed = TRUE;
3571     //Link the head to the tail
3572     sp->first->p.other = sp->last;
3573     sp->last->n.other  = sp->first;
3574     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3575     sp->first          = sp->last;
3577     //Remove the extra end node
3578     sp_nodepath_node_destroy(sp->last->n.other);
3581 /**
3582  * Open closed (loopy) subpath at node.
3583  */
3584 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3586     g_assert(sp->closed);
3587     g_assert(n->subpath == sp);
3588     g_assert(sp->first == sp->last);
3590     /* We create new startpoint, current node will become last one */
3592    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3593                                                 &n->pos, &n->pos, &n->n.pos);
3596     sp->closed        = FALSE;
3598     //Unlink to make a head and tail
3599     sp->first         = new_path;
3600     sp->last          = n;
3601     n->n.other        = NULL;
3602     new_path->p.other = NULL;
3605 /**
3606  * Returns area in triangle given by points; may be negative.
3607  */
3608 inline double
3609 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3611     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]);
3614 /**
3615  * Return new node in subpath with given properties.
3616  * \param pos Position of node.
3617  * \param ppos Handle position in previous direction
3618  * \param npos Handle position in previous direction
3619  */
3620 Inkscape::NodePath::Node *
3621 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)
3623     g_assert(sp);
3624     g_assert(sp->nodepath);
3625     g_assert(sp->nodepath->desktop);
3627     if (nodechunk == NULL)
3628         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3630     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3632     n->subpath  = sp;
3634     if (type != Inkscape::NodePath::NODE_NONE) {
3635         // use the type from sodipodi:nodetypes
3636         n->type = type;
3637     } else {
3638         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3639             // points are (almost) collinear
3640             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3641                 // endnode, or a node with a retracted handle
3642                 n->type = Inkscape::NodePath::NODE_CUSP;
3643             } else {
3644                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3645             }
3646         } else {
3647             n->type = Inkscape::NodePath::NODE_CUSP;
3648         }
3649     }
3651     n->code     = code;
3652     n->selected = FALSE;
3653     n->pos      = *pos;
3654     n->p.pos    = *ppos;
3655     n->n.pos    = *npos;
3657     n->dragging_out = NULL;
3659     Inkscape::NodePath::Node *prev;
3660     if (next) {
3661         //g_assert(g_list_find(sp->nodes, next));
3662         prev = next->p.other;
3663     } else {
3664         prev = sp->last;
3665     }
3667     if (prev)
3668         prev->n.other = n;
3669     else
3670         sp->first = n;
3672     if (next)
3673         next->p.other = n;
3674     else
3675         sp->last = n;
3677     n->p.other = prev;
3678     n->n.other = next;
3680     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"));
3681     sp_knot_set_position(n->knot, pos, 0);
3683     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3684     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3685     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3686     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3687     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3688     sp_knot_update_ctrl(n->knot);
3690     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3691     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3692     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3693     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3694     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3695     sp_knot_show(n->knot);
3697     // We only create handle knots and lines on demand
3698     n->p.knot = NULL;
3699     n->p.line = NULL;
3700     n->n.knot = NULL;
3701     n->n.line = NULL;
3703     sp->nodes = g_list_prepend(sp->nodes, n);
3705     return n;
3708 /**
3709  * Destroy node and its knots, link neighbors in subpath.
3710  */
3711 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3713     g_assert(node);
3714     g_assert(node->subpath);
3715     g_assert(SP_IS_KNOT(node->knot));
3717    Inkscape::NodePath::SubPath *sp = node->subpath;
3719     if (node->selected) { // first, deselect
3720         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3721         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3722     }
3724     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3726     g_object_unref(G_OBJECT(node->knot));
3727     if (node->p.knot)
3728         g_object_unref(G_OBJECT(node->p.knot));
3729     if (node->n.knot)
3730         g_object_unref(G_OBJECT(node->n.knot));
3732     if (node->p.line)
3733         gtk_object_destroy(GTK_OBJECT(node->p.line));
3734     if (node->n.line)
3735         gtk_object_destroy(GTK_OBJECT(node->n.line));
3737     if (sp->nodes) { // there are others nodes on the subpath
3738         if (sp->closed) {
3739             if (sp->first == node) {
3740                 g_assert(sp->last == node);
3741                 sp->first = node->n.other;
3742                 sp->last = sp->first;
3743             }
3744             node->p.other->n.other = node->n.other;
3745             node->n.other->p.other = node->p.other;
3746         } else {
3747             if (sp->first == node) {
3748                 sp->first = node->n.other;
3749                 sp->first->code = NR_MOVETO;
3750             }
3751             if (sp->last == node) sp->last = node->p.other;
3752             if (node->p.other) node->p.other->n.other = node->n.other;
3753             if (node->n.other) node->n.other->p.other = node->p.other;
3754         }
3755     } else { // this was the last node on subpath
3756         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3757     }
3759     g_mem_chunk_free(nodechunk, node);
3762 /**
3763  * Returns one of the node's two sides.
3764  * \param which Indicates which side.
3765  * \return Pointer to previous node side if which==-1, next if which==1.
3766  */
3767 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3769     g_assert(node);
3771     switch (which) {
3772         case -1:
3773             return &node->p;
3774         case 1:
3775             return &node->n;
3776         default:
3777             break;
3778     }
3780     g_assert_not_reached();
3782     return NULL;
3785 /**
3786  * Return the other side of the node, given one of its sides.
3787  */
3788 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
3790     g_assert(node);
3792     if (me == &node->p) return &node->n;
3793     if (me == &node->n) return &node->p;
3795     g_assert_not_reached();
3797     return NULL;
3800 /**
3801  * Return NRPathcode on the given side of the node.
3802  */
3803 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3805     g_assert(node);
3807     if (me == &node->p) {
3808         if (node->p.other) return (NRPathcode)node->code;
3809         return NR_MOVETO;
3810     }
3812     if (me == &node->n) {
3813         if (node->n.other) return (NRPathcode)node->n.other->code;
3814         return NR_MOVETO;
3815     }
3817     g_assert_not_reached();
3819     return NR_END;
3822 /**
3823  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3824  */
3825 Inkscape::NodePath::Node *
3826 sp_nodepath_get_node_by_index(int index)
3828     Inkscape::NodePath::Node *e = NULL;
3830     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3831     if (!nodepath) {
3832         return e;
3833     }
3835     //find segment
3836     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3838         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3839         int n = g_list_length(sp->nodes);
3840         if (sp->closed) {
3841             n++;
3842         }
3844         //if the piece belongs to this subpath grab it
3845         //otherwise move onto the next subpath
3846         if (index < n) {
3847             e = sp->first;
3848             for (int i = 0; i < index; ++i) {
3849                 e = e->n.other;
3850             }
3851             break;
3852         } else {
3853             if (sp->closed) {
3854                 index -= (n+1);
3855             } else {
3856                 index -= n;
3857             }
3858         }
3859     }
3861     return e;
3864 /**
3865  * Returns plain text meaning of node type.
3866  */
3867 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3869     unsigned retracted = 0;
3870     bool endnode = false;
3872     for (int which = -1; which <= 1; which += 2) {
3873         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3874         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3875             retracted ++;
3876         if (!side->other)
3877             endnode = true;
3878     }
3880     if (retracted == 0) {
3881         if (endnode) {
3882                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3883                 return _("end node");
3884         } else {
3885             switch (node->type) {
3886                 case Inkscape::NodePath::NODE_CUSP:
3887                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3888                     return _("cusp");
3889                 case Inkscape::NodePath::NODE_SMOOTH:
3890                     // TRANSLATORS: "smooth" is an adjective here
3891                     return _("smooth");
3892                 case Inkscape::NodePath::NODE_SYMM:
3893                     return _("symmetric");
3894             }
3895         }
3896     } else if (retracted == 1) {
3897         if (endnode) {
3898             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3899             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3900         } else {
3901             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3902         }
3903     } else {
3904         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3905     }
3907     return NULL;
3910 /**
3911  * Handles content of statusbar as long as node tool is active.
3912  */
3913 void
3914 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3916     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");
3917     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3919     gint total_nodes = sp_nodepath_get_node_count(nodepath);
3920     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
3921     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
3922     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
3924     SPDesktop *desktop = NULL;
3925     if (nodepath) {
3926         desktop = nodepath->desktop;
3927     } else {
3928         desktop = SP_ACTIVE_DESKTOP;
3929     }
3931     SPEventContext *ec = desktop->event_context;
3932     if (!ec) return;
3933     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3934     if (!mc) return;
3936     if (selected_nodes == 0) {
3937         Inkscape::Selection *sel = desktop->selection;
3938         if (!sel || sel->isEmpty()) {
3939             mc->setF(Inkscape::NORMAL_MESSAGE,
3940                      _("Select a single object to edit its nodes or handles."));
3941         } else {
3942             if (nodepath) {
3943             mc->setF(Inkscape::NORMAL_MESSAGE,
3944                      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.",
3945                               "<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.",
3946                               total_nodes),
3947                      total_nodes);
3948             } else {
3949                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3950                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3951                 } else {
3952                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3953                 }
3954             }
3955         }
3956     } else if (nodepath && selected_nodes == 1) {
3957         mc->setF(Inkscape::NORMAL_MESSAGE,
3958                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3959                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3960                           total_nodes),
3961                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3962     } else {
3963         if (selected_subpaths > 1) {
3964             mc->setF(Inkscape::NORMAL_MESSAGE,
3965                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
3966                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
3967                               total_nodes),
3968                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
3969         } else {
3970             mc->setF(Inkscape::NORMAL_MESSAGE,
3971                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
3972                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
3973                               total_nodes),
3974                      selected_nodes, total_nodes, when_selected);
3975         }
3976     }
3980 /*
3981   Local Variables:
3982   mode:c++
3983   c-file-style:"stroustrup"
3984   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3985   indent-tabs-mode:nil
3986   fill-column:99
3987   End:
3988 */
3989 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :