Code

show handles toggle
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/curve.h"
19 #include "display/sp-ctrlline.h"
20 #include "display/sodipodi-ctrl.h"
21 #include <glibmm/i18n.h>
22 #include "libnr/n-art-bpath.h"
23 #include "helper/units.h"
24 #include "knot.h"
25 #include "inkscape.h"
26 #include "document.h"
27 #include "sp-namedview.h"
28 #include "desktop.h"
29 #include "desktop-handles.h"
30 #include "snap.h"
31 #include "message-stack.h"
32 #include "message-context.h"
33 #include "node-context.h"
34 #include "selection-chemistry.h"
35 #include "selection.h"
36 #include "xml/repr.h"
37 #include "prefs-utils.h"
38 #include "sp-metrics.h"
39 #include "sp-path.h"
40 #include "libnr/nr-matrix-ops.h"
41 #include "splivarot.h"
42 #include "svg/svg.h"
43 #include "display/bezier-utils.h"
44 #include <vector>
45 #include <algorithm>
47 class NR::Matrix;
49 /// \todo
50 /// evil evil evil. FIXME: conflict of two different Path classes!
51 /// There is a conflict in the namespace between two classes named Path.
52 /// #include "sp-flowtext.h"
53 /// #include "sp-flowregion.h"
55 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
56 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
57 GType sp_flowregion_get_type (void);
58 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
59 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
60 GType sp_flowtext_get_type (void);
61 // end evil workaround
63 #include "helper/stlport.h"
66 /// \todo fixme: Implement these via preferences */
68 #define NODE_FILL          0xbfbfbf00
69 #define NODE_STROKE        0x000000ff
70 #define NODE_FILL_HI       0xff000000
71 #define NODE_STROKE_HI     0x000000ff
72 #define NODE_FILL_SEL      0x0000ffff
73 #define NODE_STROKE_SEL    0x000000ff
74 #define NODE_FILL_SEL_HI   0xff000000
75 #define NODE_STROKE_SEL_HI 0x000000ff
76 #define KNOT_FILL          0xffffffff
77 #define KNOT_STROKE        0x000000ff
78 #define KNOT_FILL_HI       0xff000000
79 #define KNOT_STROKE_HI     0x000000ff
81 static GMemChunk *nodechunk = NULL;
83 /* Creation from object */
85 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
86 static gchar *parse_nodetypes(gchar const *types, gint length);
88 /* Object updating */
90 static void stamp_repr(Inkscape::NodePath::Path *np);
91 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
92 static gchar *create_typestr(Inkscape::NodePath::Path *np);
94 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
96 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
98 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
100 /* Adjust handle placement, if the node or the other handle is moved */
101 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
102 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
104 /* Node event callbacks */
105 static void node_clicked(SPKnot *knot, guint state, gpointer data);
106 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
107 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
108 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
110 /* Handle event callbacks */
111 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
112 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
113 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
114 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
115 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
116 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
118 /* Constructors and destructors */
120 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
121 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
122 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
123 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
124 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
125                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
126 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
128 /* Helpers */
130 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
131 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
132 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
134 // active_node indicates mouseover node
135 static Inkscape::NodePath::Node *active_node = NULL;
137 /**
138  * \brief Creates new nodepath from item
139  */
140 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool show_handles)
142     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
144     /** \todo
145      * FIXME: remove this. We don't want to edit paths inside flowtext.
146      * Instead we will build our flowtext with cloned paths, so that the
147      * real paths are outside the flowtext and thus editable as usual.
148      */
149     if (SP_IS_FLOWTEXT(item)) {
150         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
151             if SP_IS_FLOWREGION(child) {
152                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
153                 if (grandchild && SP_IS_PATH(grandchild)) {
154                     item = SP_ITEM(grandchild);
155                     break;
156                 }
157             }
158         }
159     }
161     if (!SP_IS_PATH(item))
162         return NULL;
163     SPPath *path = SP_PATH(item);
164     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
165     if (curve == NULL)
166         return NULL;
168     NArtBpath *bpath = sp_curve_first_bpath(curve);
169     gint length = curve->end;
170     if (length == 0)
171         return NULL; // prevent crash for one-node paths
173     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
174     gchar *typestr = parse_nodetypes(nodetypes, length);
176     //Create new nodepath
177     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
178     if (!np)
179         return NULL;
181     // Set defaults
182     np->desktop     = desktop;
183     np->path        = path;
184     np->subpaths    = NULL;
185     np->selected    = NULL;
186     np->nodeContext = NULL; //Let the context that makes this set it
187     np->livarot_path = NULL;
188     np->local_change = 0;
189     np->show_handles = show_handles;
191     // we need to update item's transform from the repr here,
192     // because they may be out of sync when we respond
193     // to a change in repr by regenerating nodepath     --bb
194     sp_object_read_attr(SP_OBJECT(item), "transform");
196     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
197     np->d2i  = np->i2d.inverse();
198     np->repr = repr;
200     // create the subpath(s) from the bpath
201     NArtBpath *b = bpath;
202     while (b->code != NR_END) {
203         b = subpath_from_bpath(np, b, typestr + (b - bpath));
204     }
206     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
207     np->subpaths = g_list_reverse(np->subpaths);
209     g_free(typestr);
210     sp_curve_unref(curve);
212     // create the livarot representation from the same item
213     np->livarot_path = Path_for_item(item, true, true);
214     if (np->livarot_path)
215         np->livarot_path->ConvertWithBackData(0.01);
217     return np;
220 /**
221  * Destroys nodepath's subpaths, then itself, also tell context about it.
222  */
223 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
225     if (!np)  //soft fail, like delete
226         return;
228     while (np->subpaths) {
229         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
230     }
232     //Inform the context that made me, if any, that I am gone.
233     if (np->nodeContext)
234         np->nodeContext->nodepath = NULL;
236     g_assert(!np->selected);
238     if (np->livarot_path) {
239         delete np->livarot_path;
240         np->livarot_path = NULL;
241     }
243     np->desktop = NULL;
245     g_free(np);
249 /**
250  *  Return the node count of a given NodeSubPath.
251  */
252 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
254     if (!subpath)
255         return 0;
256     gint nodeCount = g_list_length(subpath->nodes);
257     return nodeCount;
260 /**
261  *  Return the node count of a given NodePath.
262  */
263 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
265     if (!np)
266         return 0;
267     gint nodeCount = 0;
268     for (GList *item = np->subpaths ; item ; item=item->next) {
269        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
270         nodeCount += g_list_length(subpath->nodes);
271     }
272     return nodeCount;
275 /**
276  *  Return the subpath count of a given NodePath.
277  */
278 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
280     if (!np)
281         return 0;
282     return g_list_length (np->subpaths);
285 /**
286  *  Return the selected node count of a given NodePath.
287  */
288 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
290     if (!np)
291         return 0;
292     return g_list_length (np->selected);
295 /**
296  *  Return the number of subpaths where nodes are selected in a given NodePath.
297  */
298 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
300     if (!np)
301         return 0;
302     if (!np->selected)
303         return 0;
304     if (!np->selected->next)
305         return 1;
306     gint count = 0;
307     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
308         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
309         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
310             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
311             if (node->selected) {
312                 count ++;
313                 break;
314             }
315         }
316     }
317     return count;
319  
320 /**
321  * Clean up a nodepath after editing.
322  *
323  * Currently we are deleting trivial subpaths.
324  */
325 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
327     GList *badSubPaths = NULL;
329     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
330     for (GList *l = nodepath->subpaths; l ; l=l->next) {
331        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
332        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
333             badSubPaths = g_list_append(badSubPaths, sp);
334     }
336     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
337     //also removes the subpath from nodepath->subpaths
338     for (GList *l = badSubPaths; l ; l=l->next) {
339        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
340         sp_nodepath_subpath_destroy(sp);
341     }
343     g_list_free(badSubPaths);
346 /**
347  * Create new nodepath from b, make it subpath of np.
348  * \param t The node type.
349  * \todo Fixme: t should be a proper type, rather than gchar
350  */
351 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
353     NR::Point ppos, pos, npos;
355     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
357     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
358     bool const closed = (b->code == NR_MOVETO);
360     pos = NR::Point(b->x3, b->y3) * np->i2d;
361     if (b[1].code == NR_CURVETO) {
362         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
363     } else {
364         npos = pos;
365     }
366     Inkscape::NodePath::Node *n;
367     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
368     g_assert(sp->first == n);
369     g_assert(sp->last  == n);
371     b++;
372     t++;
373     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
374         pos = NR::Point(b->x3, b->y3) * np->i2d;
375         if (b->code == NR_CURVETO) {
376             ppos = NR::Point(b->x2, b->y2) * np->i2d;
377         } else {
378             ppos = pos;
379         }
380         if (b[1].code == NR_CURVETO) {
381             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
382         } else {
383             npos = pos;
384         }
385         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
386         b++;
387         t++;
388     }
390     if (closed) sp_nodepath_subpath_close(sp);
392     return b;
395 /**
396  * Convert from sodipodi:nodetypes to new style type string.
397  */
398 static gchar *parse_nodetypes(gchar const *types, gint length)
400     g_assert(length > 0);
402     gchar *typestr = g_new(gchar, length + 1);
404     gint pos = 0;
406     if (types) {
407         for (gint i = 0; types[i] && ( i < length ); i++) {
408             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
409             if (types[i] != '\0') {
410                 switch (types[i]) {
411                     case 's':
412                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
413                         break;
414                     case 'z':
415                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
416                         break;
417                     case 'c':
418                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
419                         break;
420                     default:
421                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
422                         break;
423                 }
424             }
425         }
426     }
428     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
430     return typestr;
433 /**
434  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
435  * updated but repr is not (for speed). Used during curve and node drag.
436  */
437 static void update_object(Inkscape::NodePath::Path *np)
439     g_assert(np);
441     SPCurve *curve = create_curve(np);
443     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
445     sp_curve_unref(curve);
448 /**
449  * Update XML path node with data from path object.
450  */
451 static void update_repr_internal(Inkscape::NodePath::Path *np)
453     g_assert(np);
455     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
457     SPCurve *curve = create_curve(np);
458     gchar *typestr = create_typestr(np);
459     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
461     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
462         np->local_change++;
463         repr->setAttribute("d", svgpath);
464     }
466     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
467         np->local_change++;
468         repr->setAttribute("sodipodi:nodetypes", typestr);
469     }
471     g_free(svgpath);
472     g_free(typestr);
473     sp_curve_unref(curve);
476 /**
477  * Update XML path node with data from path object, commit changes forever.
478  */
479 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np)
481     update_repr_internal(np);
482     sp_document_done(sp_desktop_document(np->desktop));
484     if (np->livarot_path) {
485         delete np->livarot_path;
486         np->livarot_path = NULL;
487     }
489     if (np->path && SP_IS_ITEM(np->path)) {
490         np->livarot_path = Path_for_item (np->path, true, true);
491         if (np->livarot_path)
492             np->livarot_path->ConvertWithBackData(0.01);
493     }
496 /**
497  * Update XML path node with data from path object, commit changes with undo.
498  */
499 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key)
501     update_repr_internal(np);
502     sp_document_maybe_done(sp_desktop_document(np->desktop), key);
504     if (np->livarot_path) {
505         delete np->livarot_path;
506         np->livarot_path = NULL;
507     }
509     if (np->path && SP_IS_ITEM(np->path)) {
510         np->livarot_path = Path_for_item (np->path, true, true);
511         if (np->livarot_path)
512             np->livarot_path->ConvertWithBackData(0.01);
513     }
516 /**
517  * Make duplicate of path, replace corresponding XML node in tree, commit.
518  */
519 static void stamp_repr(Inkscape::NodePath::Path *np)
521     g_assert(np);
523     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
524     Inkscape::XML::Node *new_repr = old_repr->duplicate();
526     // remember the position of the item
527     gint pos = old_repr->position();
528     // remember parent
529     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
531     SPCurve *curve = create_curve(np);
532     gchar *typestr = create_typestr(np);
534     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
536     new_repr->setAttribute("d", svgpath);
537     new_repr->setAttribute("sodipodi:nodetypes", typestr);
539     // add the new repr to the parent
540     parent->appendChild(new_repr);
541     // move to the saved position
542     new_repr->setPosition(pos > 0 ? pos : 0);
544     sp_document_done(sp_desktop_document(np->desktop));
546     Inkscape::GC::release(new_repr);
547     g_free(svgpath);
548     g_free(typestr);
549     sp_curve_unref(curve);
552 /**
553  * Create curve from path.
554  */
555 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
557     SPCurve *curve = sp_curve_new();
559     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
560        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
561         sp_curve_moveto(curve,
562                         sp->first->pos * np->d2i);
563        Inkscape::NodePath::Node *n = sp->first->n.other;
564         while (n) {
565             NR::Point const end_pt = n->pos * np->d2i;
566             switch (n->code) {
567                 case NR_LINETO:
568                     sp_curve_lineto(curve, end_pt);
569                     break;
570                 case NR_CURVETO:
571                     sp_curve_curveto(curve,
572                                      n->p.other->n.pos * np->d2i,
573                                      n->p.pos * np->d2i,
574                                      end_pt);
575                     break;
576                 default:
577                     g_assert_not_reached();
578                     break;
579             }
580             if (n != sp->last) {
581                 n = n->n.other;
582             } else {
583                 n = NULL;
584             }
585         }
586         if (sp->closed) {
587             sp_curve_closepath(curve);
588         }
589     }
591     return curve;
594 /**
595  * Convert path type string to sodipodi:nodetypes style.
596  */
597 static gchar *create_typestr(Inkscape::NodePath::Path *np)
599     gchar *typestr = g_new(gchar, 32);
600     gint len = 32;
601     gint pos = 0;
603     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
604        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
606         if (pos >= len) {
607             typestr = g_renew(gchar, typestr, len + 32);
608             len += 32;
609         }
611         typestr[pos++] = 'c';
613        Inkscape::NodePath::Node *n;
614         n = sp->first->n.other;
615         while (n) {
616             gchar code;
618             switch (n->type) {
619                 case Inkscape::NodePath::NODE_CUSP:
620                     code = 'c';
621                     break;
622                 case Inkscape::NodePath::NODE_SMOOTH:
623                     code = 's';
624                     break;
625                 case Inkscape::NodePath::NODE_SYMM:
626                     code = 'z';
627                     break;
628                 default:
629                     g_assert_not_reached();
630                     code = '\0';
631                     break;
632             }
634             if (pos >= len) {
635                 typestr = g_renew(gchar, typestr, len + 32);
636                 len += 32;
637             }
639             typestr[pos++] = code;
641             if (n != sp->last) {
642                 n = n->n.other;
643             } else {
644                 n = NULL;
645             }
646         }
647     }
649     if (pos >= len) {
650         typestr = g_renew(gchar, typestr, len + 1);
651         len += 1;
652     }
654     typestr[pos++] = '\0';
656     return typestr;
659 /**
660  * Returns current path in context.
661  */
662 static Inkscape::NodePath::Path *sp_nodepath_current()
664     if (!SP_ACTIVE_DESKTOP) {
665         return NULL;
666     }
668     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
670     if (!SP_IS_NODE_CONTEXT(event_context)) {
671         return NULL;
672     }
674     return SP_NODE_CONTEXT(event_context)->nodepath;
679 /**
680  \brief Fills node and handle positions for three nodes, splitting line
681   marked by end at distance t.
682  */
683 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
685     g_assert(new_path != NULL);
686     g_assert(end      != NULL);
688     g_assert(end->p.other == new_path);
689    Inkscape::NodePath::Node *start = new_path->p.other;
690     g_assert(start);
692     if (end->code == NR_LINETO) {
693         new_path->type =Inkscape::NodePath::NODE_CUSP;
694         new_path->code = NR_LINETO;
695         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
696     } else {
697         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
698         new_path->code = NR_CURVETO;
699         gdouble s      = 1 - t;
700         for (int dim = 0; dim < 2; dim++) {
701             NR::Coord const f000 = start->pos[dim];
702             NR::Coord const f001 = start->n.pos[dim];
703             NR::Coord const f011 = end->p.pos[dim];
704             NR::Coord const f111 = end->pos[dim];
705             NR::Coord const f00t = s * f000 + t * f001;
706             NR::Coord const f01t = s * f001 + t * f011;
707             NR::Coord const f11t = s * f011 + t * f111;
708             NR::Coord const f0tt = s * f00t + t * f01t;
709             NR::Coord const f1tt = s * f01t + t * f11t;
710             NR::Coord const fttt = s * f0tt + t * f1tt;
711             start->n.pos[dim]    = f00t;
712             new_path->p.pos[dim] = f0tt;
713             new_path->pos[dim]   = fttt;
714             new_path->n.pos[dim] = f1tt;
715             end->p.pos[dim]      = f11t;
716         }
717     }
720 /**
721  * Adds new node on direct line between two nodes, activates handles of all
722  * three nodes.
723  */
724 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
726     g_assert(end);
727     g_assert(end->subpath);
728     g_assert(g_list_find(end->subpath->nodes, end));
730    Inkscape::NodePath::Node *start = end->p.other;
731     g_assert( start->n.other == end );
732    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
733                                                end,
734                                               Inkscape::NodePath::NODE_SMOOTH,
735                                                (NRPathcode)end->code,
736                                                &start->pos, &start->pos, &start->n.pos);
737     sp_nodepath_line_midpoint(newnode, end, t);
739     sp_node_update_handles(start);
740     sp_node_update_handles(newnode);
741     sp_node_update_handles(end);
743     return newnode;
746 /**
747 \brief Break the path at the node: duplicate the argument node, start a new subpath with the duplicate, and copy all nodes after the argument node to it
748 */
749 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
751     g_assert(node);
752     g_assert(node->subpath);
753     g_assert(g_list_find(node->subpath->nodes, node));
755    Inkscape::NodePath::SubPath *sp = node->subpath;
756     Inkscape::NodePath::Path *np    = sp->nodepath;
758     if (sp->closed) {
759         sp_nodepath_subpath_open(sp, node);
760         return sp->first;
761     } else {
762         // no break for end nodes
763         if (node == sp->first) return NULL;
764         if (node == sp->last ) return NULL;
766         // create a new subpath
767        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
769         // duplicate the break node as start of the new subpath
770        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
772         while (node->n.other) { // copy the remaining nodes into the new subpath
773            Inkscape::NodePath::Node *n  = node->n.other;
774            Inkscape::NodePath::Node *nn = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
775             if (n->selected) {
776                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
777             }
778             sp_nodepath_node_destroy(n); // remove the point on the original subpath
779         }
781         return newnode;
782     }
785 /**
786  * Duplicate node and connect to neighbours.
787  */
788 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
790     g_assert(node);
791     g_assert(node->subpath);
792     g_assert(g_list_find(node->subpath->nodes, node));
794    Inkscape::NodePath::SubPath *sp = node->subpath;
796     NRPathcode code = (NRPathcode) node->code;
797     if (code == NR_MOVETO) { // if node is the endnode,
798         node->code = NR_LINETO; // new one is inserted before it, so change that to line
799     }
801     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
803     if (!node->n.other || !node->p.other) // if node is an endnode, select it
804         return node;
805     else
806         return newnode; // otherwise select the newly created node
809 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
811     node->p.pos = (node->pos + (node->pos - node->n.pos));
814 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
816     node->n.pos = (node->pos + (node->pos - node->p.pos));
819 /**
820  * Change line type at node, with side effects on neighbours.
821  */
822 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
824     g_assert(end);
825     g_assert(end->subpath);
826     g_assert(end->p.other);
828     if (end->code == static_cast< guint > ( code ) )
829         return;
831    Inkscape::NodePath::Node *start = end->p.other;
833     end->code = code;
835     if (code == NR_LINETO) {
836         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
837         if (end->n.other) {
838             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
839         }
840         sp_node_adjust_handle(start, -1);
841         sp_node_adjust_handle(end, 1);
842     } else {
843         NR::Point delta = end->pos - start->pos;
844         start->n.pos = start->pos + delta / 3;
845         end->p.pos = end->pos - delta / 3;
846         sp_node_adjust_handle(start, 1);
847         sp_node_adjust_handle(end, -1);
848     }
850     sp_node_update_handles(start);
851     sp_node_update_handles(end);
854 /**
855  * Change node type, and its handles accordingly.
856  */
857 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
859     g_assert(node);
860     g_assert(node->subpath);
862     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
863         return node;
865     if ((node->p.other != NULL) && (node->n.other != NULL)) {
866         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
867             type =Inkscape::NodePath::NODE_CUSP;
868         }
869     }
871     node->type = type;
873     if (node->type == Inkscape::NodePath::NODE_CUSP) {
874         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
875         node->knot->setSize (node->selected? 11 : 9);
876         sp_knot_update_ctrl(node->knot);
877     } else {
878         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
879         node->knot->setSize (node->selected? 9 : 7);
880         sp_knot_update_ctrl(node->knot);
881     }
883     // if one of handles is mouseovered, preserve its position
884     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
885         sp_node_adjust_handle(node, 1);
886     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
887         sp_node_adjust_handle(node, -1);
888     } else {
889         sp_node_adjust_handles(node);
890     }
892     sp_node_update_handles(node);
894     sp_nodepath_update_statusbar(node->subpath->nodepath);
896     return node;
899 /**
900  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
901  * adjacent segments from lines to curves.
902 */
903 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
905     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
906         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
907             // convert adjacent segment BEFORE to curve
908             node->code = NR_CURVETO;
909             NR::Point delta;
910             if (node->n.other != NULL)
911                 delta = node->n.other->pos - node->p.other->pos;
912             else
913                 delta = node->pos - node->p.other->pos;
914             node->p.pos = node->pos - delta / 4;
915             sp_node_update_handles(node);
916         }
918         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
919             // convert adjacent segment AFTER to curve
920             node->n.other->code = NR_CURVETO;
921             NR::Point delta;
922             if (node->p.other != NULL)
923                 delta = node->p.other->pos - node->n.other->pos;
924             else
925                 delta = node->pos - node->n.other->pos;
926             node->n.pos = node->pos - delta / 4;
927             sp_node_update_handles(node);
928         }
929     }
931     sp_nodepath_set_node_type (node, type);
934 /**
935  * Move node to point, and adjust its and neighbouring handles.
936  */
937 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
939     NR::Point delta = p - node->pos;
940     node->pos = p;
942     node->p.pos += delta;
943     node->n.pos += delta;
945     if (node->p.other) {
946         if (node->code == NR_LINETO) {
947             sp_node_adjust_handle(node, 1);
948             sp_node_adjust_handle(node->p.other, -1);
949         }
950     }
951     if (node->n.other) {
952         if (node->n.other->code == NR_LINETO) {
953             sp_node_adjust_handle(node, -1);
954             sp_node_adjust_handle(node->n.other, 1);
955         }
956     }
958     // this function is only called from batch movers that will update display at the end
959     // themselves, so here we just move all the knots without emitting move signals, for speed
960     sp_node_update_handles(node, false);
963 /**
964  * Call sp_node_moveto() for node selection and handle possible snapping.
965  */
966 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
967                                             bool const snap = true)
969     NR::Coord best = NR_HUGE;
970     NR::Point delta(dx, dy);
971     NR::Point best_pt = delta;
973     if (snap) {
974         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
975         
976         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
977             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
978             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
979             if (s.getDistance() < best) {
980                 best = s.getDistance();
981                 best_pt = s.getPoint() - n->pos;
982             }
983         }
984     }
986     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
987        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
988         sp_node_moveto(n, n->pos + best_pt);
989     }
991     // do not update repr here so that node dragging is acceptably fast
992     update_object(nodepath);
995 /**
996 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
997 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
998 near x = 0.
999  */
1000 double
1001 sculpt_profile (double x, double alpha)
1003     if (x >= 1)
1004         return 0;
1005     return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1008 double
1009 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1011     // extremely primitive for now, don't have time to look for the real one
1012     double lower = NR::L2(b - a);
1013     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1014     return (lower + upper)/2;
1017 void
1018 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1020     n->pos = n->origin + delta;
1021     n->n.pos = n->n.origin + delta_n;
1022     n->p.pos = n->p.origin + delta_p;
1023     sp_node_adjust_handles(n);
1024     sp_node_update_handles(n, false);
1027 /**
1028  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1029  * on how far they are from the dragged node n.
1030  */
1031 static void 
1032 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1034     g_assert (n);
1035     g_assert (nodepath);
1036     g_assert (n->subpath->nodepath == nodepath);
1038     double pressure = n->knot->pressure;
1039     if (pressure == 0)
1040         pressure = 0.5; // default
1041     pressure = CLAMP (pressure, 0.2, 0.8);
1043     // map pressure to alpha = 1/5 ... 5
1044     double alpha = 1 - 2 * fabs(pressure - 0.5);
1045     if (pressure > 0.5)
1046         alpha = 1/alpha;
1048     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1049         // Only one subpath has selected nodes:
1050         // use linear mode, where the distance from n to node being dragged is calculated along the path
1052         double n_sel_range = 0, p_sel_range = 0;
1053         guint n_nodes = 0, p_nodes = 0;
1054         guint n_sel_nodes = 0, p_sel_nodes = 0;
1056         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1057         {
1058             double n_range = 0, p_range = 0;
1059             bool n_going = true, p_going = true;
1060             Inkscape::NodePath::Node *n_node = n;
1061             Inkscape::NodePath::Node *p_node = n;
1062             do {
1063                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1064                 if (n_node && n_going)
1065                     n_node = n_node->n.other;
1066                 if (n_node == NULL) {
1067                     n_going = false;
1068                 } else {
1069                     n_nodes ++;
1070                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1071                     if (n_node->selected) {
1072                         n_sel_nodes ++;
1073                         n_sel_range = n_range;
1074                     }
1075                     if (n_node == p_node) {
1076                         n_going = false;
1077                         p_going = false;
1078                     }
1079                 }
1080                 if (p_node && p_going)
1081                     p_node = p_node->p.other;
1082                 if (p_node == NULL) {
1083                     p_going = false;
1084                 } else {
1085                     p_nodes ++;
1086                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1087                     if (p_node->selected) {
1088                         p_sel_nodes ++;
1089                         p_sel_range = p_range;
1090                     }
1091                     if (p_node == n_node) {
1092                         n_going = false;
1093                         p_going = false;
1094                     }
1095                 }
1096             } while (n_going || p_going);
1097         }
1099         // Second pass: actually move nodes in this subpath
1100         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1101         {
1102             double n_range = 0, p_range = 0;
1103             bool n_going = true, p_going = true;
1104             Inkscape::NodePath::Node *n_node = n;
1105             Inkscape::NodePath::Node *p_node = n;
1106             do {
1107                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1108                 if (n_node && n_going)
1109                     n_node = n_node->n.other;
1110                 if (n_node == NULL) {
1111                     n_going = false;
1112                 } else {
1113                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1114                     if (n_node->selected) {
1115                         sp_nodepath_move_node_and_handles (n_node, 
1116                                                            sculpt_profile (n_range / n_sel_range, alpha) * delta,
1117                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha) * delta,
1118                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha) * delta);
1119                     }
1120                     if (n_node == p_node) {
1121                         n_going = false;
1122                         p_going = false;
1123                     }
1124                 }
1125                 if (p_node && p_going)
1126                     p_node = p_node->p.other;
1127                 if (p_node == NULL) {
1128                     p_going = false;
1129                 } else {
1130                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1131                     if (p_node->selected) {
1132                         sp_nodepath_move_node_and_handles (p_node, 
1133                                                            sculpt_profile (p_range / p_sel_range, alpha) * delta,
1134                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha) * delta,
1135                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha) * delta);
1136                     }
1137                     if (p_node == n_node) {
1138                         n_going = false;
1139                         p_going = false;
1140                     }
1141                 }
1142             } while (n_going || p_going);
1143         }
1145     } else {
1146         // Multiple subpaths have selected nodes:
1147         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1148         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1149         // fix the pear-like shape when sculpting e.g. a ring
1151         // First pass: calculate range
1152         gdouble direct_range = 0;
1153         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1154             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1155             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1156                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1157                 if (node->selected) {
1158                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1159                 }
1160             }
1161         }
1163         // Second pass: actually move nodes
1164         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1165             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1166             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1167                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1168                 if (node->selected) {
1169                     if (direct_range > 1e-6) {
1170                         sp_nodepath_move_node_and_handles (node,
1171                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha) * delta,
1172                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha) * delta,
1173                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha) * delta);
1174                     } else {
1175                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1176                     }
1178                 }
1179             }
1180         }
1181     }
1183     // do not update repr here so that node dragging is acceptably fast
1184     update_object(nodepath);
1188 /**
1189  * Move node selection to point, adjust its and neighbouring handles,
1190  * handle possible snapping, and commit the change with possible undo.
1191  */
1192 void
1193 sp_node_selected_move(gdouble dx, gdouble dy)
1195     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1196     if (!nodepath) return;
1198     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1200     if (dx == 0) {
1201         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1202     } else if (dy == 0) {
1203         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1204     } else {
1205         sp_nodepath_update_repr(nodepath);
1206     }
1209 /**
1210  * Move node selection off screen and commit the change.
1211  */
1212 void
1213 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1215     // borrowed from sp_selection_move_screen in selection-chemistry.c
1216     // we find out the current zoom factor and divide deltas by it
1217     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1219     gdouble zoom = desktop->current_zoom();
1220     gdouble zdx = dx / zoom;
1221     gdouble zdy = dy / zoom;
1223     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1224     if (!nodepath) return;
1226     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1228     if (dx == 0) {
1229         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1230     } else if (dy == 0) {
1231         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1232     } else {
1233         sp_nodepath_update_repr(nodepath);
1234     }
1237 /** If they don't yet exist, creates knot and line for the given side of the node */
1238 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1240     if (!side->knot) {
1241         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"));
1243         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1244         side->knot->setSize (7);
1245         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1246         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1247         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1248         sp_knot_update_ctrl(side->knot);
1250         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1251         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1252         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1253         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1254         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1255         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1256     }
1258     if (!side->line) {
1259         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1260                                         SP_TYPE_CTRLLINE, NULL);
1261     }
1264 /**
1265  * Ensure the given handle of the node is visible/invisible, update its screen position
1266  */
1267 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1269     g_assert(node != NULL);
1271    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1272     NRPathcode code = sp_node_path_code_from_side(node, side);
1274     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1276     if (show_handle) {
1277         if (!side->knot) { // No handle knot at all
1278             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1279             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1280             side->knot->pos = side->pos;
1281             if (side->knot->item) 
1282                 SP_CTRL(side->knot->item)->moveto(side->pos);
1283             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1284             sp_knot_show(side->knot);
1285         } else {
1286             if (side->knot->pos != side->pos) { // only if it's really moved
1287                 if (fire_move_signals) {
1288                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1289                 } else {
1290                     sp_knot_moveto(side->knot, &side->pos);
1291                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1292                 }
1293             }
1294             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1295                 sp_knot_show(side->knot);
1296             }
1297         }
1298         sp_canvas_item_show(side->line);
1299     } else {
1300         if (side->knot) {
1301             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1302                 sp_knot_hide(side->knot);
1303             }
1304         }
1305         if (side->line) {
1306             sp_canvas_item_hide(side->line);
1307         }
1308     }
1311 /**
1312  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1313  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1314  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1315  * updated; otherwise, just move the knots silently (used in batch moves).
1316  */
1317 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1319     g_assert(node != NULL);
1321     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1322         sp_knot_show(node->knot);
1323     }
1325     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1326         if (fire_move_signals)
1327             sp_knot_set_position(node->knot, &node->pos, 0);
1328         else 
1329             sp_knot_moveto(node->knot, &node->pos);
1330     }
1332     gboolean show_handles = node->selected;
1333     if (node->p.other != NULL) {
1334         if (node->p.other->selected) show_handles = TRUE;
1335     }
1336     if (node->n.other != NULL) {
1337         if (node->n.other->selected) show_handles = TRUE;
1338     }
1340     if (node->subpath->nodepath->show_handles == false)
1341         show_handles = FALSE;
1343     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1344     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1347 /**
1348  * Call sp_node_update_handles() for all nodes on subpath.
1349  */
1350 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1352     g_assert(subpath != NULL);
1354     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1355         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1356     }
1359 /**
1360  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1361  */
1362 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1364     g_assert(nodepath != NULL);
1366     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1367         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1368     }
1371 void
1372 sp_nodepath_show_handles(bool show)
1374     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1375     if (nodepath == NULL) return;
1377     nodepath->show_handles = show;
1378     sp_nodepath_update_handles(nodepath);
1381 /**
1382  * Adds all selected nodes in nodepath to list.
1383  */
1384 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1386     StlConv<Node *>::list(l, selected);
1387 /// \todo this adds a copying, rework when the selection becomes a stl list
1390 /**
1391  * Align selected nodes on the specified axis.
1392  */
1393 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1395     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1396         return;
1397     }
1399     if ( !nodepath->selected->next ) { // only one node selected
1400         return;
1401     }
1402    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1403     NR::Point dest(pNode->pos);
1404     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1405         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1406         if (pNode) {
1407             dest[axis] = pNode->pos[axis];
1408             sp_node_moveto(pNode, dest);
1409         }
1410     }
1412     sp_nodepath_update_repr(nodepath);
1415 /// Helper struct.
1416 struct NodeSort
1418    Inkscape::NodePath::Node *_node;
1419     NR::Coord _coord;
1420     /// \todo use vectorof pointers instead of calling copy ctor
1421     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1422         _node(node), _coord(node->pos[axis])
1423     {}
1425 };
1427 static bool operator<(NodeSort const &a, NodeSort const &b)
1429     return (a._coord < b._coord);
1432 /**
1433  * Distribute selected nodes on the specified axis.
1434  */
1435 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1437     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1438         return;
1439     }
1441     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1442         return;
1443     }
1445    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1446     std::vector<NodeSort> sorted;
1447     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1448         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1449         if (pNode) {
1450             NodeSort n(pNode, axis);
1451             sorted.push_back(n);
1452             //dest[axis] = pNode->pos[axis];
1453             //sp_node_moveto(pNode, dest);
1454         }
1455     }
1456     std::sort(sorted.begin(), sorted.end());
1457     unsigned int len = sorted.size();
1458     //overall bboxes span
1459     float dist = (sorted.back()._coord -
1460                   sorted.front()._coord);
1461     //new distance between each bbox
1462     float step = (dist) / (len - 1);
1463     float pos = sorted.front()._coord;
1464     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1465           it < sorted.end();
1466           it ++ )
1467     {
1468         NR::Point dest((*it)._node->pos);
1469         dest[axis] = pos;
1470         sp_node_moveto((*it)._node, dest);
1471         pos += step;
1472     }
1474     sp_nodepath_update_repr(nodepath);
1478 /**
1479  * Call sp_nodepath_line_add_node() for all selected segments.
1480  */
1481 void
1482 sp_node_selected_add_node(void)
1484     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1485     if (!nodepath) {
1486         return;
1487     }
1489     GList *nl = NULL;
1491     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1492        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1493         g_assert(t->selected);
1494         if (t->p.other && t->p.other->selected) {
1495             nl = g_list_prepend(nl, t);
1496         }
1497     }
1499     while (nl) {
1500        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1501        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1502         sp_nodepath_node_select(n, TRUE, FALSE);
1503         nl = g_list_remove(nl, t);
1504     }
1506     /** \todo fixme: adjust ? */
1507     sp_nodepath_update_handles(nodepath);
1509     sp_nodepath_update_repr(nodepath);
1511     sp_nodepath_update_statusbar(nodepath);
1514 /**
1515  * Select segment nearest to point
1516  */
1517 void
1518 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1520     if (!nodepath) {
1521         return;
1522     }
1524     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1526     //find segment to segment
1527     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1529     gboolean force = FALSE;
1530     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1531         force = TRUE;
1532     }
1533     sp_nodepath_node_select(e, (gboolean) toggle, force);
1534     if (e->p.other)
1535         sp_nodepath_node_select(e->p.other, TRUE, force);
1537     sp_nodepath_update_handles(nodepath);
1539     sp_nodepath_update_statusbar(nodepath);
1542 /**
1543  * Add a node nearest to point
1544  */
1545 void
1546 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1548     if (!nodepath) {
1549         return;
1550     }
1552     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1554     //find segment to split
1555     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1557     //don't know why but t seems to flip for lines
1558     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1559         position.t = 1.0 - position.t;
1560     }
1561     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1562     sp_nodepath_node_select(n, FALSE, TRUE);
1564     /* fixme: adjust ? */
1565     sp_nodepath_update_handles(nodepath);
1567     sp_nodepath_update_repr(nodepath);
1569     sp_nodepath_update_statusbar(nodepath);
1572 /*
1573  * Adjusts a segment so that t moves by a certain delta for dragging
1574  * converts lines to curves
1575  *
1576  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1577  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1578  */
1579 void
1580 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1582     /* feel good is an arbitrary parameter that distributes the delta between handles
1583      * if t of the drag point is less than 1/6 distance form the endpoint only
1584      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1585      */
1586     double feel_good;
1587     if (t <= 1.0 / 6.0)
1588         feel_good = 0;
1589     else if (t <= 0.5)
1590         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1591     else if (t <= 5.0 / 6.0)
1592         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1593     else
1594         feel_good = 1;
1596     //if we're dragging a line convert it to a curve
1597     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1598         sp_nodepath_set_line_type(e, NR_CURVETO);
1599     }
1601     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1602     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1603     e->p.other->n.pos += offsetcoord0;
1604     e->p.pos += offsetcoord1;
1606     // adjust handles of adjacent nodes where necessary
1607     sp_node_adjust_handle(e,1);
1608     sp_node_adjust_handle(e->p.other,-1);
1610     sp_nodepath_update_handles(e->subpath->nodepath);
1612     update_object(e->subpath->nodepath);
1614     sp_nodepath_update_statusbar(e->subpath->nodepath);
1618 /**
1619  * Call sp_nodepath_break() for all selected segments.
1620  */
1621 void sp_node_selected_break()
1623     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1624     if (!nodepath) return;
1626     GList *temp = NULL;
1627     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1628        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1629        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1630         if (nn == NULL) continue; // no break, no new node
1631         temp = g_list_prepend(temp, nn);
1632     }
1634     if (temp) {
1635         sp_nodepath_deselect(nodepath);
1636     }
1637     for (GList *l = temp; l != NULL; l = l->next) {
1638         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1639     }
1641     sp_nodepath_update_handles(nodepath);
1643     sp_nodepath_update_repr(nodepath);
1646 /**
1647  * Duplicate the selected node(s).
1648  */
1649 void sp_node_selected_duplicate()
1651     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1652     if (!nodepath) {
1653         return;
1654     }
1656     GList *temp = NULL;
1657     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1658        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1659        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1660         if (nn == NULL) continue; // could not duplicate
1661         temp = g_list_prepend(temp, nn);
1662     }
1664     if (temp) {
1665         sp_nodepath_deselect(nodepath);
1666     }
1667     for (GList *l = temp; l != NULL; l = l->next) {
1668         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1669     }
1671     sp_nodepath_update_handles(nodepath);
1673     sp_nodepath_update_repr(nodepath);
1676 /**
1677  *  Join two nodes by merging them into one.
1678  */
1679 void sp_node_selected_join()
1681     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1682     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1684     if (g_list_length(nodepath->selected) != 2) {
1685         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1686         return;
1687     }
1689    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1690    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1692     g_assert(a != b);
1693     g_assert(a->p.other || a->n.other);
1694     g_assert(b->p.other || b->n.other);
1696     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1697         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1698         return;
1699     }
1701     /* a and b are endpoints */
1703     NR::Point c;
1704     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1705         c = a->pos;
1706     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1707         c = b->pos;
1708     } else {
1709         c = (a->pos + b->pos) / 2;
1710     }
1712     if (a->subpath == b->subpath) {
1713        Inkscape::NodePath::SubPath *sp = a->subpath;
1714         sp_nodepath_subpath_close(sp);
1715         sp_node_moveto (sp->first, c);
1717         sp_nodepath_update_handles(sp->nodepath);
1718         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;
1725     NR::Point p;
1726    Inkscape::NodePath::Node *n;
1727     NRPathcode code;
1728     if (a == sa->first) {
1729         p = sa->first->n.pos;
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         n = n->p.other;
1735         while (n) {
1736             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1737             n = n->p.other;
1738             if (n == sa->first) n = NULL;
1739         }
1740         sp_nodepath_subpath_destroy(sa);
1741         sa = t;
1742     } else if (a == sa->last) {
1743         p = sa->last->p.pos;
1744         code = (NRPathcode)sa->last->code;
1745         sp_nodepath_node_destroy(sa->last);
1746     } else {
1747         code = NR_END;
1748         g_assert_not_reached();
1749     }
1751     if (b == sb->first) {
1752         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1753         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1754             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1755         }
1756     } else if (b == sb->last) {
1757         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1758         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1759             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1760         }
1761     } else {
1762         g_assert_not_reached();
1763     }
1764     /* and now destroy sb */
1766     sp_nodepath_subpath_destroy(sb);
1768     sp_nodepath_update_handles(sa->nodepath);
1770     sp_nodepath_update_repr(nodepath);
1772     sp_nodepath_update_statusbar(nodepath);
1775 /**
1776  *  Join two nodes by adding a segment between them.
1777  */
1778 void sp_node_selected_join_segment()
1780     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1781     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1783     if (g_list_length(nodepath->selected) != 2) {
1784         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1785         return;
1786     }
1788    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1789    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1791     g_assert(a != b);
1792     g_assert(a->p.other || a->n.other);
1793     g_assert(b->p.other || b->n.other);
1795     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1796         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1797         return;
1798     }
1800     if (a->subpath == b->subpath) {
1801        Inkscape::NodePath::SubPath *sp = a->subpath;
1803         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1804         sp->closed = TRUE;
1806         sp->first->p.other = sp->last;
1807         sp->last->n.other  = sp->first;
1809         sp_node_handle_mirror_p_to_n(sp->last);
1810         sp_node_handle_mirror_n_to_p(sp->first);
1812         sp->first->code = sp->last->code;
1813         sp->first       = sp->last;
1815         sp_nodepath_update_handles(sp->nodepath);
1817         sp_nodepath_update_repr(nodepath);
1819         return;
1820     }
1822     /* a and b are separate subpaths */
1823    Inkscape::NodePath::SubPath *sa = a->subpath;
1824    Inkscape::NodePath::SubPath *sb = b->subpath;
1826    Inkscape::NodePath::Node *n;
1827     NR::Point p;
1828     NRPathcode code;
1829     if (a == sa->first) {
1830         code = (NRPathcode) sa->first->n.other->code;
1831        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1832         n = sa->last;
1833         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1834         for (n = n->p.other; n != NULL; n = n->p.other) {
1835             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1836         }
1837         sp_nodepath_subpath_destroy(sa);
1838         sa = t;
1839     } else if (a == sa->last) {
1840         code = (NRPathcode)sa->last->code;
1841     } else {
1842         code = NR_END;
1843         g_assert_not_reached();
1844     }
1846     if (b == sb->first) {
1847         n = sb->first;
1848         sp_node_handle_mirror_p_to_n(sa->last);
1849         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1850         sp_node_handle_mirror_n_to_p(sa->last);
1851         for (n = n->n.other; n != NULL; n = n->n.other) {
1852             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1853         }
1854     } else if (b == sb->last) {
1855         n = sb->last;
1856         sp_node_handle_mirror_p_to_n(sa->last);
1857         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1858         sp_node_handle_mirror_n_to_p(sa->last);
1859         for (n = n->p.other; n != NULL; n = n->p.other) {
1860             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1861         }
1862     } else {
1863         g_assert_not_reached();
1864     }
1865     /* and now destroy sb */
1867     sp_nodepath_subpath_destroy(sb);
1869     sp_nodepath_update_handles(sa->nodepath);
1871     sp_nodepath_update_repr(nodepath);
1874 /**
1875  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1876  */
1877 void sp_node_delete_preserve(GList *nodes_to_delete)
1879     GSList *nodepaths = NULL;
1880     
1881     while (nodes_to_delete) {
1882         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1883         Inkscape::NodePath::SubPath *sp = node->subpath;
1884         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1885         Inkscape::NodePath::Node *sample_cursor = NULL;
1886         Inkscape::NodePath::Node *sample_end = NULL;
1887         Inkscape::NodePath::Node *delete_cursor = node;
1888         bool just_delete = false;
1889         
1890         //find the start of this contiguous selection
1891         //move left to the first node that is not selected
1892         //or the start of the non-closed path
1893         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1894             delete_cursor = curr;
1895         }
1897         //just delete at the beginning of an open path
1898         if (!delete_cursor->p.other) {
1899             sample_cursor = delete_cursor;
1900             just_delete = true;
1901         } else {
1902             sample_cursor = delete_cursor->p.other;
1903         }
1904         
1905         //calculate points for each segment
1906         int rate = 5;
1907         float period = 1.0 / rate;
1908         std::vector<NR::Point> data;
1909         if (!just_delete) {
1910             data.push_back(sample_cursor->pos);
1911             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1912                 //just delete at the end of an open path
1913                 if (!sp->closed && curr->n.other == sp->last) {
1914                     just_delete = true;
1915                     break;
1916                 }
1917                 
1918                 //sample points on the contiguous selected segment
1919                 NR::Point *bez;
1920                 bez = new NR::Point [4];
1921                 bez[0] = curr->pos;
1922                 bez[1] = curr->n.pos;
1923                 bez[2] = curr->n.other->p.pos;
1924                 bez[3] = curr->n.other->pos;
1925                 for (int i=1; i<rate; i++) {
1926                     gdouble t = i * period;
1927                     NR::Point p = bezier_pt(3, bez, t);
1928                     data.push_back(p);
1929                 }
1930                 data.push_back(curr->n.other->pos);
1932                 sample_end = curr->n.other;
1933                 //break if we've come full circle or hit the end of the selection
1934                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1935                     break;
1936                 }
1937             }
1938         }
1940         if (!just_delete) {
1941             //calculate the best fitting single segment and adjust the endpoints
1942             NR::Point *adata;
1943             adata = new NR::Point [data.size()];
1944             copy(data.begin(), data.end(), adata);
1945             
1946             NR::Point *bez;
1947             bez = new NR::Point [4];
1948             //would decreasing error create a better fitting approximation?
1949             gdouble error = 1.0;
1950             gint ret;
1951             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1953             //adjust endpoints
1954             sample_cursor->n.pos = bez[1];
1955             sample_end->p.pos = bez[2];
1956         }
1957        
1958         //destroy this contiguous selection
1959         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
1960             Inkscape::NodePath::Node *temp = delete_cursor;
1961             if (delete_cursor->n.other == delete_cursor) {
1962                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
1963                 delete_cursor = NULL; 
1964             } else {
1965                 delete_cursor = delete_cursor->n.other;
1966             }
1967             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
1968             sp_nodepath_node_destroy(temp);
1969         }
1971         sp_nodepath_update_handles(nodepath);
1973         if (!g_slist_find(nodepaths, nodepath))
1974             nodepaths = g_slist_prepend (nodepaths, nodepath);
1975     }
1977     for (GSList *i = nodepaths; i; i = i->next) {
1978         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
1979         // different nodepaths will give us one undo event per nodepath
1980         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
1982         // if the entire nodepath is removed, delete the selected object.
1983         if (nodepath->subpaths == NULL ||
1984             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
1985             //at least 2
1986             sp_nodepath_get_node_count(nodepath) < 2) {
1987             SPDocument *document = sp_desktop_document (nodepath->desktop);
1988             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
1989             //delete this nodepath's object, not the entire selection! (though at this time, this
1990             //does not matter)
1991             sp_selection_delete();
1992             sp_document_done (document);
1993         } else {
1994             sp_nodepath_update_repr(nodepath);
1995             sp_nodepath_update_statusbar(nodepath);
1996         }
1997     }
1999     g_slist_free (nodepaths);
2002 /**
2003  * Delete one or more selected nodes.
2004  */
2005 void sp_node_selected_delete()
2007     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2008     if (!nodepath) return;
2009     if (!nodepath->selected) return;
2011     /** \todo fixme: do it the right way */
2012     while (nodepath->selected) {
2013        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2014         sp_nodepath_node_destroy(node);
2015     }
2018     //clean up the nodepath (such as for trivial subpaths)
2019     sp_nodepath_cleanup(nodepath);
2021     sp_nodepath_update_handles(nodepath);
2023     // if the entire nodepath is removed, delete the selected object.
2024     if (nodepath->subpaths == NULL ||
2025         sp_nodepath_get_node_count(nodepath) < 2) {
2026         SPDocument *document = sp_desktop_document (nodepath->desktop);
2027         sp_selection_delete();
2028         sp_document_done (document);
2029         return;
2030     }
2032     sp_nodepath_update_repr(nodepath);
2034     sp_nodepath_update_statusbar(nodepath);
2037 /**
2038  * Delete one or more segments between two selected nodes.
2039  * This is the code for 'split'.
2040  */
2041 void
2042 sp_node_selected_delete_segment(void)
2044    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2045    Inkscape::NodePath::Node *curr, *next;     //Iterators
2047     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2048     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2050     if (g_list_length(nodepath->selected) != 2) {
2051         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2052                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2053         return;
2054     }
2056     //Selected nodes, not inclusive
2057    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2058    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2060     if ( ( a==b)                       ||  //same node
2061          (a->subpath  != b->subpath )  ||  //not the same path
2062          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2063          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2064     {
2065         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2066                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2067         return;
2068     }
2070     //###########################################
2071     //# BEGIN EDITS
2072     //###########################################
2073     //##################################
2074     //# CLOSED PATH
2075     //##################################
2076     if (a->subpath->closed) {
2079         gboolean reversed = FALSE;
2081         //Since we can go in a circle, we need to find the shorter distance.
2082         //  a->b or b->a
2083         start = end = NULL;
2084         int distance    = 0;
2085         int minDistance = 0;
2086         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2087             if (curr==b) {
2088                 //printf("a to b:%d\n", distance);
2089                 start = a;//go from a to b
2090                 end   = b;
2091                 minDistance = distance;
2092                 //printf("A to B :\n");
2093                 break;
2094             }
2095             distance++;
2096         }
2098         //try again, the other direction
2099         distance = 0;
2100         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2101             if (curr==a) {
2102                 //printf("b to a:%d\n", distance);
2103                 if (distance < minDistance) {
2104                     start    = b;  //we go from b to a
2105                     end      = a;
2106                     reversed = TRUE;
2107                     //printf("B to A\n");
2108                 }
2109                 break;
2110             }
2111             distance++;
2112         }
2115         //Copy everything from 'end' to 'start' to a new subpath
2116        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2117         for (curr=end ; curr ; curr=curr->n.other) {
2118             NRPathcode code = (NRPathcode) curr->code;
2119             if (curr == end)
2120                 code = NR_MOVETO;
2121             sp_nodepath_node_new(t, NULL,
2122                                  (Inkscape::NodePath::NodeType)curr->type, code,
2123                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2124             if (curr == start)
2125                 break;
2126         }
2127         sp_nodepath_subpath_destroy(a->subpath);
2130     }
2134     //##################################
2135     //# OPEN PATH
2136     //##################################
2137     else {
2139         //We need to get the direction of the list between A and B
2140         //Can we walk from a to b?
2141         start = end = NULL;
2142         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2143             if (curr==b) {
2144                 start = a;  //did it!  we go from a to b
2145                 end   = b;
2146                 //printf("A to B\n");
2147                 break;
2148             }
2149         }
2150         if (!start) {//didn't work?  let's try the other direction
2151             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2152                 if (curr==a) {
2153                     start = b;  //did it!  we go from b to a
2154                     end   = a;
2155                     //printf("B to A\n");
2156                     break;
2157                 }
2158             }
2159         }
2160         if (!start) {
2161             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2162                                                      _("Cannot find path between nodes."));
2163             return;
2164         }
2168         //Copy everything after 'end' to a new subpath
2169        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2170         for (curr=end ; curr ; curr=curr->n.other) {
2171             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2172                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2173         }
2175         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2176         for (curr = start->n.other ; curr  ; curr=next) {
2177             next = curr->n.other;
2178             sp_nodepath_node_destroy(curr);
2179         }
2181     }
2182     //###########################################
2183     //# END EDITS
2184     //###########################################
2186     //clean up the nodepath (such as for trivial subpaths)
2187     sp_nodepath_cleanup(nodepath);
2189     sp_nodepath_update_handles(nodepath);
2191     sp_nodepath_update_repr(nodepath);
2193     sp_nodepath_update_statusbar(nodepath);
2196 /**
2197  * Call sp_nodepath_set_line() for all selected segments.
2198  */
2199 void
2200 sp_node_selected_set_line_type(NRPathcode code)
2202     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2203     if (nodepath == NULL) return;
2205     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2206        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2207         g_assert(n->selected);
2208         if (n->p.other && n->p.other->selected) {
2209             sp_nodepath_set_line_type(n, code);
2210         }
2211     }
2213     sp_nodepath_update_repr(nodepath);
2216 /**
2217  * Call sp_nodepath_convert_node_type() for all selected nodes.
2218  */
2219 void
2220 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2222     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2223     if (nodepath == NULL) return;
2225     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2226         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2227     }
2229     sp_nodepath_update_repr(nodepath);
2232 /**
2233  * Change select status of node, update its own and neighbour handles.
2234  */
2235 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2237     node->selected = selected;
2239     if (selected) {
2240         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2241         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2242         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2243         sp_knot_update_ctrl(node->knot);
2244     } else {
2245         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2246         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2247         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2248         sp_knot_update_ctrl(node->knot);
2249     }
2251     sp_node_update_handles(node);
2252     if (node->n.other) sp_node_update_handles(node->n.other);
2253     if (node->p.other) sp_node_update_handles(node->p.other);
2256 /**
2257 \brief Select a node
2258 \param node     The node to select
2259 \param incremental   If true, add to selection, otherwise deselect others
2260 \param override   If true, always select this node, otherwise toggle selected status
2261 */
2262 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2264     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2266     if (incremental) {
2267         if (override) {
2268             if (!g_list_find(nodepath->selected, node)) {
2269                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2270             }
2271             sp_node_set_selected(node, TRUE);
2272         } else { // toggle
2273             if (node->selected) {
2274                 g_assert(g_list_find(nodepath->selected, node));
2275                 nodepath->selected = g_list_remove(nodepath->selected, node);
2276             } else {
2277                 g_assert(!g_list_find(nodepath->selected, node));
2278                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2279             }
2280             sp_node_set_selected(node, !node->selected);
2281         }
2282     } else {
2283         sp_nodepath_deselect(nodepath);
2284         nodepath->selected = g_list_prepend(nodepath->selected, node);
2285         sp_node_set_selected(node, TRUE);
2286     }
2288     sp_nodepath_update_statusbar(nodepath);
2292 /**
2293 \brief Deselect all nodes in the nodepath
2294 */
2295 void
2296 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2298     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2300     while (nodepath->selected) {
2301         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2302         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2303     }
2304     sp_nodepath_update_statusbar(nodepath);
2307 /**
2308 \brief Select or invert selection of all nodes in the nodepath
2309 */
2310 void
2311 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2313     if (!nodepath) return;
2315     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2316        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2317         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2318            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2319            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2320         }
2321     }
2324 /**
2325  * If nothing selected, does the same as sp_nodepath_select_all();
2326  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2327  * (i.e., similar to "select all in layer", with the "selected" subpaths
2328  * being treated as "layers" in the path).
2329  */
2330 void
2331 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2333     if (!nodepath) return;
2335     if (g_list_length (nodepath->selected) == 0) {
2336         sp_nodepath_select_all (nodepath, invert);
2337         return;
2338     }
2340     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2341     GSList *subpaths = NULL;
2343     for (GList *l = copy; l != NULL; l = l->next) {
2344         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2345         Inkscape::NodePath::SubPath *subpath = n->subpath;
2346         if (!g_slist_find (subpaths, subpath))
2347             subpaths = g_slist_prepend (subpaths, subpath);
2348     }
2350     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2351         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2352         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2353             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2354             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2355         }
2356     }
2358     g_slist_free (subpaths);
2359     g_list_free (copy);
2362 /**
2363  * \brief Select the node after the last selected; if none is selected,
2364  * select the first within path.
2365  */
2366 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2368     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2370    Inkscape::NodePath::Node *last = NULL;
2371     if (nodepath->selected) {
2372         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2373            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2374             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2375             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2376                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2377                 if (node->selected) {
2378                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2379                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2380                             if (spl->next) { // there's a next subpath
2381                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2382                                 last = subpath_next->first;
2383                             } else if (spl->prev) { // there's a previous subpath
2384                                 last = NULL; // to be set later to the first node of first subpath
2385                             } else {
2386                                 last = node->n.other;
2387                             }
2388                         } else {
2389                             last = node->n.other;
2390                         }
2391                     } else {
2392                         if (node->n.other) {
2393                             last = node->n.other;
2394                         } else {
2395                             if (spl->next) { // there's a next subpath
2396                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2397                                 last = subpath_next->first;
2398                             } else if (spl->prev) { // there's a previous subpath
2399                                 last = NULL; // to be set later to the first node of first subpath
2400                             } else {
2401                                 last = (Inkscape::NodePath::Node *) subpath->first;
2402                             }
2403                         }
2404                     }
2405                 }
2406             }
2407         }
2408         sp_nodepath_deselect(nodepath);
2409     }
2411     if (last) { // there's at least one more node after selected
2412         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2413     } else { // no more nodes, select the first one in first subpath
2414        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2415         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2416     }
2419 /**
2420  * \brief Select the node before the first selected; if none is selected,
2421  * select the last within path
2422  */
2423 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2425     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2427    Inkscape::NodePath::Node *last = NULL;
2428     if (nodepath->selected) {
2429         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2430            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2431             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2432                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2433                 if (node->selected) {
2434                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2435                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2436                             if (spl->prev) { // there's a prev subpath
2437                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2438                                 last = subpath_prev->last;
2439                             } else if (spl->next) { // there's a next subpath
2440                                 last = NULL; // to be set later to the last node of last subpath
2441                             } else {
2442                                 last = node->p.other;
2443                             }
2444                         } else {
2445                             last = node->p.other;
2446                         }
2447                     } else {
2448                         if (node->p.other) {
2449                             last = node->p.other;
2450                         } else {
2451                             if (spl->prev) { // there's a prev subpath
2452                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2453                                 last = subpath_prev->last;
2454                             } else if (spl->next) { // there's a next subpath
2455                                 last = NULL; // to be set later to the last node of last subpath
2456                             } else {
2457                                 last = (Inkscape::NodePath::Node *) subpath->last;
2458                             }
2459                         }
2460                     }
2461                 }
2462             }
2463         }
2464         sp_nodepath_deselect(nodepath);
2465     }
2467     if (last) { // there's at least one more node before selected
2468         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2469     } else { // no more nodes, select the last one in last subpath
2470         GList *spl = g_list_last(nodepath->subpaths);
2471        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2472         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2473     }
2476 /**
2477  * \brief Select all nodes that are within the rectangle.
2478  */
2479 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2481     if (!incremental) {
2482         sp_nodepath_deselect(nodepath);
2483     }
2485     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2486        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2487         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2488            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2490             if (b.contains(node->pos)) {
2491                 sp_nodepath_node_select(node, TRUE, TRUE);
2492             }
2493         }
2494     }
2498 /**
2499 \brief  Saves all nodes' and handles' current positions in their origin members
2500 */
2501 void
2502 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2504     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2505        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2506         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2507            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2508            n->origin = n->pos;
2509            n->p.origin = n->p.pos;
2510            n->n.origin = n->n.pos;
2511         }
2512     }
2513
2515 /**
2516 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2517 */
2518 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2520     if (!nodepath->selected) {
2521         return NULL;
2522     }
2524     GList *r = NULL;
2525     guint i = 0;
2526     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2527        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2528         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2529            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2530             i++;
2531             if (node->selected) {
2532                 r = g_list_append(r, GINT_TO_POINTER(i));
2533             }
2534         }
2535     }
2536     return r;
2539 /**
2540 \brief  Restores selection by selecting nodes whose positions are in the list
2541 */
2542 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2544     sp_nodepath_deselect(nodepath);
2546     guint i = 0;
2547     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2548        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2549         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2550            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2551             i++;
2552             if (g_list_find(r, GINT_TO_POINTER(i))) {
2553                 sp_nodepath_node_select(node, TRUE, TRUE);
2554             }
2555         }
2556     }
2560 /**
2561 \brief Adjusts handle according to node type and line code.
2562 */
2563 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2565     double len, otherlen, linelen;
2567     g_assert(node);
2569    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2570    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2572     /** \todo fixme: */
2573     if (me->other == NULL) return;
2574     if (other->other == NULL) return;
2576     /* I have line */
2578     NRPathcode mecode, ocode;
2579     if (which_adjust == 1) {
2580         mecode = (NRPathcode)me->other->code;
2581         ocode = (NRPathcode)node->code;
2582     } else {
2583         mecode = (NRPathcode)node->code;
2584         ocode = (NRPathcode)other->other->code;
2585     }
2587     if (mecode == NR_LINETO) return;
2589     /* I am curve */
2591     if (other->other == NULL) return;
2593     /* Other has line */
2595     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2597     NR::Point delta;
2598     if (ocode == NR_LINETO) {
2599         /* other is lineto, we are either smooth or symm */
2600        Inkscape::NodePath::Node *othernode = other->other;
2601         len = NR::L2(me->pos - node->pos);
2602         delta = node->pos - othernode->pos;
2603         linelen = NR::L2(delta);
2604         if (linelen < 1e-18) 
2605             return;
2606         me->pos = node->pos + (len / linelen)*delta;
2607         return;
2608     }
2610     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2612         me->pos = 2 * node->pos - other->pos;
2613         return;
2614     }
2616     /* We are smooth */
2618     len = NR::L2(me->pos - node->pos);
2619     delta = other->pos - node->pos;
2620     otherlen = NR::L2(delta);
2621     if (otherlen < 1e-18) return;
2623     me->pos = node->pos - (len / otherlen) * delta;
2626 /**
2627  \brief Adjusts both handles according to node type and line code
2628  */
2629 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2631     g_assert(node);
2633     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2635     /* we are either smooth or symm */
2637     if (node->p.other == NULL) return;
2639     if (node->n.other == NULL) return;
2641     if (node->code == NR_LINETO) {
2642         if (node->n.other->code == NR_LINETO) return;
2643         sp_node_adjust_handle(node, 1);
2644         return;
2645     }
2647     if (node->n.other->code == NR_LINETO) {
2648         if (node->code == NR_LINETO) return;
2649         sp_node_adjust_handle(node, -1);
2650         return;
2651     }
2653     /* both are curves */
2654     NR::Point const delta( node->n.pos - node->p.pos );
2656     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2657         node->p.pos = node->pos - delta / 2;
2658         node->n.pos = node->pos + delta / 2;
2659         return;
2660     }
2662     /* We are smooth */
2663     double plen = NR::L2(node->p.pos - node->pos);
2664     if (plen < 1e-18) return;
2665     double nlen = NR::L2(node->n.pos - node->pos);
2666     if (nlen < 1e-18) return;
2667     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2668     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2671 /**
2672  * Node event callback.
2673  */
2674 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2676     gboolean ret = FALSE;
2677     switch (event->type) {
2678         case GDK_ENTER_NOTIFY:
2679             active_node = n;
2680             break;
2681         case GDK_LEAVE_NOTIFY:
2682             active_node = NULL;
2683             break;
2684         case GDK_KEY_PRESS:
2685             switch (get_group0_keyval (&event->key)) {
2686                 case GDK_space:
2687                     if (event->key.state & GDK_BUTTON1_MASK) {
2688                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2689                         stamp_repr(nodepath);
2690                         ret = TRUE;
2691                     }
2692                     break;
2693                 default:
2694                     break;
2695             }
2696             break;
2697         default:
2698             break;
2699     }
2701     return ret;
2704 /**
2705  * Handle keypress on node; directly called.
2706  */
2707 gboolean node_key(GdkEvent *event)
2709     Inkscape::NodePath::Path *np;
2711     // there is no way to verify nodes so set active_node to nil when deleting!!
2712     if (active_node == NULL) return FALSE;
2714     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2715         gint ret = FALSE;
2716         switch (get_group0_keyval (&event->key)) {
2717             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2718             case GDK_BackSpace:
2719                 np = active_node->subpath->nodepath;
2720                 sp_nodepath_node_destroy(active_node);
2721                 sp_nodepath_update_repr(np);
2722                 active_node = NULL;
2723                 ret = TRUE;
2724                 break;
2725             case GDK_c:
2726                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2727                 ret = TRUE;
2728                 break;
2729             case GDK_s:
2730                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2731                 ret = TRUE;
2732                 break;
2733             case GDK_y:
2734                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2735                 ret = TRUE;
2736                 break;
2737             case GDK_b:
2738                 sp_nodepath_node_break(active_node);
2739                 ret = TRUE;
2740                 break;
2741         }
2742         return ret;
2743     }
2744     return FALSE;
2747 /**
2748  * Mouseclick on node callback.
2749  */
2750 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2752    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2754     if (state & GDK_CONTROL_MASK) {
2755         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2757         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2758             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2759                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2760             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2761                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2762             } else {
2763                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2764             }
2765             sp_nodepath_update_repr(nodepath);
2766             sp_nodepath_update_statusbar(nodepath);
2768         } else { //ctrl+alt+click: delete node
2769             GList *node_to_delete = NULL;
2770             node_to_delete = g_list_append(node_to_delete, n);
2771             sp_node_delete_preserve(node_to_delete);
2772         }
2774     } else {
2775         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2776     }
2779 /**
2780  * Mouse grabbed node callback.
2781  */
2782 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2784    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2786     if (!n->selected) {
2787         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2788     }
2790     sp_nodepath_remember_origins (n->subpath->nodepath);
2793 /**
2794  * Mouse ungrabbed node callback.
2795  */
2796 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2798    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2800    n->dragging_out = NULL;
2802    sp_nodepath_update_repr(n->subpath->nodepath);
2805 /**
2806  * The point on a line, given by its angle, closest to the given point.
2807  * \param p  A point.
2808  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2809  * \param closest  Pointer to the point struct where the result is stored.
2810  * \todo FIXME: use dot product perhaps?
2811  */
2812 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2814     if (a == HUGE_VAL) { // vertical
2815         *closest = NR::Point(0, (*p)[NR::Y]);
2816     } else {
2817         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2818         (*closest)[NR::Y] = a * (*closest)[NR::X];
2819     }
2822 /**
2823  * Distance from the point to a line given by its angle.
2824  * \param p  A point.
2825  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2826  */
2827 static double point_line_distance(NR::Point *p, double a)
2829     NR::Point c;
2830     point_line_closest(p, a, &c);
2831     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]));
2834 /**
2835  * Callback for node "request" signal.
2836  * \todo fixme: This goes to "moved" event? (lauris)
2837  */
2838 static gboolean
2839 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2841     double yn, xn, yp, xp;
2842     double an, ap, na, pa;
2843     double d_an, d_ap, d_na, d_pa;
2844     gboolean collinear = FALSE;
2845     NR::Point c;
2846     NR::Point pr;
2848    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2850    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2851    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2853        NR::Point mouse = (*p);
2855        if (!n->dragging_out) {
2856            // This is the first drag-out event; find out which handle to drag out
2857            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2858            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2860            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2861                return FALSE;
2863            Inkscape::NodePath::NodeSide *opposite;
2864            if (appr_p > appr_n) { // closer to p
2865                n->dragging_out = &n->p;
2866                opposite = &n->n;
2867                n->code = NR_CURVETO;
2868            } else if (appr_p < appr_n) { // closer to n
2869                n->dragging_out = &n->n;
2870                opposite = &n->p;
2871                n->n.other->code = NR_CURVETO;
2872            } else { // p and n nodes are the same
2873                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2874                    n->dragging_out = &n->p;
2875                    opposite = &n->n;
2876                    n->code = NR_CURVETO;
2877                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2878                    n->dragging_out = &n->n;
2879                    opposite = &n->p;
2880                    n->n.other->code = NR_CURVETO;
2881                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2882                    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);
2883                    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);
2884                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2885                        n->dragging_out = &n->n;
2886                        opposite = &n->p;
2887                        n->n.other->code = NR_CURVETO;
2888                    } else { // closer to other's n handle
2889                        n->dragging_out = &n->p;
2890                        opposite = &n->n;
2891                        n->code = NR_CURVETO;
2892                    }
2893                }
2894            }
2896            // if there's another handle, make sure the one we drag out starts parallel to it
2897            if (opposite->pos != n->pos) {
2898                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2899            }
2901            // knots might not be created yet!
2902            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2903            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2904        }
2906        // pass this on to the handle-moved callback
2907        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2908        sp_node_update_handles(n);
2909        return TRUE;
2910    }
2912     if (state & GDK_CONTROL_MASK) { // constrained motion
2914         // calculate relative distances of handles
2915         // n handle:
2916         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2917         xn = n->n.pos[NR::X] - n->pos[NR::X];
2918         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2919         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2920             if (n->n.other) { // if there is the next point
2921                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2922                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2923                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2924             }
2925         }
2926         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2927         if (yn < 0) { xn = -xn; yn = -yn; }
2929         // p handle:
2930         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2931         xp = n->p.pos[NR::X] - n->pos[NR::X];
2932         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2933         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2934             if (n->p.other) {
2935                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2936                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2937                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2938             }
2939         }
2940         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2941         if (yp < 0) { xp = -xp; yp = -yp; }
2943         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2944             // sliding on handles, only if at least one of the handles is non-vertical
2945             // (otherwise it's the same as ctrl+drag anyway)
2947             // calculate angles of the handles
2948             if (xn == 0) {
2949                 if (yn == 0) { // no handle, consider it the continuation of the other one
2950                     an = 0;
2951                     collinear = TRUE;
2952                 }
2953                 else an = 0; // vertical; set the angle to horizontal
2954             } else an = yn/xn;
2956             if (xp == 0) {
2957                 if (yp == 0) { // no handle, consider it the continuation of the other one
2958                     ap = an;
2959                 }
2960                 else ap = 0; // vertical; set the angle to horizontal
2961             } else  ap = yp/xp;
2963             if (collinear) an = ap;
2965             // angles of the perpendiculars; HUGE_VAL means vertical
2966             if (an == 0) na = HUGE_VAL; else na = -1/an;
2967             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2969             // mouse point relative to the node's original pos
2970             pr = (*p) - n->origin;
2972             // distances to the four lines (two handles and two perpendiculars)
2973             d_an = point_line_distance(&pr, an);
2974             d_na = point_line_distance(&pr, na);
2975             d_ap = point_line_distance(&pr, ap);
2976             d_pa = point_line_distance(&pr, pa);
2978             // find out which line is the closest, save its closest point in c
2979             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2980                 point_line_closest(&pr, an, &c);
2981             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2982                 point_line_closest(&pr, ap, &c);
2983             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2984                 point_line_closest(&pr, na, &c);
2985             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2986                 point_line_closest(&pr, pa, &c);
2987             }
2989             // move the node to the closest point
2990             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2991                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2992                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2994         } else {  // constraining to hor/vert
2996             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2997                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2998             } else { // snap to vert
2999                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3000             }
3001         }
3002     } else { // move freely
3003         if (state & GDK_MOD1_MASK) { // sculpt
3004             sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3005         } else {
3006             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3007                                         (*p)[NR::X] - n->pos[NR::X],
3008                                         (*p)[NR::Y] - n->pos[NR::Y],
3009                                         (state & GDK_SHIFT_MASK) == 0);
3010         }
3011     }
3013     n->subpath->nodepath->desktop->scroll_to_point(p);
3015     return TRUE;
3018 /**
3019  * Node handle clicked callback.
3020  */
3021 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3023    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3025     if (state & GDK_CONTROL_MASK) { // "delete" handle
3026         if (n->p.knot == knot) {
3027             n->p.pos = n->pos;
3028         } else if (n->n.knot == knot) {
3029             n->n.pos = n->pos;
3030         }
3031         sp_node_update_handles(n);
3032         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3033         sp_nodepath_update_repr(nodepath);
3034         sp_nodepath_update_statusbar(nodepath);
3036     } else { // just select or add to selection, depending in Shift
3037         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3038     }
3041 /**
3042  * Node handle grabbed callback.
3043  */
3044 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3046    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3048     if (!n->selected) {
3049         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3050     }
3052     // remember the origin point of the handle
3053     if (n->p.knot == knot) {
3054         n->p.origin_radial = n->p.pos - n->pos;
3055     } else if (n->n.knot == knot) {
3056         n->n.origin_radial = n->n.pos - n->pos;
3057     } else {
3058         g_assert_not_reached();
3059     }
3063 /**
3064  * Node handle ungrabbed callback.
3065  */
3066 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3068    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3070     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3071     if (n->p.knot == knot) {
3072         n->p.origin_radial.a = 0;
3073         sp_knot_set_position(knot, &n->p.pos, state);
3074     } else if (n->n.knot == knot) {
3075         n->n.origin_radial.a = 0;
3076         sp_knot_set_position(knot, &n->n.pos, state);
3077     } else {
3078         g_assert_not_reached();
3079     }
3081     sp_nodepath_update_repr(n->subpath->nodepath);
3084 /**
3085  * Node handle "request" signal callback.
3086  */
3087 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3089     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3091     Inkscape::NodePath::NodeSide *me, *opposite;
3092     gint which;
3093     if (n->p.knot == knot) {
3094         me = &n->p;
3095         opposite = &n->n;
3096         which = -1;
3097     } else if (n->n.knot == knot) {
3098         me = &n->n;
3099         opposite = &n->p;
3100         which = 1;
3101     } else {
3102         me = opposite = NULL;
3103         which = 0;
3104         g_assert_not_reached();
3105     }
3107     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3109     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3111     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3112         /* We are smooth node adjacent with line */
3113         NR::Point const delta = *p - n->pos;
3114         NR::Coord const len = NR::L2(delta);
3115         Inkscape::NodePath::Node *othernode = opposite->other;
3116         NR::Point const ndelta = n->pos - othernode->pos;
3117         NR::Coord const linelen = NR::L2(ndelta);
3118         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3119             NR::Coord const scal = dot(delta, ndelta) / linelen;
3120             (*p) = n->pos + (scal / linelen) * ndelta;
3121         }
3122         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3123     } else {
3124         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3125     }
3127     sp_node_adjust_handle(n, -which);
3129     return FALSE;
3132 /**
3133  * Node handle moved callback.
3134  */
3135 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3137    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3139    Inkscape::NodePath::NodeSide *me;
3140    Inkscape::NodePath::NodeSide *other;
3141     if (n->p.knot == knot) {
3142         me = &n->p;
3143         other = &n->n;
3144     } else if (n->n.knot == knot) {
3145         me = &n->n;
3146         other = &n->p;
3147     } else {
3148         me = NULL;
3149         other = NULL;
3150         g_assert_not_reached();
3151     }
3153     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3154     Radial rme(me->pos - n->pos);
3155     Radial rother(other->pos - n->pos);
3156     Radial rnew(*p - n->pos);
3158     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3159         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3160         /* 0 interpreted as "no snapping". */
3162         // The closest PI/snaps angle, starting from zero.
3163         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3164         if (me->origin_radial.a == HUGE_VAL) {
3165             // ortho doesn't exist: original handle was zero length.
3166             rnew.a = a_snapped;
3167         } else {
3168             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3169              * its opposite and perpendiculars). */
3170             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3172             // Snap to the closest.
3173             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3174                        ? a_snapped
3175                        : a_ortho );
3176         }
3177     }
3179     if (state & GDK_MOD1_MASK) {
3180         // lock handle length
3181         rnew.r = me->origin_radial.r;
3182     }
3184     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3185         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3186         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3187         rother.a += rnew.a - rme.a;
3188         other->pos = NR::Point(rother) + n->pos;
3189         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3190         sp_knot_set_position(other->knot, &other->pos, 0);
3191     }
3193     me->pos = NR::Point(rnew) + n->pos;
3194     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3196     // this is what sp_knot_set_position does, but without emitting the signal:
3197     // we cannot emit a "moved" signal because we're now processing it
3198     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3200     knot->desktop->set_coordinate_status(me->pos);
3202     update_object(n->subpath->nodepath);
3204     /* status text */
3205     SPDesktop *desktop = n->subpath->nodepath->desktop;
3206     if (!desktop) return;
3207     SPEventContext *ec = desktop->event_context;
3208     if (!ec) return;
3209     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3210     if (!mc) return;
3212     double degrees = 180 / M_PI * rnew.a;
3213     if (degrees > 180) degrees -= 360;
3214     if (degrees < -180) degrees += 360;
3215     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3216         degrees = angle_to_compass (degrees);
3218     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3220     mc->setF(Inkscape::NORMAL_MESSAGE,
3221          _("<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);
3223     g_string_free(length, TRUE);
3226 /**
3227  * Node handle event callback.
3228  */
3229 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3231     gboolean ret = FALSE;
3232     switch (event->type) {
3233         case GDK_KEY_PRESS:
3234             switch (get_group0_keyval (&event->key)) {
3235                 case GDK_space:
3236                     if (event->key.state & GDK_BUTTON1_MASK) {
3237                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3238                         stamp_repr(nodepath);
3239                         ret = TRUE;
3240                     }
3241                     break;
3242                 default:
3243                     break;
3244             }
3245             break;
3246         default:
3247             break;
3248     }
3250     return ret;
3253 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3254                                  Radial &rme, Radial &rother, gboolean const both)
3256     rme.a += angle;
3257     if ( both
3258          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3259          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3260     {
3261         rother.a += angle;
3262     }
3265 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3266                                         Radial &rme, Radial &rother, gboolean const both)
3268     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3270     gdouble r;
3271     if ( both
3272          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3273          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3274     {
3275         r = MAX(rme.r, rother.r);
3276     } else {
3277         r = rme.r;
3278     }
3280     gdouble const weird_angle = atan2(norm_angle, r);
3281 /* Bulia says norm_angle is just the visible distance that the
3282  * object's end must travel on the screen.  Left as 'angle' for want of
3283  * a better name.*/
3285     rme.a += weird_angle;
3286     if ( both
3287          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3288          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3289     {
3290         rother.a += weird_angle;
3291     }
3294 /**
3295  * Rotate one node.
3296  */
3297 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3299     Inkscape::NodePath::NodeSide *me, *other;
3300     bool both = false;
3302     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3303     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3305     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3306         me = &(n->p);
3307         other = &(n->n);
3308     } else if (!n->p.other) {
3309         me = &(n->n);
3310         other = &(n->p);
3311     } else {
3312         if (which > 0) { // right handle
3313             if (xn > xp) {
3314                 me = &(n->n);
3315                 other = &(n->p);
3316             } else {
3317                 me = &(n->p);
3318                 other = &(n->n);
3319             }
3320         } else if (which < 0){ // left handle
3321             if (xn <= xp) {
3322                 me = &(n->n);
3323                 other = &(n->p);
3324             } else {
3325                 me = &(n->p);
3326                 other = &(n->n);
3327             }
3328         } else { // both handles
3329             me = &(n->n);
3330             other = &(n->p);
3331             both = true;
3332         }
3333     }
3335     Radial rme(me->pos - n->pos);
3336     Radial rother(other->pos - n->pos);
3338     if (screen) {
3339         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3340     } else {
3341         node_rotate_one_internal (*n, angle, rme, rother, both);
3342     }
3344     me->pos = n->pos + NR::Point(rme);
3346     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3347         other->pos =  n->pos + NR::Point(rother);
3348     }
3350     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3351     // so here we just move all the knots without emitting move signals, for speed
3352     sp_node_update_handles(n, false);
3355 /**
3356  * Rotate selected nodes.
3357  */
3358 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3360     if (!nodepath || !nodepath->selected) return;
3362     if (g_list_length(nodepath->selected) == 1) {
3363        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3364         node_rotate_one (n, angle, which, screen);
3365     } else {
3366        // rotate as an object:
3368         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3369         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3370         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3371             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3372             box.expandTo (n->pos); // contain all selected nodes
3373         }
3375         gdouble rot;
3376         if (screen) {
3377             gdouble const zoom = nodepath->desktop->current_zoom();
3378             gdouble const zmove = angle / zoom;
3379             gdouble const r = NR::L2(box.max() - box.midpoint());
3380             rot = atan2(zmove, r);
3381         } else {
3382             rot = angle;
3383         }
3385         NR::Matrix t =
3386             NR::Matrix (NR::translate(-box.midpoint())) *
3387             NR::Matrix (NR::rotate(rot)) *
3388             NR::Matrix (NR::translate(box.midpoint()));
3390         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3391             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3392             n->pos *= t;
3393             n->n.pos *= t;
3394             n->p.pos *= t;
3395             sp_node_update_handles(n, false);
3396         }
3397     }
3399     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3402 /**
3403  * Scale one node.
3404  */
3405 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3407     bool both = false;
3408     Inkscape::NodePath::NodeSide *me, *other;
3410     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3411     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3413     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3414         me = &(n->p);
3415         other = &(n->n);
3416         n->code = NR_CURVETO;
3417     } else if (!n->p.other) {
3418         me = &(n->n);
3419         other = &(n->p);
3420         if (n->n.other)
3421             n->n.other->code = NR_CURVETO;
3422     } else {
3423         if (which > 0) { // right handle
3424             if (xn > xp) {
3425                 me = &(n->n);
3426                 other = &(n->p);
3427                 if (n->n.other)
3428                     n->n.other->code = NR_CURVETO;
3429             } else {
3430                 me = &(n->p);
3431                 other = &(n->n);
3432                 n->code = NR_CURVETO;
3433             }
3434         } else if (which < 0){ // left handle
3435             if (xn <= xp) {
3436                 me = &(n->n);
3437                 other = &(n->p);
3438                 if (n->n.other)
3439                     n->n.other->code = NR_CURVETO;
3440             } else {
3441                 me = &(n->p);
3442                 other = &(n->n);
3443                 n->code = NR_CURVETO;
3444             }
3445         } else { // both handles
3446             me = &(n->n);
3447             other = &(n->p);
3448             both = true;
3449             n->code = NR_CURVETO;
3450             if (n->n.other)
3451                 n->n.other->code = NR_CURVETO;
3452         }
3453     }
3455     Radial rme(me->pos - n->pos);
3456     Radial rother(other->pos - n->pos);
3458     rme.r += grow;
3459     if (rme.r < 0) rme.r = 0;
3460     if (rme.a == HUGE_VAL) {
3461         if (me->other) { // if direction is unknown, initialize it towards the next node
3462             Radial rme_next(me->other->pos - n->pos);
3463             rme.a = rme_next.a;
3464         } else { // if there's no next, initialize to 0
3465             rme.a = 0;
3466         }
3467     }
3468     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3469         rother.r += grow;
3470         if (rother.r < 0) rother.r = 0;
3471         if (rother.a == HUGE_VAL) {
3472             rother.a = rme.a + M_PI;
3473         }
3474     }
3476     me->pos = n->pos + NR::Point(rme);
3478     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3479         other->pos = n->pos + NR::Point(rother);
3480     }
3482     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3483     // so here we just move all the knots without emitting move signals, for speed
3484     sp_node_update_handles(n, false);
3487 /**
3488  * Scale selected nodes.
3489  */
3490 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3492     if (!nodepath || !nodepath->selected) return;
3494     if (g_list_length(nodepath->selected) == 1) {
3495         // scale handles of the single selected node
3496         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3497         node_scale_one (n, grow, which);
3498     } else {
3499         // scale nodes as an "object":
3501         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3502         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3503         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3504             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3505             box.expandTo (n->pos); // contain all selected nodes
3506         }
3508         double scale = (box.maxExtent() + grow)/box.maxExtent();
3510         NR::Matrix t =
3511             NR::Matrix (NR::translate(-box.midpoint())) *
3512             NR::Matrix (NR::scale(scale, scale)) *
3513             NR::Matrix (NR::translate(box.midpoint()));
3515         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3516             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3517             n->pos *= t;
3518             n->n.pos *= t;
3519             n->p.pos *= t;
3520             sp_node_update_handles(n, false);
3521         }
3522     }
3524     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3527 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3529     if (!nodepath) return;
3530     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3533 /**
3534  * Flip selected nodes horizontally/vertically.
3535  */
3536 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3538     if (!nodepath || !nodepath->selected) return;
3540     if (g_list_length(nodepath->selected) == 1) {
3541         // flip handles of the single selected node
3542         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3543         double temp = n->p.pos[axis];
3544         n->p.pos[axis] = n->n.pos[axis];
3545         n->n.pos[axis] = temp;
3546         sp_node_update_handles(n, false);
3547     } else {
3548         // scale nodes as an "object":
3550         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3551         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3552         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3553             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3554             box.expandTo (n->pos); // contain all selected nodes
3555         }
3557         NR::Matrix t =
3558             NR::Matrix (NR::translate(-box.midpoint())) *
3559             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3560             NR::Matrix (NR::translate(box.midpoint()));
3562         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3563             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3564             n->pos *= t;
3565             n->n.pos *= t;
3566             n->p.pos *= t;
3567             sp_node_update_handles(n, false);
3568         }
3569     }
3571     sp_nodepath_update_repr(nodepath);
3574 //-----------------------------------------------
3575 /**
3576  * Return new subpath under given nodepath.
3577  */
3578 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3580     g_assert(nodepath);
3581     g_assert(nodepath->desktop);
3583    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3585     s->nodepath = nodepath;
3586     s->closed = FALSE;
3587     s->nodes = NULL;
3588     s->first = NULL;
3589     s->last = NULL;
3591     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3592     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3593     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3595     return s;
3598 /**
3599  * Destroy nodes in subpath, then subpath itself.
3600  */
3601 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3603     g_assert(subpath);
3604     g_assert(subpath->nodepath);
3605     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3607     while (subpath->nodes) {
3608         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3609     }
3611     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3613     g_free(subpath);
3616 /**
3617  * Link head to tail in subpath.
3618  */
3619 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3621     g_assert(!sp->closed);
3622     g_assert(sp->last != sp->first);
3623     g_assert(sp->first->code == NR_MOVETO);
3625     sp->closed = TRUE;
3627     //Link the head to the tail
3628     sp->first->p.other = sp->last;
3629     sp->last->n.other  = sp->first;
3630     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3631     sp->first          = sp->last;
3633     //Remove the extra end node
3634     sp_nodepath_node_destroy(sp->last->n.other);
3637 /**
3638  * Open closed (loopy) subpath at node.
3639  */
3640 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3642     g_assert(sp->closed);
3643     g_assert(n->subpath == sp);
3644     g_assert(sp->first == sp->last);
3646     /* We create new startpoint, current node will become last one */
3648    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3649                                                 &n->pos, &n->pos, &n->n.pos);
3652     sp->closed        = FALSE;
3654     //Unlink to make a head and tail
3655     sp->first         = new_path;
3656     sp->last          = n;
3657     n->n.other        = NULL;
3658     new_path->p.other = NULL;
3661 /**
3662  * Returns area in triangle given by points; may be negative.
3663  */
3664 inline double
3665 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3667     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]);
3670 /**
3671  * Return new node in subpath with given properties.
3672  * \param pos Position of node.
3673  * \param ppos Handle position in previous direction
3674  * \param npos Handle position in previous direction
3675  */
3676 Inkscape::NodePath::Node *
3677 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)
3679     g_assert(sp);
3680     g_assert(sp->nodepath);
3681     g_assert(sp->nodepath->desktop);
3683     if (nodechunk == NULL)
3684         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3686     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3688     n->subpath  = sp;
3690     if (type != Inkscape::NodePath::NODE_NONE) {
3691         // use the type from sodipodi:nodetypes
3692         n->type = type;
3693     } else {
3694         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3695             // points are (almost) collinear
3696             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3697                 // endnode, or a node with a retracted handle
3698                 n->type = Inkscape::NodePath::NODE_CUSP;
3699             } else {
3700                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3701             }
3702         } else {
3703             n->type = Inkscape::NodePath::NODE_CUSP;
3704         }
3705     }
3707     n->code     = code;
3708     n->selected = FALSE;
3709     n->pos      = *pos;
3710     n->p.pos    = *ppos;
3711     n->n.pos    = *npos;
3713     n->dragging_out = NULL;
3715     Inkscape::NodePath::Node *prev;
3716     if (next) {
3717         //g_assert(g_list_find(sp->nodes, next));
3718         prev = next->p.other;
3719     } else {
3720         prev = sp->last;
3721     }
3723     if (prev)
3724         prev->n.other = n;
3725     else
3726         sp->first = n;
3728     if (next)
3729         next->p.other = n;
3730     else
3731         sp->last = n;
3733     n->p.other = prev;
3734     n->n.other = next;
3736     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"));
3737     sp_knot_set_position(n->knot, pos, 0);
3739     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3740     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3741     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3742     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3743     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3744     sp_knot_update_ctrl(n->knot);
3746     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3747     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3748     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3749     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3750     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3751     sp_knot_show(n->knot);
3753     // We only create handle knots and lines on demand
3754     n->p.knot = NULL;
3755     n->p.line = NULL;
3756     n->n.knot = NULL;
3757     n->n.line = NULL;
3759     sp->nodes = g_list_prepend(sp->nodes, n);
3761     return n;
3764 /**
3765  * Destroy node and its knots, link neighbors in subpath.
3766  */
3767 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3769     g_assert(node);
3770     g_assert(node->subpath);
3771     g_assert(SP_IS_KNOT(node->knot));
3773    Inkscape::NodePath::SubPath *sp = node->subpath;
3775     if (node->selected) { // first, deselect
3776         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3777         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3778     }
3780     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3782     g_object_unref(G_OBJECT(node->knot));
3783     if (node->p.knot)
3784         g_object_unref(G_OBJECT(node->p.knot));
3785     if (node->n.knot)
3786         g_object_unref(G_OBJECT(node->n.knot));
3788     if (node->p.line)
3789         gtk_object_destroy(GTK_OBJECT(node->p.line));
3790     if (node->n.line)
3791         gtk_object_destroy(GTK_OBJECT(node->n.line));
3793     if (sp->nodes) { // there are others nodes on the subpath
3794         if (sp->closed) {
3795             if (sp->first == node) {
3796                 g_assert(sp->last == node);
3797                 sp->first = node->n.other;
3798                 sp->last = sp->first;
3799             }
3800             node->p.other->n.other = node->n.other;
3801             node->n.other->p.other = node->p.other;
3802         } else {
3803             if (sp->first == node) {
3804                 sp->first = node->n.other;
3805                 sp->first->code = NR_MOVETO;
3806             }
3807             if (sp->last == node) sp->last = node->p.other;
3808             if (node->p.other) node->p.other->n.other = node->n.other;
3809             if (node->n.other) node->n.other->p.other = node->p.other;
3810         }
3811     } else { // this was the last node on subpath
3812         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3813     }
3815     g_mem_chunk_free(nodechunk, node);
3818 /**
3819  * Returns one of the node's two sides.
3820  * \param which Indicates which side.
3821  * \return Pointer to previous node side if which==-1, next if which==1.
3822  */
3823 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3825     g_assert(node);
3827     switch (which) {
3828         case -1:
3829             return &node->p;
3830         case 1:
3831             return &node->n;
3832         default:
3833             break;
3834     }
3836     g_assert_not_reached();
3838     return NULL;
3841 /**
3842  * Return the other side of the node, given one of its sides.
3843  */
3844 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
3846     g_assert(node);
3848     if (me == &node->p) return &node->n;
3849     if (me == &node->n) return &node->p;
3851     g_assert_not_reached();
3853     return NULL;
3856 /**
3857  * Return NRPathcode on the given side of the node.
3858  */
3859 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3861     g_assert(node);
3863     if (me == &node->p) {
3864         if (node->p.other) return (NRPathcode)node->code;
3865         return NR_MOVETO;
3866     }
3868     if (me == &node->n) {
3869         if (node->n.other) return (NRPathcode)node->n.other->code;
3870         return NR_MOVETO;
3871     }
3873     g_assert_not_reached();
3875     return NR_END;
3878 /**
3879  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3880  */
3881 Inkscape::NodePath::Node *
3882 sp_nodepath_get_node_by_index(int index)
3884     Inkscape::NodePath::Node *e = NULL;
3886     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3887     if (!nodepath) {
3888         return e;
3889     }
3891     //find segment
3892     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3894         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3895         int n = g_list_length(sp->nodes);
3896         if (sp->closed) {
3897             n++;
3898         }
3900         //if the piece belongs to this subpath grab it
3901         //otherwise move onto the next subpath
3902         if (index < n) {
3903             e = sp->first;
3904             for (int i = 0; i < index; ++i) {
3905                 e = e->n.other;
3906             }
3907             break;
3908         } else {
3909             if (sp->closed) {
3910                 index -= (n+1);
3911             } else {
3912                 index -= n;
3913             }
3914         }
3915     }
3917     return e;
3920 /**
3921  * Returns plain text meaning of node type.
3922  */
3923 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3925     unsigned retracted = 0;
3926     bool endnode = false;
3928     for (int which = -1; which <= 1; which += 2) {
3929         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3930         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3931             retracted ++;
3932         if (!side->other)
3933             endnode = true;
3934     }
3936     if (retracted == 0) {
3937         if (endnode) {
3938                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3939                 return _("end node");
3940         } else {
3941             switch (node->type) {
3942                 case Inkscape::NodePath::NODE_CUSP:
3943                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3944                     return _("cusp");
3945                 case Inkscape::NodePath::NODE_SMOOTH:
3946                     // TRANSLATORS: "smooth" is an adjective here
3947                     return _("smooth");
3948                 case Inkscape::NodePath::NODE_SYMM:
3949                     return _("symmetric");
3950             }
3951         }
3952     } else if (retracted == 1) {
3953         if (endnode) {
3954             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3955             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3956         } else {
3957             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3958         }
3959     } else {
3960         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3961     }
3963     return NULL;
3966 /**
3967  * Handles content of statusbar as long as node tool is active.
3968  */
3969 void
3970 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3972     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>Alt+drag</b> nodes to sculpt; <b>arrow</b> keys to move nodes, <b>&lt; &gt;</b> to scale, <b>[ ]</b> to rotate");
3973     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3975     gint total_nodes = sp_nodepath_get_node_count(nodepath);
3976     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
3977     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
3978     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
3980     SPDesktop *desktop = NULL;
3981     if (nodepath) {
3982         desktop = nodepath->desktop;
3983     } else {
3984         desktop = SP_ACTIVE_DESKTOP;
3985     }
3987     SPEventContext *ec = desktop->event_context;
3988     if (!ec) return;
3989     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3990     if (!mc) return;
3992     if (selected_nodes == 0) {
3993         Inkscape::Selection *sel = desktop->selection;
3994         if (!sel || sel->isEmpty()) {
3995             mc->setF(Inkscape::NORMAL_MESSAGE,
3996                      _("Select a single object to edit its nodes or handles."));
3997         } else {
3998             if (nodepath) {
3999             mc->setF(Inkscape::NORMAL_MESSAGE,
4000                      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.",
4001                               "<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.",
4002                               total_nodes),
4003                      total_nodes);
4004             } else {
4005                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4006                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4007                 } else {
4008                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4009                 }
4010             }
4011         }
4012     } else if (nodepath && selected_nodes == 1) {
4013         mc->setF(Inkscape::NORMAL_MESSAGE,
4014                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4015                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4016                           total_nodes),
4017                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4018     } else {
4019         if (selected_subpaths > 1) {
4020             mc->setF(Inkscape::NORMAL_MESSAGE,
4021                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4022                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4023                               total_nodes),
4024                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4025         } else {
4026             mc->setF(Inkscape::NORMAL_MESSAGE,
4027                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4028                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4029                               total_nodes),
4030                      selected_nodes, total_nodes, when_selected);
4031         }
4032     }
4036 /*
4037   Local Variables:
4038   mode:c++
4039   c-file-style:"stroustrup"
4040   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4041   indent-tabs-mode:nil
4042   fill-column:99
4043   End:
4044 */
4045 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :