Code

fix undo bug in nodepath_preserve; fix crash caused by nodepath_cleanup deleting...
[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;
275 /**
276  * Clean up a nodepath after editing.
277  *
278  * Currently we are deleting trivial subpaths.
279  */
280 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
282     GList *badSubPaths = NULL;
284     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
285     for (GList *l = nodepath->subpaths; l ; l=l->next) {
286        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
287        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
288             badSubPaths = g_list_append(badSubPaths, sp);
289     }
291     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
292     //also removes the subpath from nodepath->subpaths
293     for (GList *l = badSubPaths; l ; l=l->next) {
294        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
295         sp_nodepath_subpath_destroy(sp);
296     }
298     g_list_free(badSubPaths);
301 /**
302  * Create new nodepath from b, make it subpath of np.
303  * \param t The node type.
304  * \todo Fixme: t should be a proper type, rather than gchar
305  */
306 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
308     NR::Point ppos, pos, npos;
310     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
312     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
313     bool const closed = (b->code == NR_MOVETO);
315     pos = NR::Point(b->x3, b->y3) * np->i2d;
316     if (b[1].code == NR_CURVETO) {
317         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
318     } else {
319         npos = pos;
320     }
321     Inkscape::NodePath::Node *n;
322     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
323     g_assert(sp->first == n);
324     g_assert(sp->last  == n);
326     b++;
327     t++;
328     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
329         pos = NR::Point(b->x3, b->y3) * np->i2d;
330         if (b->code == NR_CURVETO) {
331             ppos = NR::Point(b->x2, b->y2) * np->i2d;
332         } else {
333             ppos = pos;
334         }
335         if (b[1].code == NR_CURVETO) {
336             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
337         } else {
338             npos = pos;
339         }
340         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
341         b++;
342         t++;
343     }
345     if (closed) sp_nodepath_subpath_close(sp);
347     return b;
350 /**
351  * Convert from sodipodi:nodetypes to new style type string.
352  */
353 static gchar *parse_nodetypes(gchar const *types, gint length)
355     g_assert(length > 0);
357     gchar *typestr = g_new(gchar, length + 1);
359     gint pos = 0;
361     if (types) {
362         for (gint i = 0; types[i] && ( i < length ); i++) {
363             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
364             if (types[i] != '\0') {
365                 switch (types[i]) {
366                     case 's':
367                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
368                         break;
369                     case 'z':
370                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
371                         break;
372                     case 'c':
373                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
374                         break;
375                     default:
376                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
377                         break;
378                 }
379             }
380         }
381     }
383     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
385     return typestr;
388 /**
389  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
390  * updated but repr is not (for speed). Used during curve and node drag.
391  */
392 static void update_object(Inkscape::NodePath::Path *np)
394     g_assert(np);
396     SPCurve *curve = create_curve(np);
398     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
400     sp_curve_unref(curve);
403 /**
404  * Update XML path node with data from path object.
405  */
406 static void update_repr_internal(Inkscape::NodePath::Path *np)
408     g_assert(np);
410     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
412     SPCurve *curve = create_curve(np);
413     gchar *typestr = create_typestr(np);
414     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
416     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
417         np->local_change++;
418         repr->setAttribute("d", svgpath);
419     }
421     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
422         np->local_change++;
423         repr->setAttribute("sodipodi:nodetypes", typestr);
424     }
426     g_free(svgpath);
427     g_free(typestr);
428     sp_curve_unref(curve);
431 /**
432  * Update XML path node with data from path object, commit changes forever.
433  */
434 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np)
436     update_repr_internal(np);
437     sp_document_done(sp_desktop_document(np->desktop));
439     if (np->livarot_path) {
440         delete np->livarot_path;
441         np->livarot_path = NULL;
442     }
444     if (np->path && SP_IS_ITEM(np->path)) {
445         np->livarot_path = Path_for_item (np->path, true, true);
446         if (np->livarot_path)
447             np->livarot_path->ConvertWithBackData(0.01);
448     }
451 /**
452  * Update XML path node with data from path object, commit changes with undo.
453  */
454 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key)
456     update_repr_internal(np);
457     sp_document_maybe_done(sp_desktop_document(np->desktop), key);
459     if (np->livarot_path) {
460         delete np->livarot_path;
461         np->livarot_path = NULL;
462     }
464     if (np->path && SP_IS_ITEM(np->path)) {
465         np->livarot_path = Path_for_item (np->path, true, true);
466         if (np->livarot_path)
467             np->livarot_path->ConvertWithBackData(0.01);
468     }
471 /**
472  * Make duplicate of path, replace corresponding XML node in tree, commit.
473  */
474 static void stamp_repr(Inkscape::NodePath::Path *np)
476     g_assert(np);
478     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
479     Inkscape::XML::Node *new_repr = old_repr->duplicate();
481     // remember the position of the item
482     gint pos = old_repr->position();
483     // remember parent
484     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
486     SPCurve *curve = create_curve(np);
487     gchar *typestr = create_typestr(np);
489     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
491     new_repr->setAttribute("d", svgpath);
492     new_repr->setAttribute("sodipodi:nodetypes", typestr);
494     // add the new repr to the parent
495     parent->appendChild(new_repr);
496     // move to the saved position
497     new_repr->setPosition(pos > 0 ? pos : 0);
499     sp_document_done(sp_desktop_document(np->desktop));
501     Inkscape::GC::release(new_repr);
502     g_free(svgpath);
503     g_free(typestr);
504     sp_curve_unref(curve);
507 /**
508  * Create curve from path.
509  */
510 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
512     SPCurve *curve = sp_curve_new();
514     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
515        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
516         sp_curve_moveto(curve,
517                         sp->first->pos * np->d2i);
518        Inkscape::NodePath::Node *n = sp->first->n.other;
519         while (n) {
520             NR::Point const end_pt = n->pos * np->d2i;
521             switch (n->code) {
522                 case NR_LINETO:
523                     sp_curve_lineto(curve, end_pt);
524                     break;
525                 case NR_CURVETO:
526                     sp_curve_curveto(curve,
527                                      n->p.other->n.pos * np->d2i,
528                                      n->p.pos * np->d2i,
529                                      end_pt);
530                     break;
531                 default:
532                     g_assert_not_reached();
533                     break;
534             }
535             if (n != sp->last) {
536                 n = n->n.other;
537             } else {
538                 n = NULL;
539             }
540         }
541         if (sp->closed) {
542             sp_curve_closepath(curve);
543         }
544     }
546     return curve;
549 /**
550  * Convert path type string to sodipodi:nodetypes style.
551  */
552 static gchar *create_typestr(Inkscape::NodePath::Path *np)
554     gchar *typestr = g_new(gchar, 32);
555     gint len = 32;
556     gint pos = 0;
558     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
559        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
561         if (pos >= len) {
562             typestr = g_renew(gchar, typestr, len + 32);
563             len += 32;
564         }
566         typestr[pos++] = 'c';
568        Inkscape::NodePath::Node *n;
569         n = sp->first->n.other;
570         while (n) {
571             gchar code;
573             switch (n->type) {
574                 case Inkscape::NodePath::NODE_CUSP:
575                     code = 'c';
576                     break;
577                 case Inkscape::NodePath::NODE_SMOOTH:
578                     code = 's';
579                     break;
580                 case Inkscape::NodePath::NODE_SYMM:
581                     code = 'z';
582                     break;
583                 default:
584                     g_assert_not_reached();
585                     code = '\0';
586                     break;
587             }
589             if (pos >= len) {
590                 typestr = g_renew(gchar, typestr, len + 32);
591                 len += 32;
592             }
594             typestr[pos++] = code;
596             if (n != sp->last) {
597                 n = n->n.other;
598             } else {
599                 n = NULL;
600             }
601         }
602     }
604     if (pos >= len) {
605         typestr = g_renew(gchar, typestr, len + 1);
606         len += 1;
607     }
609     typestr[pos++] = '\0';
611     return typestr;
614 /**
615  * Returns current path in context.
616  */
617 static Inkscape::NodePath::Path *sp_nodepath_current()
619     if (!SP_ACTIVE_DESKTOP) {
620         return NULL;
621     }
623     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
625     if (!SP_IS_NODE_CONTEXT(event_context)) {
626         return NULL;
627     }
629     return SP_NODE_CONTEXT(event_context)->nodepath;
634 /**
635  \brief Fills node and handle positions for three nodes, splitting line
636   marked by end at distance t.
637  */
638 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
640     g_assert(new_path != NULL);
641     g_assert(end      != NULL);
643     g_assert(end->p.other == new_path);
644    Inkscape::NodePath::Node *start = new_path->p.other;
645     g_assert(start);
647     if (end->code == NR_LINETO) {
648         new_path->type =Inkscape::NodePath::NODE_CUSP;
649         new_path->code = NR_LINETO;
650         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
651     } else {
652         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
653         new_path->code = NR_CURVETO;
654         gdouble s      = 1 - t;
655         for (int dim = 0; dim < 2; dim++) {
656             NR::Coord const f000 = start->pos[dim];
657             NR::Coord const f001 = start->n.pos[dim];
658             NR::Coord const f011 = end->p.pos[dim];
659             NR::Coord const f111 = end->pos[dim];
660             NR::Coord const f00t = s * f000 + t * f001;
661             NR::Coord const f01t = s * f001 + t * f011;
662             NR::Coord const f11t = s * f011 + t * f111;
663             NR::Coord const f0tt = s * f00t + t * f01t;
664             NR::Coord const f1tt = s * f01t + t * f11t;
665             NR::Coord const fttt = s * f0tt + t * f1tt;
666             start->n.pos[dim]    = f00t;
667             new_path->p.pos[dim] = f0tt;
668             new_path->pos[dim]   = fttt;
669             new_path->n.pos[dim] = f1tt;
670             end->p.pos[dim]      = f11t;
671         }
672     }
675 /**
676  * Adds new node on direct line between two nodes, activates handles of all
677  * three nodes.
678  */
679 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
681     g_assert(end);
682     g_assert(end->subpath);
683     g_assert(g_list_find(end->subpath->nodes, end));
685    Inkscape::NodePath::Node *start = end->p.other;
686     g_assert( start->n.other == end );
687    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
688                                                end,
689                                               Inkscape::NodePath::NODE_SMOOTH,
690                                                (NRPathcode)end->code,
691                                                &start->pos, &start->pos, &start->n.pos);
692     sp_nodepath_line_midpoint(newnode, end, t);
694     sp_node_update_handles(start);
695     sp_node_update_handles(newnode);
696     sp_node_update_handles(end);
698     return newnode;
701 /**
702 \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
703 */
704 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
706     g_assert(node);
707     g_assert(node->subpath);
708     g_assert(g_list_find(node->subpath->nodes, node));
710    Inkscape::NodePath::SubPath *sp = node->subpath;
711     Inkscape::NodePath::Path *np    = sp->nodepath;
713     if (sp->closed) {
714         sp_nodepath_subpath_open(sp, node);
715         return sp->first;
716     } else {
717         // no break for end nodes
718         if (node == sp->first) return NULL;
719         if (node == sp->last ) return NULL;
721         // create a new subpath
722        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
724         // duplicate the break node as start of the new subpath
725        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
727         while (node->n.other) { // copy the remaining nodes into the new subpath
728            Inkscape::NodePath::Node *n  = node->n.other;
729            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);
730             if (n->selected) {
731                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
732             }
733             sp_nodepath_node_destroy(n); // remove the point on the original subpath
734         }
736         return newnode;
737     }
740 /**
741  * Duplicate node and connect to neighbours.
742  */
743 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
745     g_assert(node);
746     g_assert(node->subpath);
747     g_assert(g_list_find(node->subpath->nodes, node));
749    Inkscape::NodePath::SubPath *sp = node->subpath;
751     NRPathcode code = (NRPathcode) node->code;
752     if (code == NR_MOVETO) { // if node is the endnode,
753         node->code = NR_LINETO; // new one is inserted before it, so change that to line
754     }
756     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
758     if (!node->n.other || !node->p.other) // if node is an endnode, select it
759         return node;
760     else
761         return newnode; // otherwise select the newly created node
764 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
766     node->p.pos = (node->pos + (node->pos - node->n.pos));
769 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
771     node->n.pos = (node->pos + (node->pos - node->p.pos));
774 /**
775  * Change line type at node, with side effects on neighbours.
776  */
777 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
779     g_assert(end);
780     g_assert(end->subpath);
781     g_assert(end->p.other);
783     if (end->code == static_cast< guint > ( code ) )
784         return;
786    Inkscape::NodePath::Node *start = end->p.other;
788     end->code = code;
790     if (code == NR_LINETO) {
791         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
792         if (end->n.other) {
793             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
794         }
795         sp_node_adjust_handle(start, -1);
796         sp_node_adjust_handle(end, 1);
797     } else {
798         NR::Point delta = end->pos - start->pos;
799         start->n.pos = start->pos + delta / 3;
800         end->p.pos = end->pos - delta / 3;
801         sp_node_adjust_handle(start, 1);
802         sp_node_adjust_handle(end, -1);
803     }
805     sp_node_update_handles(start);
806     sp_node_update_handles(end);
809 /**
810  * Change node type, and its handles accordingly.
811  */
812 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
814     g_assert(node);
815     g_assert(node->subpath);
817     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
818         return node;
820     if ((node->p.other != NULL) && (node->n.other != NULL)) {
821         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
822             type =Inkscape::NodePath::NODE_CUSP;
823         }
824     }
826     node->type = type;
828     if (node->type == Inkscape::NodePath::NODE_CUSP) {
829         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
830         node->knot->setSize (node->selected? 11 : 9);
831         sp_knot_update_ctrl(node->knot);
832     } else {
833         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
834         node->knot->setSize (node->selected? 9 : 7);
835         sp_knot_update_ctrl(node->knot);
836     }
838     // if one of handles is mouseovered, preserve its position
839     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
840         sp_node_adjust_handle(node, 1);
841     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
842         sp_node_adjust_handle(node, -1);
843     } else {
844         sp_node_adjust_handles(node);
845     }
847     sp_node_update_handles(node);
849     sp_nodepath_update_statusbar(node->subpath->nodepath);
851     return node;
854 /**
855  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
856  * adjacent segments from lines to curves.
857 */
858 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
860     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
861         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
862             // convert adjacent segment BEFORE to curve
863             node->code = NR_CURVETO;
864             NR::Point delta;
865             if (node->n.other != NULL)
866                 delta = node->n.other->pos - node->p.other->pos;
867             else
868                 delta = node->pos - node->p.other->pos;
869             node->p.pos = node->pos - delta / 4;
870             sp_node_update_handles(node);
871         }
873         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
874             // convert adjacent segment AFTER to curve
875             node->n.other->code = NR_CURVETO;
876             NR::Point delta;
877             if (node->p.other != NULL)
878                 delta = node->p.other->pos - node->n.other->pos;
879             else
880                 delta = node->pos - node->n.other->pos;
881             node->n.pos = node->pos - delta / 4;
882             sp_node_update_handles(node);
883         }
884     }
886     sp_nodepath_set_node_type (node, type);
889 /**
890  * Move node to point, and adjust its and neighbouring handles.
891  */
892 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
894     NR::Point delta = p - node->pos;
895     node->pos = p;
897     node->p.pos += delta;
898     node->n.pos += delta;
900     if (node->p.other) {
901         if (node->code == NR_LINETO) {
902             sp_node_adjust_handle(node, 1);
903             sp_node_adjust_handle(node->p.other, -1);
904         }
905     }
906     if (node->n.other) {
907         if (node->n.other->code == NR_LINETO) {
908             sp_node_adjust_handle(node, -1);
909             sp_node_adjust_handle(node->n.other, 1);
910         }
911     }
913     // this function is only called from batch movers that will update display at the end
914     // themselves, so here we just move all the knots without emitting move signals, for speed
915     sp_node_update_handles(node, false);
918 /**
919  * Call sp_node_moveto() for node selection and handle possible snapping.
920  */
921 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
922                                             bool const snap = true)
924     NR::Coord best = NR_HUGE;
925     NR::Point delta(dx, dy);
926     NR::Point best_pt = delta;
928     if (snap) {
929         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
930         
931         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
932             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
933             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
934             if (s.getDistance() < best) {
935                 best = s.getDistance();
936                 best_pt = s.getPoint() - n->pos;
937             }
938         }
939     }
941     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
942        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
943         sp_node_moveto(n, n->pos + best_pt);
944     }
946     // do not update repr here so that node dragging is acceptably fast
947     update_object(nodepath);
950 /**
951 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
952 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
953 near x = 0.
954  */
955 double
956 sculpt_profile (double x, double alpha)
958     if (x >= 1)
959         return 0;
960     return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
963 double
964 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
966     // extremely primitive for now, don't have time to look for the real one
967     double lower = NR::L2(b - a);
968     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
969     return (lower + upper)/2;
972 void
973 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
975     n->pos = n->origin + delta;
976     n->n.pos = n->n.origin + delta_n;
977     n->p.pos = n->p.origin + delta_p;
978     sp_node_adjust_handles(n);
979     sp_node_update_handles(n, false);
982 /**
983  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
984  * on how far they are from the dragged node n.
985  */
986 static void 
987 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
989     g_assert (n);
990     g_assert (nodepath);
991     g_assert (n->subpath->nodepath == nodepath);
993     double pressure = n->knot->pressure;
994     if (pressure == 0)
995         pressure = 0.5; // default
996     pressure = CLAMP (pressure, 0.2, 0.8);
998     // map pressure to alpha = 1/5 ... 5
999     double alpha = 1 - 2 * fabs(pressure - 0.5);
1000     if (pressure > 0.5)
1001         alpha = 1/alpha;
1003     double n_sel_range = 0, p_sel_range = 0;
1004     guint n_nodes = 0, p_nodes = 0;
1005     guint n_sel_nodes = 0, p_sel_nodes = 0;
1007     // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1008     {
1009         double n_range = 0, p_range = 0;
1010         bool n_going = true, p_going = true;
1011         Inkscape::NodePath::Node *n_node = n;
1012         Inkscape::NodePath::Node *p_node = n;
1013         do {
1014             // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1015             if (n_node && n_going)
1016                 n_node = n_node->n.other;
1017             if (n_node == NULL) {
1018                 n_going = false;
1019             } else {
1020                 n_nodes ++;
1021                 n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1022                 if (n_node->selected) {
1023                     n_sel_nodes ++;
1024                     n_sel_range = n_range;
1025                 }
1026                 if (n_node == p_node) {
1027                     n_going = false;
1028                     p_going = false;
1029                 }
1030             }
1031             if (p_node && p_going)
1032                 p_node = p_node->p.other;
1033             if (p_node == NULL) {
1034                 p_going = false;
1035             } else {
1036                 p_nodes ++;
1037                 p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1038                 if (p_node->selected) {
1039                     p_sel_nodes ++;
1040                     p_sel_range = p_range;
1041                 }
1042                 if (p_node == n_node) {
1043                     n_going = false;
1044                     p_going = false;
1045                 }
1046             }
1047         } while (n_going || p_going);
1048     }
1050     // Second pass: actually move nodes
1051     sp_nodepath_move_node_and_handles (n, delta, delta, delta);
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_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1065                 if (n_node->selected) {
1066                     sp_nodepath_move_node_and_handles (n_node, 
1067                                       sculpt_profile (n_range / n_sel_range, alpha) * delta,
1068                                       sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha) * delta,
1069                                       sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha) * delta);
1070                 }
1071                 if (n_node == p_node) {
1072                     n_going = false;
1073                     p_going = false;
1074                 }
1075             }
1076             if (p_node && p_going)
1077                 p_node = p_node->p.other;
1078             if (p_node == NULL) {
1079                 p_going = false;
1080             } else {
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                     sp_nodepath_move_node_and_handles (p_node, 
1084                                       sculpt_profile (p_range / p_sel_range, alpha) * delta,
1085                                       sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha) * delta,
1086                                       sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha) * delta);
1087                 }
1088                 if (p_node == n_node) {
1089                     n_going = false;
1090                     p_going = false;
1091                 }
1092             }
1093         } while (n_going || p_going);
1094     }
1096     // do not update repr here so that node dragging is acceptably fast
1097     update_object(nodepath);
1101 /**
1102  * Move node selection to point, adjust its and neighbouring handles,
1103  * handle possible snapping, and commit the change with possible undo.
1104  */
1105 void
1106 sp_node_selected_move(gdouble dx, gdouble dy)
1108     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1109     if (!nodepath) return;
1111     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1113     if (dx == 0) {
1114         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1115     } else if (dy == 0) {
1116         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1117     } else {
1118         sp_nodepath_update_repr(nodepath);
1119     }
1122 /**
1123  * Move node selection off screen and commit the change.
1124  */
1125 void
1126 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1128     // borrowed from sp_selection_move_screen in selection-chemistry.c
1129     // we find out the current zoom factor and divide deltas by it
1130     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1132     gdouble zoom = desktop->current_zoom();
1133     gdouble zdx = dx / zoom;
1134     gdouble zdy = dy / zoom;
1136     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1137     if (!nodepath) return;
1139     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1141     if (dx == 0) {
1142         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1143     } else if (dy == 0) {
1144         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1145     } else {
1146         sp_nodepath_update_repr(nodepath);
1147     }
1150 /** If they don't yet exist, creates knot and line for the given side of the node */
1151 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1153     if (!side->knot) {
1154         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"));
1156         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1157         side->knot->setSize (7);
1158         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1159         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1160         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1161         sp_knot_update_ctrl(side->knot);
1163         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1164         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1165         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1166         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1167         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1168         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1169     }
1171     if (!side->line) {
1172         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1173                                         SP_TYPE_CTRLLINE, NULL);
1174     }
1177 /**
1178  * Ensure the given handle of the node is visible/invisible, update its screen position
1179  */
1180 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1182     g_assert(node != NULL);
1184    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1185     NRPathcode code = sp_node_path_code_from_side(node, side);
1187     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1189     if (show_handle) {
1190         if (!side->knot) { // No handle knot at all
1191             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1192             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1193             side->knot->pos = side->pos;
1194             if (side->knot->item) 
1195                 SP_CTRL(side->knot->item)->moveto(side->pos);
1196             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1197             sp_knot_show(side->knot);
1198         } else {
1199             if (side->knot->pos != side->pos) { // only if it's really moved
1200                 if (fire_move_signals) {
1201                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1202                 } else {
1203                     sp_knot_moveto(side->knot, &side->pos);
1204                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1205                 }
1206             }
1207             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1208                 sp_knot_show(side->knot);
1209             }
1210         }
1211         sp_canvas_item_show(side->line);
1212     } else {
1213         if (side->knot) {
1214             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1215                 sp_knot_hide(side->knot);
1216             }
1217         }
1218         if (side->line) {
1219             sp_canvas_item_hide(side->line);
1220         }
1221     }
1224 /**
1225  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1226  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1227  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1228  * updated; otherwise, just move the knots silently (used in batch moves).
1229  */
1230 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1232     g_assert(node != NULL);
1234     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1235         sp_knot_show(node->knot);
1236     }
1238     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1239         if (fire_move_signals)
1240             sp_knot_set_position(node->knot, &node->pos, 0);
1241         else 
1242             sp_knot_moveto(node->knot, &node->pos);
1243     }
1245     gboolean show_handles = node->selected;
1246     if (node->p.other != NULL) {
1247         if (node->p.other->selected) show_handles = TRUE;
1248     }
1249     if (node->n.other != NULL) {
1250         if (node->n.other->selected) show_handles = TRUE;
1251     }
1253     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1254     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1257 /**
1258  * Call sp_node_update_handles() for all nodes on subpath.
1259  */
1260 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1262     g_assert(subpath != NULL);
1264     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1265         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1266     }
1269 /**
1270  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1271  */
1272 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1274     g_assert(nodepath != NULL);
1276     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1277         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1278     }
1281 /**
1282  * Adds all selected nodes in nodepath to list.
1283  */
1284 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1286     StlConv<Node *>::list(l, selected);
1287 /// \todo this adds a copying, rework when the selection becomes a stl list
1290 /**
1291  * Align selected nodes on the specified axis.
1292  */
1293 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1295     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1296         return;
1297     }
1299     if ( !nodepath->selected->next ) { // only one node selected
1300         return;
1301     }
1302    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1303     NR::Point dest(pNode->pos);
1304     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1305         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1306         if (pNode) {
1307             dest[axis] = pNode->pos[axis];
1308             sp_node_moveto(pNode, dest);
1309         }
1310     }
1312     sp_nodepath_update_repr(nodepath);
1315 /// Helper struct.
1316 struct NodeSort
1318    Inkscape::NodePath::Node *_node;
1319     NR::Coord _coord;
1320     /// \todo use vectorof pointers instead of calling copy ctor
1321     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1322         _node(node), _coord(node->pos[axis])
1323     {}
1325 };
1327 static bool operator<(NodeSort const &a, NodeSort const &b)
1329     return (a._coord < b._coord);
1332 /**
1333  * Distribute selected nodes on the specified axis.
1334  */
1335 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1337     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1338         return;
1339     }
1341     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1342         return;
1343     }
1345    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1346     std::vector<NodeSort> sorted;
1347     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1348         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1349         if (pNode) {
1350             NodeSort n(pNode, axis);
1351             sorted.push_back(n);
1352             //dest[axis] = pNode->pos[axis];
1353             //sp_node_moveto(pNode, dest);
1354         }
1355     }
1356     std::sort(sorted.begin(), sorted.end());
1357     unsigned int len = sorted.size();
1358     //overall bboxes span
1359     float dist = (sorted.back()._coord -
1360                   sorted.front()._coord);
1361     //new distance between each bbox
1362     float step = (dist) / (len - 1);
1363     float pos = sorted.front()._coord;
1364     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1365           it < sorted.end();
1366           it ++ )
1367     {
1368         NR::Point dest((*it)._node->pos);
1369         dest[axis] = pos;
1370         sp_node_moveto((*it)._node, dest);
1371         pos += step;
1372     }
1374     sp_nodepath_update_repr(nodepath);
1378 /**
1379  * Call sp_nodepath_line_add_node() for all selected segments.
1380  */
1381 void
1382 sp_node_selected_add_node(void)
1384     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1385     if (!nodepath) {
1386         return;
1387     }
1389     GList *nl = NULL;
1391     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1392        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1393         g_assert(t->selected);
1394         if (t->p.other && t->p.other->selected) {
1395             nl = g_list_prepend(nl, t);
1396         }
1397     }
1399     while (nl) {
1400        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1401        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1402         sp_nodepath_node_select(n, TRUE, FALSE);
1403         nl = g_list_remove(nl, t);
1404     }
1406     /** \todo fixme: adjust ? */
1407     sp_nodepath_update_handles(nodepath);
1409     sp_nodepath_update_repr(nodepath);
1411     sp_nodepath_update_statusbar(nodepath);
1414 /**
1415  * Select segment nearest to point
1416  */
1417 void
1418 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1420     if (!nodepath) {
1421         return;
1422     }
1424     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1426     //find segment to segment
1427     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1429     gboolean force = FALSE;
1430     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1431         force = TRUE;
1432     }
1433     sp_nodepath_node_select(e, (gboolean) toggle, force);
1434     if (e->p.other)
1435         sp_nodepath_node_select(e->p.other, TRUE, force);
1437     sp_nodepath_update_handles(nodepath);
1439     sp_nodepath_update_statusbar(nodepath);
1442 /**
1443  * Add a node nearest to point
1444  */
1445 void
1446 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1448     if (!nodepath) {
1449         return;
1450     }
1452     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1454     //find segment to split
1455     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1457     //don't know why but t seems to flip for lines
1458     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1459         position.t = 1.0 - position.t;
1460     }
1461     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1462     sp_nodepath_node_select(n, FALSE, TRUE);
1464     /* fixme: adjust ? */
1465     sp_nodepath_update_handles(nodepath);
1467     sp_nodepath_update_repr(nodepath);
1469     sp_nodepath_update_statusbar(nodepath);
1472 /*
1473  * Adjusts a segment so that t moves by a certain delta for dragging
1474  * converts lines to curves
1475  *
1476  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1477  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1478  */
1479 void
1480 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1482     /* feel good is an arbitrary parameter that distributes the delta between handles
1483      * if t of the drag point is less than 1/6 distance form the endpoint only
1484      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1485      */
1486     double feel_good;
1487     if (t <= 1.0 / 6.0)
1488         feel_good = 0;
1489     else if (t <= 0.5)
1490         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1491     else if (t <= 5.0 / 6.0)
1492         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1493     else
1494         feel_good = 1;
1496     //if we're dragging a line convert it to a curve
1497     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1498         sp_nodepath_set_line_type(e, NR_CURVETO);
1499     }
1501     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1502     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1503     e->p.other->n.pos += offsetcoord0;
1504     e->p.pos += offsetcoord1;
1506     // adjust handles of adjacent nodes where necessary
1507     sp_node_adjust_handle(e,1);
1508     sp_node_adjust_handle(e->p.other,-1);
1510     sp_nodepath_update_handles(e->subpath->nodepath);
1512     update_object(e->subpath->nodepath);
1514     sp_nodepath_update_statusbar(e->subpath->nodepath);
1518 /**
1519  * Call sp_nodepath_break() for all selected segments.
1520  */
1521 void sp_node_selected_break()
1523     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1524     if (!nodepath) return;
1526     GList *temp = NULL;
1527     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1528        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1529        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1530         if (nn == NULL) continue; // no break, no new node
1531         temp = g_list_prepend(temp, nn);
1532     }
1534     if (temp) {
1535         sp_nodepath_deselect(nodepath);
1536     }
1537     for (GList *l = temp; l != NULL; l = l->next) {
1538         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1539     }
1541     sp_nodepath_update_handles(nodepath);
1543     sp_nodepath_update_repr(nodepath);
1546 /**
1547  * Duplicate the selected node(s).
1548  */
1549 void sp_node_selected_duplicate()
1551     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1552     if (!nodepath) {
1553         return;
1554     }
1556     GList *temp = NULL;
1557     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1558        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1559        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1560         if (nn == NULL) continue; // could not duplicate
1561         temp = g_list_prepend(temp, nn);
1562     }
1564     if (temp) {
1565         sp_nodepath_deselect(nodepath);
1566     }
1567     for (GList *l = temp; l != NULL; l = l->next) {
1568         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1569     }
1571     sp_nodepath_update_handles(nodepath);
1573     sp_nodepath_update_repr(nodepath);
1576 /**
1577  *  Join two nodes by merging them into one.
1578  */
1579 void sp_node_selected_join()
1581     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1582     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1584     if (g_list_length(nodepath->selected) != 2) {
1585         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1586         return;
1587     }
1589    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1590    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1592     g_assert(a != b);
1593     g_assert(a->p.other || a->n.other);
1594     g_assert(b->p.other || b->n.other);
1596     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1597         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1598         return;
1599     }
1601     /* a and b are endpoints */
1603     NR::Point c;
1604     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1605         c = a->pos;
1606     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1607         c = b->pos;
1608     } else {
1609         c = (a->pos + b->pos) / 2;
1610     }
1612     if (a->subpath == b->subpath) {
1613        Inkscape::NodePath::SubPath *sp = a->subpath;
1614         sp_nodepath_subpath_close(sp);
1615         sp_node_moveto (sp->first, c);
1617         sp_nodepath_update_handles(sp->nodepath);
1618         sp_nodepath_update_repr(nodepath);
1619         return;
1620     }
1622     /* a and b are separate subpaths */
1623    Inkscape::NodePath::SubPath *sa = a->subpath;
1624    Inkscape::NodePath::SubPath *sb = b->subpath;
1625     NR::Point p;
1626    Inkscape::NodePath::Node *n;
1627     NRPathcode code;
1628     if (a == sa->first) {
1629         p = sa->first->n.pos;
1630         code = (NRPathcode)sa->first->n.other->code;
1631        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1632         n = sa->last;
1633         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1634         n = n->p.other;
1635         while (n) {
1636             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1637             n = n->p.other;
1638             if (n == sa->first) n = NULL;
1639         }
1640         sp_nodepath_subpath_destroy(sa);
1641         sa = t;
1642     } else if (a == sa->last) {
1643         p = sa->last->p.pos;
1644         code = (NRPathcode)sa->last->code;
1645         sp_nodepath_node_destroy(sa->last);
1646     } else {
1647         code = NR_END;
1648         g_assert_not_reached();
1649     }
1651     if (b == sb->first) {
1652         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1653         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1654             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1655         }
1656     } else if (b == sb->last) {
1657         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1658         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1659             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1660         }
1661     } else {
1662         g_assert_not_reached();
1663     }
1664     /* and now destroy sb */
1666     sp_nodepath_subpath_destroy(sb);
1668     sp_nodepath_update_handles(sa->nodepath);
1670     sp_nodepath_update_repr(nodepath);
1672     sp_nodepath_update_statusbar(nodepath);
1675 /**
1676  *  Join two nodes by adding a segment between them.
1677  */
1678 void sp_node_selected_join_segment()
1680     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1681     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1683     if (g_list_length(nodepath->selected) != 2) {
1684         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1685         return;
1686     }
1688    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1689    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1691     g_assert(a != b);
1692     g_assert(a->p.other || a->n.other);
1693     g_assert(b->p.other || b->n.other);
1695     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1696         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1697         return;
1698     }
1700     if (a->subpath == b->subpath) {
1701        Inkscape::NodePath::SubPath *sp = a->subpath;
1703         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1704         sp->closed = TRUE;
1706         sp->first->p.other = sp->last;
1707         sp->last->n.other  = sp->first;
1709         sp_node_handle_mirror_p_to_n(sp->last);
1710         sp_node_handle_mirror_n_to_p(sp->first);
1712         sp->first->code = sp->last->code;
1713         sp->first       = sp->last;
1715         sp_nodepath_update_handles(sp->nodepath);
1717         sp_nodepath_update_repr(nodepath);
1719         return;
1720     }
1722     /* a and b are separate subpaths */
1723    Inkscape::NodePath::SubPath *sa = a->subpath;
1724    Inkscape::NodePath::SubPath *sb = b->subpath;
1726    Inkscape::NodePath::Node *n;
1727     NR::Point p;
1728     NRPathcode code;
1729     if (a == sa->first) {
1730         code = (NRPathcode) sa->first->n.other->code;
1731        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1732         n = sa->last;
1733         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1734         for (n = n->p.other; n != NULL; n = n->p.other) {
1735             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1736         }
1737         sp_nodepath_subpath_destroy(sa);
1738         sa = t;
1739     } else if (a == sa->last) {
1740         code = (NRPathcode)sa->last->code;
1741     } else {
1742         code = NR_END;
1743         g_assert_not_reached();
1744     }
1746     if (b == sb->first) {
1747         n = sb->first;
1748         sp_node_handle_mirror_p_to_n(sa->last);
1749         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1750         sp_node_handle_mirror_n_to_p(sa->last);
1751         for (n = n->n.other; n != NULL; n = n->n.other) {
1752             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1753         }
1754     } else if (b == sb->last) {
1755         n = sb->last;
1756         sp_node_handle_mirror_p_to_n(sa->last);
1757         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1758         sp_node_handle_mirror_n_to_p(sa->last);
1759         for (n = n->p.other; n != NULL; n = n->p.other) {
1760             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1761         }
1762     } else {
1763         g_assert_not_reached();
1764     }
1765     /* and now destroy sb */
1767     sp_nodepath_subpath_destroy(sb);
1769     sp_nodepath_update_handles(sa->nodepath);
1771     sp_nodepath_update_repr(nodepath);
1774 /**
1775  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1776  */
1777 void sp_node_delete_preserve(GList *nodes_to_delete)
1779     GSList *nodepaths = NULL;
1780     
1781     while (nodes_to_delete) {
1782         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1783         Inkscape::NodePath::SubPath *sp = node->subpath;
1784         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1785         Inkscape::NodePath::Node *sample_cursor = NULL;
1786         Inkscape::NodePath::Node *sample_end = NULL;
1787         Inkscape::NodePath::Node *delete_cursor = node;
1788         bool just_delete = false;
1789         
1790         //find the start of this contiguous selection
1791         //move left to the first node that is not selected
1792         //or the start of the non-closed path
1793         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1794             delete_cursor = curr;
1795         }
1797         //just delete at the beginning of an open path
1798         if (!delete_cursor->p.other) {
1799             sample_cursor = delete_cursor;
1800             just_delete = true;
1801         } else {
1802             sample_cursor = delete_cursor->p.other;
1803         }
1804         
1805         //calculate points for each segment
1806         int rate = 5;
1807         float period = 1.0 / rate;
1808         std::vector<NR::Point> data;
1809         if (!just_delete) {
1810             data.push_back(sample_cursor->pos);
1811             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1812                 //just delete at the end of an open path
1813                 if (!sp->closed && curr->n.other == sp->last) {
1814                     just_delete = true;
1815                     break;
1816                 }
1817                 
1818                 //sample points on the contiguous selected segment
1819                 NR::Point *bez;
1820                 bez = new NR::Point [4];
1821                 bez[0] = curr->pos;
1822                 bez[1] = curr->n.pos;
1823                 bez[2] = curr->n.other->p.pos;
1824                 bez[3] = curr->n.other->pos;
1825                 for (int i=1; i<rate; i++) {
1826                     gdouble t = i * period;
1827                     NR::Point p = bezier_pt(3, bez, t);
1828                     data.push_back(p);
1829                 }
1830                 data.push_back(curr->n.other->pos);
1832                 sample_end = curr->n.other;
1833                 //break if we've come full circle or hit the end of the selection
1834                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1835                     break;
1836                 }
1837             }
1838         }
1840         if (!just_delete) {
1841             //calculate the best fitting single segment and adjust the endpoints
1842             NR::Point *adata;
1843             adata = new NR::Point [data.size()];
1844             copy(data.begin(), data.end(), adata);
1845             
1846             NR::Point *bez;
1847             bez = new NR::Point [4];
1848             //would decreasing error create a better fitting approximation?
1849             gdouble error = 1.0;
1850             gint ret;
1851             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1853             //adjust endpoints
1854             sample_cursor->n.pos = bez[1];
1855             sample_end->p.pos = bez[2];
1856         }
1857        
1858         //destroy this contiguous selection
1859         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
1860             Inkscape::NodePath::Node *temp = delete_cursor;
1861             if (delete_cursor->n.other == delete_cursor) {
1862                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
1863                 delete_cursor = NULL; 
1864             } else {
1865                 delete_cursor = delete_cursor->n.other;
1866             }
1867             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
1868             sp_nodepath_node_destroy(temp);
1869         }
1871         sp_nodepath_update_handles(nodepath);
1873         // if the entire nodepath is removed, delete the selected object.
1874         if (nodepath->subpaths == NULL ||
1875             sp_nodepath_get_node_count(nodepath) < 2) {
1876             SPDocument *document = sp_desktop_document (nodepath->desktop);
1877             sp_nodepath_destroy(nodepath);
1878             g_list_free(nodes_to_delete);
1879             nodes_to_delete = NULL;
1880             //is the next line necessary?
1881             sp_selection_delete();
1882             sp_document_done (document);
1883             return;
1884         }
1886         if (!g_slist_find(nodepaths, nodepath))
1887             nodepaths = g_slist_prepend (nodepaths, nodepath);
1888     }
1890     for (GSList *i = nodepaths; i; i = i->next) {
1891         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
1892         sp_nodepath_update_repr(nodepath);
1893         sp_nodepath_update_statusbar(nodepath);
1894     }
1897 /**
1898  * Delete one or more selected nodes.
1899  */
1900 void sp_node_selected_delete()
1902     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1903     if (!nodepath) return;
1904     if (!nodepath->selected) return;
1906     /** \todo fixme: do it the right way */
1907     while (nodepath->selected) {
1908        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1909         sp_nodepath_node_destroy(node);
1910     }
1913     //clean up the nodepath (such as for trivial subpaths)
1914     sp_nodepath_cleanup(nodepath);
1916     sp_nodepath_update_handles(nodepath);
1918     // if the entire nodepath is removed, delete the selected object.
1919     if (nodepath->subpaths == NULL ||
1920         sp_nodepath_get_node_count(nodepath) < 2) {
1921         SPDocument *document = sp_desktop_document (nodepath->desktop);
1922         sp_nodepath_destroy(nodepath);
1923         sp_selection_delete();
1924         sp_document_done (document);
1925         return;
1926     }
1928     sp_nodepath_update_repr(nodepath);
1930     sp_nodepath_update_statusbar(nodepath);
1933 /**
1934  * Delete one or more segments between two selected nodes.
1935  * This is the code for 'split'.
1936  */
1937 void
1938 sp_node_selected_delete_segment(void)
1940    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
1941    Inkscape::NodePath::Node *curr, *next;     //Iterators
1943     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1944     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1946     if (g_list_length(nodepath->selected) != 2) {
1947         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1948                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1949         return;
1950     }
1952     //Selected nodes, not inclusive
1953    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1954    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1956     if ( ( a==b)                       ||  //same node
1957          (a->subpath  != b->subpath )  ||  //not the same path
1958          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
1959          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
1960     {
1961         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1962                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1963         return;
1964     }
1966     //###########################################
1967     //# BEGIN EDITS
1968     //###########################################
1969     //##################################
1970     //# CLOSED PATH
1971     //##################################
1972     if (a->subpath->closed) {
1975         gboolean reversed = FALSE;
1977         //Since we can go in a circle, we need to find the shorter distance.
1978         //  a->b or b->a
1979         start = end = NULL;
1980         int distance    = 0;
1981         int minDistance = 0;
1982         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1983             if (curr==b) {
1984                 //printf("a to b:%d\n", distance);
1985                 start = a;//go from a to b
1986                 end   = b;
1987                 minDistance = distance;
1988                 //printf("A to B :\n");
1989                 break;
1990             }
1991             distance++;
1992         }
1994         //try again, the other direction
1995         distance = 0;
1996         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1997             if (curr==a) {
1998                 //printf("b to a:%d\n", distance);
1999                 if (distance < minDistance) {
2000                     start    = b;  //we go from b to a
2001                     end      = a;
2002                     reversed = TRUE;
2003                     //printf("B to A\n");
2004                 }
2005                 break;
2006             }
2007             distance++;
2008         }
2011         //Copy everything from 'end' to 'start' to a new subpath
2012        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2013         for (curr=end ; curr ; curr=curr->n.other) {
2014             NRPathcode code = (NRPathcode) curr->code;
2015             if (curr == end)
2016                 code = NR_MOVETO;
2017             sp_nodepath_node_new(t, NULL,
2018                                  (Inkscape::NodePath::NodeType)curr->type, code,
2019                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2020             if (curr == start)
2021                 break;
2022         }
2023         sp_nodepath_subpath_destroy(a->subpath);
2026     }
2030     //##################################
2031     //# OPEN PATH
2032     //##################################
2033     else {
2035         //We need to get the direction of the list between A and B
2036         //Can we walk from a to b?
2037         start = end = NULL;
2038         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2039             if (curr==b) {
2040                 start = a;  //did it!  we go from a to b
2041                 end   = b;
2042                 //printf("A to B\n");
2043                 break;
2044             }
2045         }
2046         if (!start) {//didn't work?  let's try the other direction
2047             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2048                 if (curr==a) {
2049                     start = b;  //did it!  we go from b to a
2050                     end   = a;
2051                     //printf("B to A\n");
2052                     break;
2053                 }
2054             }
2055         }
2056         if (!start) {
2057             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2058                                                      _("Cannot find path between nodes."));
2059             return;
2060         }
2064         //Copy everything after 'end' to a new subpath
2065        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2066         for (curr=end ; curr ; curr=curr->n.other) {
2067             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2068                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2069         }
2071         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2072         for (curr = start->n.other ; curr  ; curr=next) {
2073             next = curr->n.other;
2074             sp_nodepath_node_destroy(curr);
2075         }
2077     }
2078     //###########################################
2079     //# END EDITS
2080     //###########################################
2082     //clean up the nodepath (such as for trivial subpaths)
2083     sp_nodepath_cleanup(nodepath);
2085     sp_nodepath_update_handles(nodepath);
2087     sp_nodepath_update_repr(nodepath);
2089     // if the entire nodepath is removed, delete the selected object.
2090     if (nodepath->subpaths == NULL ||
2091         sp_nodepath_get_node_count(nodepath) < 2) {
2092         sp_nodepath_destroy(nodepath);
2093         sp_selection_delete();
2094         return;
2095     }
2097     sp_nodepath_update_statusbar(nodepath);
2100 /**
2101  * Call sp_nodepath_set_line() for all selected segments.
2102  */
2103 void
2104 sp_node_selected_set_line_type(NRPathcode code)
2106     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2107     if (nodepath == NULL) return;
2109     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2110        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2111         g_assert(n->selected);
2112         if (n->p.other && n->p.other->selected) {
2113             sp_nodepath_set_line_type(n, code);
2114         }
2115     }
2117     sp_nodepath_update_repr(nodepath);
2120 /**
2121  * Call sp_nodepath_convert_node_type() for all selected nodes.
2122  */
2123 void
2124 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2126     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2127     if (nodepath == NULL) return;
2129     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2130         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2131     }
2133     sp_nodepath_update_repr(nodepath);
2136 /**
2137  * Change select status of node, update its own and neighbour handles.
2138  */
2139 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2141     node->selected = selected;
2143     if (selected) {
2144         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2145         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2146         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2147         sp_knot_update_ctrl(node->knot);
2148     } else {
2149         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2150         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2151         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2152         sp_knot_update_ctrl(node->knot);
2153     }
2155     sp_node_update_handles(node);
2156     if (node->n.other) sp_node_update_handles(node->n.other);
2157     if (node->p.other) sp_node_update_handles(node->p.other);
2160 /**
2161 \brief Select a node
2162 \param node     The node to select
2163 \param incremental   If true, add to selection, otherwise deselect others
2164 \param override   If true, always select this node, otherwise toggle selected status
2165 */
2166 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2168     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2170     if (incremental) {
2171         if (override) {
2172             if (!g_list_find(nodepath->selected, node)) {
2173                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2174             }
2175             sp_node_set_selected(node, TRUE);
2176         } else { // toggle
2177             if (node->selected) {
2178                 g_assert(g_list_find(nodepath->selected, node));
2179                 nodepath->selected = g_list_remove(nodepath->selected, node);
2180             } else {
2181                 g_assert(!g_list_find(nodepath->selected, node));
2182                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2183             }
2184             sp_node_set_selected(node, !node->selected);
2185         }
2186     } else {
2187         sp_nodepath_deselect(nodepath);
2188         nodepath->selected = g_list_prepend(nodepath->selected, node);
2189         sp_node_set_selected(node, TRUE);
2190     }
2192     sp_nodepath_update_statusbar(nodepath);
2196 /**
2197 \brief Deselect all nodes in the nodepath
2198 */
2199 void
2200 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2202     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2204     while (nodepath->selected) {
2205         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2206         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2207     }
2208     sp_nodepath_update_statusbar(nodepath);
2211 /**
2212 \brief Select or invert selection of all nodes in the nodepath
2213 */
2214 void
2215 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2217     if (!nodepath) return;
2219     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2220        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2221         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2222            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2223            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2224         }
2225     }
2228 /**
2229  * If nothing selected, does the same as sp_nodepath_select_all();
2230  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2231  * (i.e., similar to "select all in layer", with the "selected" subpaths
2232  * being treated as "layers" in the path).
2233  */
2234 void
2235 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2237     if (!nodepath) return;
2239     if (g_list_length (nodepath->selected) == 0) {
2240         sp_nodepath_select_all (nodepath, invert);
2241         return;
2242     }
2244     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2245     GSList *subpaths = NULL;
2247     for (GList *l = copy; l != NULL; l = l->next) {
2248         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2249         Inkscape::NodePath::SubPath *subpath = n->subpath;
2250         if (!g_slist_find (subpaths, subpath))
2251             subpaths = g_slist_prepend (subpaths, subpath);
2252     }
2254     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2255         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2256         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2257             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2258             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2259         }
2260     }
2262     g_slist_free (subpaths);
2263     g_list_free (copy);
2266 /**
2267  * \brief Select the node after the last selected; if none is selected,
2268  * select the first within path.
2269  */
2270 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2272     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2274    Inkscape::NodePath::Node *last = NULL;
2275     if (nodepath->selected) {
2276         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2277            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2278             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2279             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2280                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2281                 if (node->selected) {
2282                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2283                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2284                             if (spl->next) { // there's a next subpath
2285                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2286                                 last = subpath_next->first;
2287                             } else if (spl->prev) { // there's a previous subpath
2288                                 last = NULL; // to be set later to the first node of first subpath
2289                             } else {
2290                                 last = node->n.other;
2291                             }
2292                         } else {
2293                             last = node->n.other;
2294                         }
2295                     } else {
2296                         if (node->n.other) {
2297                             last = node->n.other;
2298                         } else {
2299                             if (spl->next) { // there's a next subpath
2300                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2301                                 last = subpath_next->first;
2302                             } else if (spl->prev) { // there's a previous subpath
2303                                 last = NULL; // to be set later to the first node of first subpath
2304                             } else {
2305                                 last = (Inkscape::NodePath::Node *) subpath->first;
2306                             }
2307                         }
2308                     }
2309                 }
2310             }
2311         }
2312         sp_nodepath_deselect(nodepath);
2313     }
2315     if (last) { // there's at least one more node after selected
2316         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2317     } else { // no more nodes, select the first one in first subpath
2318        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2319         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2320     }
2323 /**
2324  * \brief Select the node before the first selected; if none is selected,
2325  * select the last within path
2326  */
2327 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2329     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2331    Inkscape::NodePath::Node *last = NULL;
2332     if (nodepath->selected) {
2333         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2334            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2335             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2336                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2337                 if (node->selected) {
2338                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2339                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2340                             if (spl->prev) { // there's a prev subpath
2341                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2342                                 last = subpath_prev->last;
2343                             } else if (spl->next) { // there's a next subpath
2344                                 last = NULL; // to be set later to the last node of last subpath
2345                             } else {
2346                                 last = node->p.other;
2347                             }
2348                         } else {
2349                             last = node->p.other;
2350                         }
2351                     } else {
2352                         if (node->p.other) {
2353                             last = node->p.other;
2354                         } else {
2355                             if (spl->prev) { // there's a prev subpath
2356                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2357                                 last = subpath_prev->last;
2358                             } else if (spl->next) { // there's a next subpath
2359                                 last = NULL; // to be set later to the last node of last subpath
2360                             } else {
2361                                 last = (Inkscape::NodePath::Node *) subpath->last;
2362                             }
2363                         }
2364                     }
2365                 }
2366             }
2367         }
2368         sp_nodepath_deselect(nodepath);
2369     }
2371     if (last) { // there's at least one more node before selected
2372         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2373     } else { // no more nodes, select the last one in last subpath
2374         GList *spl = g_list_last(nodepath->subpaths);
2375        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2376         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2377     }
2380 /**
2381  * \brief Select all nodes that are within the rectangle.
2382  */
2383 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2385     if (!incremental) {
2386         sp_nodepath_deselect(nodepath);
2387     }
2389     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2390        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2391         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2392            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2394             if (b.contains(node->pos)) {
2395                 sp_nodepath_node_select(node, TRUE, TRUE);
2396             }
2397         }
2398     }
2402 /**
2403 \brief  Saves all nodes' and handles' current positions in their origin members
2404 */
2405 void
2406 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2408     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2409        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2410         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2411            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2412            n->origin = n->pos;
2413            n->p.origin = n->p.pos;
2414            n->n.origin = n->n.pos;
2415         }
2416     }
2417
2419 /**
2420 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2421 */
2422 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2424     if (!nodepath->selected) {
2425         return NULL;
2426     }
2428     GList *r = NULL;
2429     guint i = 0;
2430     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2431        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2432         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2433            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2434             i++;
2435             if (node->selected) {
2436                 r = g_list_append(r, GINT_TO_POINTER(i));
2437             }
2438         }
2439     }
2440     return r;
2443 /**
2444 \brief  Restores selection by selecting nodes whose positions are in the list
2445 */
2446 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2448     sp_nodepath_deselect(nodepath);
2450     guint i = 0;
2451     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2452        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2453         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2454            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2455             i++;
2456             if (g_list_find(r, GINT_TO_POINTER(i))) {
2457                 sp_nodepath_node_select(node, TRUE, TRUE);
2458             }
2459         }
2460     }
2464 /**
2465 \brief Adjusts handle according to node type and line code.
2466 */
2467 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2469     double len, otherlen, linelen;
2471     g_assert(node);
2473    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2474    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2476     /** \todo fixme: */
2477     if (me->other == NULL) return;
2478     if (other->other == NULL) return;
2480     /* I have line */
2482     NRPathcode mecode, ocode;
2483     if (which_adjust == 1) {
2484         mecode = (NRPathcode)me->other->code;
2485         ocode = (NRPathcode)node->code;
2486     } else {
2487         mecode = (NRPathcode)node->code;
2488         ocode = (NRPathcode)other->other->code;
2489     }
2491     if (mecode == NR_LINETO) return;
2493     /* I am curve */
2495     if (other->other == NULL) return;
2497     /* Other has line */
2499     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2501     NR::Point delta;
2502     if (ocode == NR_LINETO) {
2503         /* other is lineto, we are either smooth or symm */
2504        Inkscape::NodePath::Node *othernode = other->other;
2505         len = NR::L2(me->pos - node->pos);
2506         delta = node->pos - othernode->pos;
2507         linelen = NR::L2(delta);
2508         if (linelen < 1e-18) 
2509             return;
2510         me->pos = node->pos + (len / linelen)*delta;
2511         return;
2512     }
2514     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2516         me->pos = 2 * node->pos - other->pos;
2517         return;
2518     }
2520     /* We are smooth */
2522     len = NR::L2(me->pos - node->pos);
2523     delta = other->pos - node->pos;
2524     otherlen = NR::L2(delta);
2525     if (otherlen < 1e-18) return;
2527     me->pos = node->pos - (len / otherlen) * delta;
2530 /**
2531  \brief Adjusts both handles according to node type and line code
2532  */
2533 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2535     g_assert(node);
2537     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2539     /* we are either smooth or symm */
2541     if (node->p.other == NULL) return;
2543     if (node->n.other == NULL) return;
2545     if (node->code == NR_LINETO) {
2546         if (node->n.other->code == NR_LINETO) return;
2547         sp_node_adjust_handle(node, 1);
2548         return;
2549     }
2551     if (node->n.other->code == NR_LINETO) {
2552         if (node->code == NR_LINETO) return;
2553         sp_node_adjust_handle(node, -1);
2554         return;
2555     }
2557     /* both are curves */
2558     NR::Point const delta( node->n.pos - node->p.pos );
2560     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2561         node->p.pos = node->pos - delta / 2;
2562         node->n.pos = node->pos + delta / 2;
2563         return;
2564     }
2566     /* We are smooth */
2567     double plen = NR::L2(node->p.pos - node->pos);
2568     if (plen < 1e-18) return;
2569     double nlen = NR::L2(node->n.pos - node->pos);
2570     if (nlen < 1e-18) return;
2571     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2572     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2575 /**
2576  * Node event callback.
2577  */
2578 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2580     gboolean ret = FALSE;
2581     switch (event->type) {
2582         case GDK_ENTER_NOTIFY:
2583             active_node = n;
2584             break;
2585         case GDK_LEAVE_NOTIFY:
2586             active_node = NULL;
2587             break;
2588         case GDK_KEY_PRESS:
2589             switch (get_group0_keyval (&event->key)) {
2590                 case GDK_space:
2591                     if (event->key.state & GDK_BUTTON1_MASK) {
2592                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2593                         stamp_repr(nodepath);
2594                         ret = TRUE;
2595                     }
2596                     break;
2597                 default:
2598                     break;
2599             }
2600             break;
2601         default:
2602             break;
2603     }
2605     return ret;
2608 /**
2609  * Handle keypress on node; directly called.
2610  */
2611 gboolean node_key(GdkEvent *event)
2613     Inkscape::NodePath::Path *np;
2615     // there is no way to verify nodes so set active_node to nil when deleting!!
2616     if (active_node == NULL) return FALSE;
2618     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2619         gint ret = FALSE;
2620         switch (get_group0_keyval (&event->key)) {
2621             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2622             case GDK_BackSpace:
2623                 np = active_node->subpath->nodepath;
2624                 sp_nodepath_node_destroy(active_node);
2625                 sp_nodepath_update_repr(np);
2626                 active_node = NULL;
2627                 ret = TRUE;
2628                 break;
2629             case GDK_c:
2630                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2631                 ret = TRUE;
2632                 break;
2633             case GDK_s:
2634                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2635                 ret = TRUE;
2636                 break;
2637             case GDK_y:
2638                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2639                 ret = TRUE;
2640                 break;
2641             case GDK_b:
2642                 sp_nodepath_node_break(active_node);
2643                 ret = TRUE;
2644                 break;
2645         }
2646         return ret;
2647     }
2648     return FALSE;
2651 /**
2652  * Mouseclick on node callback.
2653  */
2654 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2656    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2658     if (state & GDK_CONTROL_MASK) {
2659         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2661         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2662             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2663                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2664             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2665                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2666             } else {
2667                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2668             }
2669             sp_nodepath_update_repr(nodepath);
2670             sp_nodepath_update_statusbar(nodepath);
2672         } else { //ctrl+alt+click: delete node
2673             GList *node_to_delete = NULL;
2674             node_to_delete = g_list_append(node_to_delete, n);
2675             sp_node_delete_preserve(node_to_delete);
2676         }
2678     } else {
2679         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2680     }
2683 /**
2684  * Mouse grabbed node callback.
2685  */
2686 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2688    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2690     if (!n->selected) {
2691         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2692     }
2694     sp_nodepath_remember_origins (n->subpath->nodepath);
2697 /**
2698  * Mouse ungrabbed node callback.
2699  */
2700 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2702    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2704    n->dragging_out = NULL;
2706    sp_nodepath_update_repr(n->subpath->nodepath);
2709 /**
2710  * The point on a line, given by its angle, closest to the given point.
2711  * \param p  A point.
2712  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2713  * \param closest  Pointer to the point struct where the result is stored.
2714  * \todo FIXME: use dot product perhaps?
2715  */
2716 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2718     if (a == HUGE_VAL) { // vertical
2719         *closest = NR::Point(0, (*p)[NR::Y]);
2720     } else {
2721         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2722         (*closest)[NR::Y] = a * (*closest)[NR::X];
2723     }
2726 /**
2727  * Distance from the point to a line given by its angle.
2728  * \param p  A point.
2729  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2730  */
2731 static double point_line_distance(NR::Point *p, double a)
2733     NR::Point c;
2734     point_line_closest(p, a, &c);
2735     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]));
2738 /**
2739  * Callback for node "request" signal.
2740  * \todo fixme: This goes to "moved" event? (lauris)
2741  */
2742 static gboolean
2743 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2745     double yn, xn, yp, xp;
2746     double an, ap, na, pa;
2747     double d_an, d_ap, d_na, d_pa;
2748     gboolean collinear = FALSE;
2749     NR::Point c;
2750     NR::Point pr;
2752    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2754    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2755    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2757        NR::Point mouse = (*p);
2759        if (!n->dragging_out) {
2760            // This is the first drag-out event; find out which handle to drag out
2761            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2762            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2764            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2765                return FALSE;
2767            Inkscape::NodePath::NodeSide *opposite;
2768            if (appr_p > appr_n) { // closer to p
2769                n->dragging_out = &n->p;
2770                opposite = &n->n;
2771                n->code = NR_CURVETO;
2772            } else if (appr_p < appr_n) { // closer to n
2773                n->dragging_out = &n->n;
2774                opposite = &n->p;
2775                n->n.other->code = NR_CURVETO;
2776            } else { // p and n nodes are the same
2777                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2778                    n->dragging_out = &n->p;
2779                    opposite = &n->n;
2780                    n->code = NR_CURVETO;
2781                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2782                    n->dragging_out = &n->n;
2783                    opposite = &n->p;
2784                    n->n.other->code = NR_CURVETO;
2785                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2786                    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);
2787                    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);
2788                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2789                        n->dragging_out = &n->n;
2790                        opposite = &n->p;
2791                        n->n.other->code = NR_CURVETO;
2792                    } else { // closer to other's n handle
2793                        n->dragging_out = &n->p;
2794                        opposite = &n->n;
2795                        n->code = NR_CURVETO;
2796                    }
2797                }
2798            }
2800            // if there's another handle, make sure the one we drag out starts parallel to it
2801            if (opposite->pos != n->pos) {
2802                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2803            }
2805            // knots might not be created yet!
2806            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2807            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2808        }
2810        // pass this on to the handle-moved callback
2811        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2812        sp_node_update_handles(n);
2813        return TRUE;
2814    }
2816     if (state & GDK_CONTROL_MASK) { // constrained motion
2818         // calculate relative distances of handles
2819         // n handle:
2820         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2821         xn = n->n.pos[NR::X] - n->pos[NR::X];
2822         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2823         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2824             if (n->n.other) { // if there is the next point
2825                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2826                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2827                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2828             }
2829         }
2830         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2831         if (yn < 0) { xn = -xn; yn = -yn; }
2833         // p handle:
2834         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2835         xp = n->p.pos[NR::X] - n->pos[NR::X];
2836         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2837         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2838             if (n->p.other) {
2839                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2840                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2841                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2842             }
2843         }
2844         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2845         if (yp < 0) { xp = -xp; yp = -yp; }
2847         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2848             // sliding on handles, only if at least one of the handles is non-vertical
2849             // (otherwise it's the same as ctrl+drag anyway)
2851             // calculate angles of the handles
2852             if (xn == 0) {
2853                 if (yn == 0) { // no handle, consider it the continuation of the other one
2854                     an = 0;
2855                     collinear = TRUE;
2856                 }
2857                 else an = 0; // vertical; set the angle to horizontal
2858             } else an = yn/xn;
2860             if (xp == 0) {
2861                 if (yp == 0) { // no handle, consider it the continuation of the other one
2862                     ap = an;
2863                 }
2864                 else ap = 0; // vertical; set the angle to horizontal
2865             } else  ap = yp/xp;
2867             if (collinear) an = ap;
2869             // angles of the perpendiculars; HUGE_VAL means vertical
2870             if (an == 0) na = HUGE_VAL; else na = -1/an;
2871             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2873             // mouse point relative to the node's original pos
2874             pr = (*p) - n->origin;
2876             // distances to the four lines (two handles and two perpendiculars)
2877             d_an = point_line_distance(&pr, an);
2878             d_na = point_line_distance(&pr, na);
2879             d_ap = point_line_distance(&pr, ap);
2880             d_pa = point_line_distance(&pr, pa);
2882             // find out which line is the closest, save its closest point in c
2883             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2884                 point_line_closest(&pr, an, &c);
2885             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2886                 point_line_closest(&pr, ap, &c);
2887             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2888                 point_line_closest(&pr, na, &c);
2889             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2890                 point_line_closest(&pr, pa, &c);
2891             }
2893             // move the node to the closest point
2894             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2895                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2896                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2898         } else {  // constraining to hor/vert
2900             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2901                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2902             } else { // snap to vert
2903                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2904             }
2905         }
2906     } else { // move freely
2907         if (state & GDK_MOD1_MASK) { // sculpt
2908             sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
2909         } else {
2910             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2911                                         (*p)[NR::X] - n->pos[NR::X],
2912                                         (*p)[NR::Y] - n->pos[NR::Y],
2913                                         (state & GDK_SHIFT_MASK) == 0);
2914         }
2915     }
2917     n->subpath->nodepath->desktop->scroll_to_point(p);
2919     return TRUE;
2922 /**
2923  * Node handle clicked callback.
2924  */
2925 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
2927    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2929     if (state & GDK_CONTROL_MASK) { // "delete" handle
2930         if (n->p.knot == knot) {
2931             n->p.pos = n->pos;
2932         } else if (n->n.knot == knot) {
2933             n->n.pos = n->pos;
2934         }
2935         sp_node_update_handles(n);
2936         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2937         sp_nodepath_update_repr(nodepath);
2938         sp_nodepath_update_statusbar(nodepath);
2940     } else { // just select or add to selection, depending in Shift
2941         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2942     }
2945 /**
2946  * Node handle grabbed callback.
2947  */
2948 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
2950    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2952     if (!n->selected) {
2953         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2954     }
2956     // remember the origin point of the handle
2957     if (n->p.knot == knot) {
2958         n->p.origin_radial = n->p.pos - n->pos;
2959     } else if (n->n.knot == knot) {
2960         n->n.origin_radial = n->n.pos - n->pos;
2961     } else {
2962         g_assert_not_reached();
2963     }
2967 /**
2968  * Node handle ungrabbed callback.
2969  */
2970 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
2972    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2974     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
2975     if (n->p.knot == knot) {
2976         n->p.origin_radial.a = 0;
2977         sp_knot_set_position(knot, &n->p.pos, state);
2978     } else if (n->n.knot == knot) {
2979         n->n.origin_radial.a = 0;
2980         sp_knot_set_position(knot, &n->n.pos, state);
2981     } else {
2982         g_assert_not_reached();
2983     }
2985     sp_nodepath_update_repr(n->subpath->nodepath);
2988 /**
2989  * Node handle "request" signal callback.
2990  */
2991 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2993     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2995     Inkscape::NodePath::NodeSide *me, *opposite;
2996     gint which;
2997     if (n->p.knot == knot) {
2998         me = &n->p;
2999         opposite = &n->n;
3000         which = -1;
3001     } else if (n->n.knot == knot) {
3002         me = &n->n;
3003         opposite = &n->p;
3004         which = 1;
3005     } else {
3006         me = opposite = NULL;
3007         which = 0;
3008         g_assert_not_reached();
3009     }
3011     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3013     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3015     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3016         /* We are smooth node adjacent with line */
3017         NR::Point const delta = *p - n->pos;
3018         NR::Coord const len = NR::L2(delta);
3019         Inkscape::NodePath::Node *othernode = opposite->other;
3020         NR::Point const ndelta = n->pos - othernode->pos;
3021         NR::Coord const linelen = NR::L2(ndelta);
3022         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3023             NR::Coord const scal = dot(delta, ndelta) / linelen;
3024             (*p) = n->pos + (scal / linelen) * ndelta;
3025         }
3026         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3027     } else {
3028         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3029     }
3031     sp_node_adjust_handle(n, -which);
3033     return FALSE;
3036 /**
3037  * Node handle moved callback.
3038  */
3039 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3041    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3043    Inkscape::NodePath::NodeSide *me;
3044    Inkscape::NodePath::NodeSide *other;
3045     if (n->p.knot == knot) {
3046         me = &n->p;
3047         other = &n->n;
3048     } else if (n->n.knot == knot) {
3049         me = &n->n;
3050         other = &n->p;
3051     } else {
3052         me = NULL;
3053         other = NULL;
3054         g_assert_not_reached();
3055     }
3057     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3058     Radial rme(me->pos - n->pos);
3059     Radial rother(other->pos - n->pos);
3060     Radial rnew(*p - n->pos);
3062     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3063         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3064         /* 0 interpreted as "no snapping". */
3066         // The closest PI/snaps angle, starting from zero.
3067         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3068         if (me->origin_radial.a == HUGE_VAL) {
3069             // ortho doesn't exist: original handle was zero length.
3070             rnew.a = a_snapped;
3071         } else {
3072             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3073              * its opposite and perpendiculars). */
3074             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3076             // Snap to the closest.
3077             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3078                        ? a_snapped
3079                        : a_ortho );
3080         }
3081     }
3083     if (state & GDK_MOD1_MASK) {
3084         // lock handle length
3085         rnew.r = me->origin_radial.r;
3086     }
3088     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3089         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3090         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3091         rother.a += rnew.a - rme.a;
3092         other->pos = NR::Point(rother) + n->pos;
3093         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3094         sp_knot_set_position(other->knot, &other->pos, 0);
3095     }
3097     me->pos = NR::Point(rnew) + n->pos;
3098     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3100     // this is what sp_knot_set_position does, but without emitting the signal:
3101     // we cannot emit a "moved" signal because we're now processing it
3102     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3104     knot->desktop->set_coordinate_status(me->pos);
3106     update_object(n->subpath->nodepath);
3108     /* status text */
3109     SPDesktop *desktop = n->subpath->nodepath->desktop;
3110     if (!desktop) return;
3111     SPEventContext *ec = desktop->event_context;
3112     if (!ec) return;
3113     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3114     if (!mc) return;
3116     double degrees = 180 / M_PI * rnew.a;
3117     if (degrees > 180) degrees -= 360;
3118     if (degrees < -180) degrees += 360;
3119     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3120         degrees = angle_to_compass (degrees);
3122     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3124     mc->setF(Inkscape::NORMAL_MESSAGE,
3125          _("<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);
3127     g_string_free(length, TRUE);
3130 /**
3131  * Node handle event callback.
3132  */
3133 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3135     gboolean ret = FALSE;
3136     switch (event->type) {
3137         case GDK_KEY_PRESS:
3138             switch (get_group0_keyval (&event->key)) {
3139                 case GDK_space:
3140                     if (event->key.state & GDK_BUTTON1_MASK) {
3141                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3142                         stamp_repr(nodepath);
3143                         ret = TRUE;
3144                     }
3145                     break;
3146                 default:
3147                     break;
3148             }
3149             break;
3150         default:
3151             break;
3152     }
3154     return ret;
3157 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3158                                  Radial &rme, Radial &rother, gboolean const both)
3160     rme.a += angle;
3161     if ( both
3162          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3163          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3164     {
3165         rother.a += angle;
3166     }
3169 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3170                                         Radial &rme, Radial &rother, gboolean const both)
3172     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3174     gdouble r;
3175     if ( both
3176          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3177          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3178     {
3179         r = MAX(rme.r, rother.r);
3180     } else {
3181         r = rme.r;
3182     }
3184     gdouble const weird_angle = atan2(norm_angle, r);
3185 /* Bulia says norm_angle is just the visible distance that the
3186  * object's end must travel on the screen.  Left as 'angle' for want of
3187  * a better name.*/
3189     rme.a += weird_angle;
3190     if ( both
3191          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3192          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3193     {
3194         rother.a += weird_angle;
3195     }
3198 /**
3199  * Rotate one node.
3200  */
3201 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3203     Inkscape::NodePath::NodeSide *me, *other;
3204     bool both = false;
3206     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3207     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3209     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3210         me = &(n->p);
3211         other = &(n->n);
3212     } else if (!n->p.other) {
3213         me = &(n->n);
3214         other = &(n->p);
3215     } else {
3216         if (which > 0) { // right handle
3217             if (xn > xp) {
3218                 me = &(n->n);
3219                 other = &(n->p);
3220             } else {
3221                 me = &(n->p);
3222                 other = &(n->n);
3223             }
3224         } else if (which < 0){ // left handle
3225             if (xn <= xp) {
3226                 me = &(n->n);
3227                 other = &(n->p);
3228             } else {
3229                 me = &(n->p);
3230                 other = &(n->n);
3231             }
3232         } else { // both handles
3233             me = &(n->n);
3234             other = &(n->p);
3235             both = true;
3236         }
3237     }
3239     Radial rme(me->pos - n->pos);
3240     Radial rother(other->pos - n->pos);
3242     if (screen) {
3243         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3244     } else {
3245         node_rotate_one_internal (*n, angle, rme, rother, both);
3246     }
3248     me->pos = n->pos + NR::Point(rme);
3250     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3251         other->pos =  n->pos + NR::Point(rother);
3252     }
3254     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3255     // so here we just move all the knots without emitting move signals, for speed
3256     sp_node_update_handles(n, false);
3259 /**
3260  * Rotate selected nodes.
3261  */
3262 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3264     if (!nodepath || !nodepath->selected) return;
3266     if (g_list_length(nodepath->selected) == 1) {
3267        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3268         node_rotate_one (n, angle, which, screen);
3269     } else {
3270        // rotate as an object:
3272         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3273         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3274         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3275             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3276             box.expandTo (n->pos); // contain all selected nodes
3277         }
3279         gdouble rot;
3280         if (screen) {
3281             gdouble const zoom = nodepath->desktop->current_zoom();
3282             gdouble const zmove = angle / zoom;
3283             gdouble const r = NR::L2(box.max() - box.midpoint());
3284             rot = atan2(zmove, r);
3285         } else {
3286             rot = angle;
3287         }
3289         NR::Matrix t =
3290             NR::Matrix (NR::translate(-box.midpoint())) *
3291             NR::Matrix (NR::rotate(rot)) *
3292             NR::Matrix (NR::translate(box.midpoint()));
3294         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3295             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3296             n->pos *= t;
3297             n->n.pos *= t;
3298             n->p.pos *= t;
3299             sp_node_update_handles(n, false);
3300         }
3301     }
3303     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3306 /**
3307  * Scale one node.
3308  */
3309 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3311     bool both = false;
3312     Inkscape::NodePath::NodeSide *me, *other;
3314     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3315     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3317     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3318         me = &(n->p);
3319         other = &(n->n);
3320         n->code = NR_CURVETO;
3321     } else if (!n->p.other) {
3322         me = &(n->n);
3323         other = &(n->p);
3324         if (n->n.other)
3325             n->n.other->code = NR_CURVETO;
3326     } else {
3327         if (which > 0) { // right handle
3328             if (xn > xp) {
3329                 me = &(n->n);
3330                 other = &(n->p);
3331                 if (n->n.other)
3332                     n->n.other->code = NR_CURVETO;
3333             } else {
3334                 me = &(n->p);
3335                 other = &(n->n);
3336                 n->code = NR_CURVETO;
3337             }
3338         } else if (which < 0){ // left handle
3339             if (xn <= xp) {
3340                 me = &(n->n);
3341                 other = &(n->p);
3342                 if (n->n.other)
3343                     n->n.other->code = NR_CURVETO;
3344             } else {
3345                 me = &(n->p);
3346                 other = &(n->n);
3347                 n->code = NR_CURVETO;
3348             }
3349         } else { // both handles
3350             me = &(n->n);
3351             other = &(n->p);
3352             both = true;
3353             n->code = NR_CURVETO;
3354             if (n->n.other)
3355                 n->n.other->code = NR_CURVETO;
3356         }
3357     }
3359     Radial rme(me->pos - n->pos);
3360     Radial rother(other->pos - n->pos);
3362     rme.r += grow;
3363     if (rme.r < 0) rme.r = 0;
3364     if (rme.a == HUGE_VAL) {
3365         if (me->other) { // if direction is unknown, initialize it towards the next node
3366             Radial rme_next(me->other->pos - n->pos);
3367             rme.a = rme_next.a;
3368         } else { // if there's no next, initialize to 0
3369             rme.a = 0;
3370         }
3371     }
3372     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3373         rother.r += grow;
3374         if (rother.r < 0) rother.r = 0;
3375         if (rother.a == HUGE_VAL) {
3376             rother.a = rme.a + M_PI;
3377         }
3378     }
3380     me->pos = n->pos + NR::Point(rme);
3382     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3383         other->pos = n->pos + NR::Point(rother);
3384     }
3386     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3387     // so here we just move all the knots without emitting move signals, for speed
3388     sp_node_update_handles(n, false);
3391 /**
3392  * Scale selected nodes.
3393  */
3394 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3396     if (!nodepath || !nodepath->selected) return;
3398     if (g_list_length(nodepath->selected) == 1) {
3399         // scale handles of the single selected node
3400         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3401         node_scale_one (n, grow, which);
3402     } else {
3403         // scale nodes as an "object":
3405         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3406         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3407         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3408             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3409             box.expandTo (n->pos); // contain all selected nodes
3410         }
3412         double scale = (box.maxExtent() + grow)/box.maxExtent();
3414         NR::Matrix t =
3415             NR::Matrix (NR::translate(-box.midpoint())) *
3416             NR::Matrix (NR::scale(scale, scale)) *
3417             NR::Matrix (NR::translate(box.midpoint()));
3419         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3420             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3421             n->pos *= t;
3422             n->n.pos *= t;
3423             n->p.pos *= t;
3424             sp_node_update_handles(n, false);
3425         }
3426     }
3428     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3431 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3433     if (!nodepath) return;
3434     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3437 /**
3438  * Flip selected nodes horizontally/vertically.
3439  */
3440 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3442     if (!nodepath || !nodepath->selected) return;
3444     if (g_list_length(nodepath->selected) == 1) {
3445         // flip handles of the single selected node
3446         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3447         double temp = n->p.pos[axis];
3448         n->p.pos[axis] = n->n.pos[axis];
3449         n->n.pos[axis] = temp;
3450         sp_node_update_handles(n, false);
3451     } else {
3452         // scale nodes as an "object":
3454         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3455         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3456         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3457             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3458             box.expandTo (n->pos); // contain all selected nodes
3459         }
3461         NR::Matrix t =
3462             NR::Matrix (NR::translate(-box.midpoint())) *
3463             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3464             NR::Matrix (NR::translate(box.midpoint()));
3466         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3467             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3468             n->pos *= t;
3469             n->n.pos *= t;
3470             n->p.pos *= t;
3471             sp_node_update_handles(n, false);
3472         }
3473     }
3475     sp_nodepath_update_repr(nodepath);
3478 //-----------------------------------------------
3479 /**
3480  * Return new subpath under given nodepath.
3481  */
3482 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3484     g_assert(nodepath);
3485     g_assert(nodepath->desktop);
3487    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3489     s->nodepath = nodepath;
3490     s->closed = FALSE;
3491     s->nodes = NULL;
3492     s->first = NULL;
3493     s->last = NULL;
3495     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3496     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3497     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3499     return s;
3502 /**
3503  * Destroy nodes in subpath, then subpath itself.
3504  */
3505 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3507     g_assert(subpath);
3508     g_assert(subpath->nodepath);
3509     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3511     while (subpath->nodes) {
3512         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3513     }
3515     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3517     g_free(subpath);
3520 /**
3521  * Link head to tail in subpath.
3522  */
3523 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3525     g_assert(!sp->closed);
3526     g_assert(sp->last != sp->first);
3527     g_assert(sp->first->code == NR_MOVETO);
3529     sp->closed = TRUE;
3531     //Link the head to the tail
3532     sp->first->p.other = sp->last;
3533     sp->last->n.other  = sp->first;
3534     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3535     sp->first          = sp->last;
3537     //Remove the extra end node
3538     sp_nodepath_node_destroy(sp->last->n.other);
3541 /**
3542  * Open closed (loopy) subpath at node.
3543  */
3544 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3546     g_assert(sp->closed);
3547     g_assert(n->subpath == sp);
3548     g_assert(sp->first == sp->last);
3550     /* We create new startpoint, current node will become last one */
3552    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3553                                                 &n->pos, &n->pos, &n->n.pos);
3556     sp->closed        = FALSE;
3558     //Unlink to make a head and tail
3559     sp->first         = new_path;
3560     sp->last          = n;
3561     n->n.other        = NULL;
3562     new_path->p.other = NULL;
3565 /**
3566  * Returns area in triangle given by points; may be negative.
3567  */
3568 inline double
3569 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3571     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]);
3574 /**
3575  * Return new node in subpath with given properties.
3576  * \param pos Position of node.
3577  * \param ppos Handle position in previous direction
3578  * \param npos Handle position in previous direction
3579  */
3580 Inkscape::NodePath::Node *
3581 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)
3583     g_assert(sp);
3584     g_assert(sp->nodepath);
3585     g_assert(sp->nodepath->desktop);
3587     if (nodechunk == NULL)
3588         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3590     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3592     n->subpath  = sp;
3594     if (type != Inkscape::NodePath::NODE_NONE) {
3595         // use the type from sodipodi:nodetypes
3596         n->type = type;
3597     } else {
3598         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3599             // points are (almost) collinear
3600             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3601                 // endnode, or a node with a retracted handle
3602                 n->type = Inkscape::NodePath::NODE_CUSP;
3603             } else {
3604                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3605             }
3606         } else {
3607             n->type = Inkscape::NodePath::NODE_CUSP;
3608         }
3609     }
3611     n->code     = code;
3612     n->selected = FALSE;
3613     n->pos      = *pos;
3614     n->p.pos    = *ppos;
3615     n->n.pos    = *npos;
3617     n->dragging_out = NULL;
3619     Inkscape::NodePath::Node *prev;
3620     if (next) {
3621         //g_assert(g_list_find(sp->nodes, next));
3622         prev = next->p.other;
3623     } else {
3624         prev = sp->last;
3625     }
3627     if (prev)
3628         prev->n.other = n;
3629     else
3630         sp->first = n;
3632     if (next)
3633         next->p.other = n;
3634     else
3635         sp->last = n;
3637     n->p.other = prev;
3638     n->n.other = next;
3640     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"));
3641     sp_knot_set_position(n->knot, pos, 0);
3643     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3644     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3645     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3646     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3647     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3648     sp_knot_update_ctrl(n->knot);
3650     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3651     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3652     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3653     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3654     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3655     sp_knot_show(n->knot);
3657     // We only create handle knots and lines on demand
3658     n->p.knot = NULL;
3659     n->p.line = NULL;
3660     n->n.knot = NULL;
3661     n->n.line = NULL;
3663     sp->nodes = g_list_prepend(sp->nodes, n);
3665     return n;
3668 /**
3669  * Destroy node and its knots, link neighbors in subpath.
3670  */
3671 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3673     g_assert(node);
3674     g_assert(node->subpath);
3675     g_assert(SP_IS_KNOT(node->knot));
3677    Inkscape::NodePath::SubPath *sp = node->subpath;
3679     if (node->selected) { // first, deselect
3680         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3681         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3682     }
3684     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3686     g_object_unref(G_OBJECT(node->knot));
3687     if (node->p.knot)
3688         g_object_unref(G_OBJECT(node->p.knot));
3689     if (node->n.knot)
3690         g_object_unref(G_OBJECT(node->n.knot));
3692     if (node->p.line)
3693         gtk_object_destroy(GTK_OBJECT(node->p.line));
3694     if (node->n.line)
3695         gtk_object_destroy(GTK_OBJECT(node->n.line));
3697     if (sp->nodes) { // there are others nodes on the subpath
3698         if (sp->closed) {
3699             if (sp->first == node) {
3700                 g_assert(sp->last == node);
3701                 sp->first = node->n.other;
3702                 sp->last = sp->first;
3703             }
3704             node->p.other->n.other = node->n.other;
3705             node->n.other->p.other = node->p.other;
3706         } else {
3707             if (sp->first == node) {
3708                 sp->first = node->n.other;
3709                 sp->first->code = NR_MOVETO;
3710             }
3711             if (sp->last == node) sp->last = node->p.other;
3712             if (node->p.other) node->p.other->n.other = node->n.other;
3713             if (node->n.other) node->n.other->p.other = node->p.other;
3714         }
3715     } else { // this was the last node on subpath
3716         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3717     }
3719     g_mem_chunk_free(nodechunk, node);
3722 /**
3723  * Returns one of the node's two sides.
3724  * \param which Indicates which side.
3725  * \return Pointer to previous node side if which==-1, next if which==1.
3726  */
3727 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3729     g_assert(node);
3731     switch (which) {
3732         case -1:
3733             return &node->p;
3734         case 1:
3735             return &node->n;
3736         default:
3737             break;
3738     }
3740     g_assert_not_reached();
3742     return NULL;
3745 /**
3746  * Return the other side of the node, given one of its sides.
3747  */
3748 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
3750     g_assert(node);
3752     if (me == &node->p) return &node->n;
3753     if (me == &node->n) return &node->p;
3755     g_assert_not_reached();
3757     return NULL;
3760 /**
3761  * Return NRPathcode on the given side of the node.
3762  */
3763 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3765     g_assert(node);
3767     if (me == &node->p) {
3768         if (node->p.other) return (NRPathcode)node->code;
3769         return NR_MOVETO;
3770     }
3772     if (me == &node->n) {
3773         if (node->n.other) return (NRPathcode)node->n.other->code;
3774         return NR_MOVETO;
3775     }
3777     g_assert_not_reached();
3779     return NR_END;
3782 /**
3783  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3784  */
3785 Inkscape::NodePath::Node *
3786 sp_nodepath_get_node_by_index(int index)
3788     Inkscape::NodePath::Node *e = NULL;
3790     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3791     if (!nodepath) {
3792         return e;
3793     }
3795     //find segment
3796     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3798         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3799         int n = g_list_length(sp->nodes);
3800         if (sp->closed) {
3801             n++;
3802         }
3804         //if the piece belongs to this subpath grab it
3805         //otherwise move onto the next subpath
3806         if (index < n) {
3807             e = sp->first;
3808             for (int i = 0; i < index; ++i) {
3809                 e = e->n.other;
3810             }
3811             break;
3812         } else {
3813             if (sp->closed) {
3814                 index -= (n+1);
3815             } else {
3816                 index -= n;
3817             }
3818         }
3819     }
3821     return e;
3824 /**
3825  * Returns plain text meaning of node type.
3826  */
3827 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3829     unsigned retracted = 0;
3830     bool endnode = false;
3832     for (int which = -1; which <= 1; which += 2) {
3833         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3834         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3835             retracted ++;
3836         if (!side->other)
3837             endnode = true;
3838     }
3840     if (retracted == 0) {
3841         if (endnode) {
3842                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3843                 return _("end node");
3844         } else {
3845             switch (node->type) {
3846                 case Inkscape::NodePath::NODE_CUSP:
3847                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3848                     return _("cusp");
3849                 case Inkscape::NodePath::NODE_SMOOTH:
3850                     // TRANSLATORS: "smooth" is an adjective here
3851                     return _("smooth");
3852                 case Inkscape::NodePath::NODE_SYMM:
3853                     return _("symmetric");
3854             }
3855         }
3856     } else if (retracted == 1) {
3857         if (endnode) {
3858             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3859             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3860         } else {
3861             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3862         }
3863     } else {
3864         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3865     }
3867     return NULL;
3870 /**
3871  * Handles content of statusbar as long as node tool is active.
3872  */
3873 void
3874 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3876     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>arrow</b> keys to move nodes");
3877     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3879     gint total = 0;
3880     gint selected = 0;
3881     SPDesktop *desktop = NULL;
3883     if (nodepath) {
3884         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3885             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3886             total += g_list_length(subpath->nodes);
3887         }
3888         selected = g_list_length(nodepath->selected);
3889         desktop = nodepath->desktop;
3890     } else {
3891         desktop = SP_ACTIVE_DESKTOP;
3892     }
3894     SPEventContext *ec = desktop->event_context;
3895     if (!ec) return;
3896     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3897     if (!mc) return;
3899     if (selected == 0) {
3900         Inkscape::Selection *sel = desktop->selection;
3901         if (!sel || sel->isEmpty()) {
3902             mc->setF(Inkscape::NORMAL_MESSAGE,
3903                      _("Select a single object to edit its nodes or handles."));
3904         } else {
3905             if (nodepath) {
3906             mc->setF(Inkscape::NORMAL_MESSAGE,
3907                      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.",
3908                               "<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.",
3909                               total),
3910                      total);
3911             } else {
3912                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3913                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3914                 } else {
3915                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3916                 }
3917             }
3918         }
3919     } else if (nodepath && selected == 1) {
3920         mc->setF(Inkscape::NORMAL_MESSAGE,
3921                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3922                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3923                           total),
3924                  selected, total, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3925     } else {
3926         mc->setF(Inkscape::NORMAL_MESSAGE,
3927                  ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
3928                           "<b>%i</b> of <b>%i</b> nodes selected. %s.",
3929                           total),
3930                  selected, total, when_selected);
3931     }
3935 /*
3936   Local Variables:
3937   mode:c++
3938   c-file-style:"stroustrup"
3939   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3940   indent-tabs-mode:nil
3941   fill-column:99
3942   End:
3943 */
3944 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :