Code

ec3066cfc2ff9c22e40a55b8dc49293f24db7b42
[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 "shape-editor.h"
35 #include "selection-chemistry.h"
36 #include "selection.h"
37 #include "xml/repr.h"
38 #include "prefs-utils.h"
39 #include "sp-metrics.h"
40 #include "sp-path.h"
41 #include "libnr/nr-matrix-ops.h"
42 #include "splivarot.h"
43 #include "svg/svg.h"
44 #include "verbs.h"
45 #include "display/bezier-utils.h"
46 #include <vector>
47 #include <algorithm>
49 class NR::Matrix;
51 /// \todo
52 /// evil evil evil. FIXME: conflict of two different Path classes!
53 /// There is a conflict in the namespace between two classes named Path.
54 /// #include "sp-flowtext.h"
55 /// #include "sp-flowregion.h"
57 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
58 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
59 GType sp_flowregion_get_type (void);
60 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
61 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
62 GType sp_flowtext_get_type (void);
63 // end evil workaround
65 #include "helper/stlport.h"
68 /// \todo fixme: Implement these via preferences */
70 #define NODE_FILL          0xbfbfbf00
71 #define NODE_STROKE        0x000000ff
72 #define NODE_FILL_HI       0xff000000
73 #define NODE_STROKE_HI     0x000000ff
74 #define NODE_FILL_SEL      0x0000ffff
75 #define NODE_STROKE_SEL    0x000000ff
76 #define NODE_FILL_SEL_HI   0xff000000
77 #define NODE_STROKE_SEL_HI 0x000000ff
78 #define KNOT_FILL          0xffffffff
79 #define KNOT_STROKE        0x000000ff
80 #define KNOT_FILL_HI       0xff000000
81 #define KNOT_STROKE_HI     0x000000ff
83 static GMemChunk *nodechunk = NULL;
85 /* Creation from object */
87 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
88 static gchar *parse_nodetypes(gchar const *types, gint length);
90 /* Object updating */
92 static void stamp_repr(Inkscape::NodePath::Path *np);
93 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
94 static gchar *create_typestr(Inkscape::NodePath::Path *np);
96 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
98 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
100 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
102 /* Adjust handle placement, if the node or the other handle is moved */
103 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
104 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
106 /* Node event callbacks */
107 static void node_clicked(SPKnot *knot, guint state, gpointer data);
108 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
109 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
110 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
112 /* Handle event callbacks */
113 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
114 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
115 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
116 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
117 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
118 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
120 /* Constructors and destructors */
122 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
123 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
124 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
125 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
126 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
127                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
128 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
130 /* Helpers */
132 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
133 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
134 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
136 // active_node indicates mouseover node
137 static Inkscape::NodePath::Node *active_node = NULL;
139 /**
140  * \brief Creates new nodepath from item
141  */
142 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool show_handles)
144     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
146     /** \todo
147      * FIXME: remove this. We don't want to edit paths inside flowtext.
148      * Instead we will build our flowtext with cloned paths, so that the
149      * real paths are outside the flowtext and thus editable as usual.
150      */
151     if (SP_IS_FLOWTEXT(item)) {
152         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
153             if SP_IS_FLOWREGION(child) {
154                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
155                 if (grandchild && SP_IS_PATH(grandchild)) {
156                     item = SP_ITEM(grandchild);
157                     break;
158                 }
159             }
160         }
161     }
163     if (!SP_IS_PATH(item))
164         return NULL;
165     SPPath *path = SP_PATH(item);
166     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
167     if (curve == NULL)
168         return NULL;
170     NArtBpath *bpath = sp_curve_first_bpath(curve);
171     gint length = curve->end;
172     if (length == 0)
173         return NULL; // prevent crash for one-node paths
175     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
176     gchar *typestr = parse_nodetypes(nodetypes, length);
178     //Create new nodepath
179     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
180     if (!np)
181         return NULL;
183     // Set defaults
184     np->desktop     = desktop;
185     np->path        = path;
186     np->subpaths    = NULL;
187     np->selected    = NULL;
188     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
189     np->livarot_path = NULL;
190     np->local_change = 0;
191     np->show_handles = show_handles;
193     // we need to update item's transform from the repr here,
194     // because they may be out of sync when we respond
195     // to a change in repr by regenerating nodepath     --bb
196     sp_object_read_attr(SP_OBJECT(item), "transform");
198     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
199     np->d2i  = np->i2d.inverse();
200     np->repr = repr;
202     // create the subpath(s) from the bpath
203     NArtBpath *b = bpath;
204     while (b->code != NR_END) {
205         b = subpath_from_bpath(np, b, typestr + (b - bpath));
206     }
208     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
209     np->subpaths = g_list_reverse(np->subpaths);
211     g_free(typestr);
212     sp_curve_unref(curve);
214     // create the livarot representation from the same item
215     sp_nodepath_ensure_livarot_path(np);
217     return np;
220 /**
221  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor 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 ShapeEditor that made me, if any, that I am gone.
233     if (np->shape_editor)
234         np->shape_editor->nodepath_destroyed();
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 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
251     if (np && np->livarot_path == NULL && np->path && SP_IS_ITEM(np->path)) {
252         np->livarot_path = Path_for_item (np->path, true, true);
253         if (np->livarot_path)
254             np->livarot_path->ConvertWithBackData(0.01);
255     }
259 /**
260  *  Return the node count of a given NodeSubPath.
261  */
262 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
264     if (!subpath)
265         return 0;
266     gint nodeCount = g_list_length(subpath->nodes);
267     return nodeCount;
270 /**
271  *  Return the node count of a given NodePath.
272  */
273 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
275     if (!np)
276         return 0;
277     gint nodeCount = 0;
278     for (GList *item = np->subpaths ; item ; item=item->next) {
279        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
280         nodeCount += g_list_length(subpath->nodes);
281     }
282     return nodeCount;
285 /**
286  *  Return the subpath count of a given NodePath.
287  */
288 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
290     if (!np)
291         return 0;
292     return g_list_length (np->subpaths);
295 /**
296  *  Return the selected node count of a given NodePath.
297  */
298 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
300     if (!np)
301         return 0;
302     return g_list_length (np->selected);
305 /**
306  *  Return the number of subpaths where nodes are selected in a given NodePath.
307  */
308 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
310     if (!np)
311         return 0;
312     if (!np->selected)
313         return 0;
314     if (!np->selected->next)
315         return 1;
316     gint count = 0;
317     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
318         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
319         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
320             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
321             if (node->selected) {
322                 count ++;
323                 break;
324             }
325         }
326     }
327     return count;
329  
330 /**
331  * Clean up a nodepath after editing.
332  *
333  * Currently we are deleting trivial subpaths.
334  */
335 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
337     GList *badSubPaths = NULL;
339     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
340     for (GList *l = nodepath->subpaths; l ; l=l->next) {
341        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
342        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
343             badSubPaths = g_list_append(badSubPaths, sp);
344     }
346     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
347     //also removes the subpath from nodepath->subpaths
348     for (GList *l = badSubPaths; l ; l=l->next) {
349        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
350         sp_nodepath_subpath_destroy(sp);
351     }
353     g_list_free(badSubPaths);
356 /**
357  * Create new nodepath from b, make it subpath of np.
358  * \param t The node type.
359  * \todo Fixme: t should be a proper type, rather than gchar
360  */
361 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
363     NR::Point ppos, pos, npos;
365     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
367     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
368     bool const closed = (b->code == NR_MOVETO);
370     pos = NR::Point(b->x3, b->y3) * np->i2d;
371     if (b[1].code == NR_CURVETO) {
372         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
373     } else {
374         npos = pos;
375     }
376     Inkscape::NodePath::Node *n;
377     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
378     g_assert(sp->first == n);
379     g_assert(sp->last  == n);
381     b++;
382     t++;
383     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
384         pos = NR::Point(b->x3, b->y3) * np->i2d;
385         if (b->code == NR_CURVETO) {
386             ppos = NR::Point(b->x2, b->y2) * np->i2d;
387         } else {
388             ppos = pos;
389         }
390         if (b[1].code == NR_CURVETO) {
391             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
392         } else {
393             npos = pos;
394         }
395         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
396         b++;
397         t++;
398     }
400     if (closed) sp_nodepath_subpath_close(sp);
402     return b;
405 /**
406  * Convert from sodipodi:nodetypes to new style type string.
407  */
408 static gchar *parse_nodetypes(gchar const *types, gint length)
410     g_assert(length > 0);
412     gchar *typestr = g_new(gchar, length + 1);
414     gint pos = 0;
416     if (types) {
417         for (gint i = 0; types[i] && ( i < length ); i++) {
418             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
419             if (types[i] != '\0') {
420                 switch (types[i]) {
421                     case 's':
422                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
423                         break;
424                     case 'z':
425                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
426                         break;
427                     case 'c':
428                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
429                         break;
430                     default:
431                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
432                         break;
433                 }
434             }
435         }
436     }
438     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
440     return typestr;
443 /**
444  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
445  * updated but repr is not (for speed). Used during curve and node drag.
446  */
447 static void update_object(Inkscape::NodePath::Path *np)
449     g_assert(np);
451     SPCurve *curve = create_curve(np);
453     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
455     sp_curve_unref(curve);
458 /**
459  * Update XML path node with data from path object.
460  */
461 static void update_repr_internal(Inkscape::NodePath::Path *np)
463     g_assert(np);
465     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
467     SPCurve *curve = create_curve(np);
468     gchar *typestr = create_typestr(np);
469     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
471     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
472         np->local_change++;
473         repr->setAttribute("d", svgpath);
474     }
476     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
477         np->local_change++;
478         repr->setAttribute("sodipodi:nodetypes", typestr);
479     }
481     g_free(svgpath);
482     g_free(typestr);
483     sp_curve_unref(curve);
486 /**
487  * Update XML path node with data from path object, commit changes forever.
488  */
489 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
491     //fixme: np can be NULL, so check before proceeding
492     g_return_if_fail(np != NULL);
494     if (np->livarot_path) {
495         delete np->livarot_path;
496         np->livarot_path = NULL;
497     }
499     update_repr_internal(np);
500     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
501     
502     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
503                      annotation);
506 /**
507  * Update XML path node with data from path object, commit changes with undo.
508  */
509 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
511     if (np->livarot_path) {
512         delete np->livarot_path;
513         np->livarot_path = NULL;
514     }
516     update_repr_internal(np);
517     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
518                            annotation);
521 /**
522  * Make duplicate of path, replace corresponding XML node in tree, commit.
523  */
524 static void stamp_repr(Inkscape::NodePath::Path *np)
526     g_assert(np);
528     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
529     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
531     // remember the position of the item
532     gint pos = old_repr->position();
533     // remember parent
534     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
536     SPCurve *curve = create_curve(np);
537     gchar *typestr = create_typestr(np);
539     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
541     new_repr->setAttribute("d", svgpath);
542     new_repr->setAttribute("sodipodi:nodetypes", typestr);
544     // add the new repr to the parent
545     parent->appendChild(new_repr);
546     // move to the saved position
547     new_repr->setPosition(pos > 0 ? pos : 0);
549     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
550                      _("Stamp"));
552     Inkscape::GC::release(new_repr);
553     g_free(svgpath);
554     g_free(typestr);
555     sp_curve_unref(curve);
558 /**
559  * Create curve from path.
560  */
561 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
563     SPCurve *curve = sp_curve_new();
565     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
566        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
567         sp_curve_moveto(curve,
568                         sp->first->pos * np->d2i);
569        Inkscape::NodePath::Node *n = sp->first->n.other;
570         while (n) {
571             NR::Point const end_pt = n->pos * np->d2i;
572             switch (n->code) {
573                 case NR_LINETO:
574                     sp_curve_lineto(curve, end_pt);
575                     break;
576                 case NR_CURVETO:
577                     sp_curve_curveto(curve,
578                                      n->p.other->n.pos * np->d2i,
579                                      n->p.pos * np->d2i,
580                                      end_pt);
581                     break;
582                 default:
583                     g_assert_not_reached();
584                     break;
585             }
586             if (n != sp->last) {
587                 n = n->n.other;
588             } else {
589                 n = NULL;
590             }
591         }
592         if (sp->closed) {
593             sp_curve_closepath(curve);
594         }
595     }
597     return curve;
600 /**
601  * Convert path type string to sodipodi:nodetypes style.
602  */
603 static gchar *create_typestr(Inkscape::NodePath::Path *np)
605     gchar *typestr = g_new(gchar, 32);
606     gint len = 32;
607     gint pos = 0;
609     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
610        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
612         if (pos >= len) {
613             typestr = g_renew(gchar, typestr, len + 32);
614             len += 32;
615         }
617         typestr[pos++] = 'c';
619        Inkscape::NodePath::Node *n;
620         n = sp->first->n.other;
621         while (n) {
622             gchar code;
624             switch (n->type) {
625                 case Inkscape::NodePath::NODE_CUSP:
626                     code = 'c';
627                     break;
628                 case Inkscape::NodePath::NODE_SMOOTH:
629                     code = 's';
630                     break;
631                 case Inkscape::NodePath::NODE_SYMM:
632                     code = 'z';
633                     break;
634                 default:
635                     g_assert_not_reached();
636                     code = '\0';
637                     break;
638             }
640             if (pos >= len) {
641                 typestr = g_renew(gchar, typestr, len + 32);
642                 len += 32;
643             }
645             typestr[pos++] = code;
647             if (n != sp->last) {
648                 n = n->n.other;
649             } else {
650                 n = NULL;
651             }
652         }
653     }
655     if (pos >= len) {
656         typestr = g_renew(gchar, typestr, len + 1);
657         len += 1;
658     }
660     typestr[pos++] = '\0';
662     return typestr;
665 /**
666  * Returns current path in context. // later eliminate this function at all!
667  */
668 static Inkscape::NodePath::Path *sp_nodepath_current()
670     if (!SP_ACTIVE_DESKTOP) {
671         return NULL;
672     }
674     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
676     if (!SP_IS_NODE_CONTEXT(event_context)) {
677         return NULL;
678     }
680     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
685 /**
686  \brief Fills node and handle positions for three nodes, splitting line
687   marked by end at distance t.
688  */
689 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
691     g_assert(new_path != NULL);
692     g_assert(end      != NULL);
694     g_assert(end->p.other == new_path);
695    Inkscape::NodePath::Node *start = new_path->p.other;
696     g_assert(start);
698     if (end->code == NR_LINETO) {
699         new_path->type =Inkscape::NodePath::NODE_CUSP;
700         new_path->code = NR_LINETO;
701         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
702     } else {
703         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
704         new_path->code = NR_CURVETO;
705         gdouble s      = 1 - t;
706         for (int dim = 0; dim < 2; dim++) {
707             NR::Coord const f000 = start->pos[dim];
708             NR::Coord const f001 = start->n.pos[dim];
709             NR::Coord const f011 = end->p.pos[dim];
710             NR::Coord const f111 = end->pos[dim];
711             NR::Coord const f00t = s * f000 + t * f001;
712             NR::Coord const f01t = s * f001 + t * f011;
713             NR::Coord const f11t = s * f011 + t * f111;
714             NR::Coord const f0tt = s * f00t + t * f01t;
715             NR::Coord const f1tt = s * f01t + t * f11t;
716             NR::Coord const fttt = s * f0tt + t * f1tt;
717             start->n.pos[dim]    = f00t;
718             new_path->p.pos[dim] = f0tt;
719             new_path->pos[dim]   = fttt;
720             new_path->n.pos[dim] = f1tt;
721             end->p.pos[dim]      = f11t;
722         }
723     }
726 /**
727  * Adds new node on direct line between two nodes, activates handles of all
728  * three nodes.
729  */
730 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
732     g_assert(end);
733     g_assert(end->subpath);
734     g_assert(g_list_find(end->subpath->nodes, end));
736    Inkscape::NodePath::Node *start = end->p.other;
737     g_assert( start->n.other == end );
738    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
739                                                end,
740                                               Inkscape::NodePath::NODE_SMOOTH,
741                                                (NRPathcode)end->code,
742                                                &start->pos, &start->pos, &start->n.pos);
743     sp_nodepath_line_midpoint(newnode, end, t);
745     sp_node_update_handles(start);
746     sp_node_update_handles(newnode);
747     sp_node_update_handles(end);
749     return newnode;
752 /**
753 \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
754 */
755 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
757     g_assert(node);
758     g_assert(node->subpath);
759     g_assert(g_list_find(node->subpath->nodes, node));
761    Inkscape::NodePath::SubPath *sp = node->subpath;
762     Inkscape::NodePath::Path *np    = sp->nodepath;
764     if (sp->closed) {
765         sp_nodepath_subpath_open(sp, node);
766         return sp->first;
767     } else {
768         // no break for end nodes
769         if (node == sp->first) return NULL;
770         if (node == sp->last ) return NULL;
772         // create a new subpath
773        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
775         // duplicate the break node as start of the new subpath
776        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
778         while (node->n.other) { // copy the remaining nodes into the new subpath
779            Inkscape::NodePath::Node *n  = node->n.other;
780            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);
781             if (n->selected) {
782                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
783             }
784             sp_nodepath_node_destroy(n); // remove the point on the original subpath
785         }
787         return newnode;
788     }
791 /**
792  * Duplicate node and connect to neighbours.
793  */
794 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
796     g_assert(node);
797     g_assert(node->subpath);
798     g_assert(g_list_find(node->subpath->nodes, node));
800    Inkscape::NodePath::SubPath *sp = node->subpath;
802     NRPathcode code = (NRPathcode) node->code;
803     if (code == NR_MOVETO) { // if node is the endnode,
804         node->code = NR_LINETO; // new one is inserted before it, so change that to line
805     }
807     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
809     if (!node->n.other || !node->p.other) // if node is an endnode, select it
810         return node;
811     else
812         return newnode; // otherwise select the newly created node
815 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
817     node->p.pos = (node->pos + (node->pos - node->n.pos));
820 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
822     node->n.pos = (node->pos + (node->pos - node->p.pos));
825 /**
826  * Change line type at node, with side effects on neighbours.
827  */
828 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
830     g_assert(end);
831     g_assert(end->subpath);
832     g_assert(end->p.other);
834     if (end->code == static_cast< guint > ( code ) )
835         return;
837    Inkscape::NodePath::Node *start = end->p.other;
839     end->code = code;
841     if (code == NR_LINETO) {
842         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
843         if (end->n.other) {
844             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
845         }
846         sp_node_adjust_handle(start, -1);
847         sp_node_adjust_handle(end, 1);
848     } else {
849         NR::Point delta = end->pos - start->pos;
850         start->n.pos = start->pos + delta / 3;
851         end->p.pos = end->pos - delta / 3;
852         sp_node_adjust_handle(start, 1);
853         sp_node_adjust_handle(end, -1);
854     }
856     sp_node_update_handles(start);
857     sp_node_update_handles(end);
860 /**
861  * Change node type, and its handles accordingly.
862  */
863 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
865     g_assert(node);
866     g_assert(node->subpath);
868     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
869         return node;
871     if ((node->p.other != NULL) && (node->n.other != NULL)) {
872         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
873             type =Inkscape::NodePath::NODE_CUSP;
874         }
875     }
877     node->type = type;
879     if (node->type == Inkscape::NodePath::NODE_CUSP) {
880         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
881         node->knot->setSize (node->selected? 11 : 9);
882         sp_knot_update_ctrl(node->knot);
883     } else {
884         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
885         node->knot->setSize (node->selected? 9 : 7);
886         sp_knot_update_ctrl(node->knot);
887     }
889     // if one of handles is mouseovered, preserve its position
890     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
891         sp_node_adjust_handle(node, 1);
892     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
893         sp_node_adjust_handle(node, -1);
894     } else {
895         sp_node_adjust_handles(node);
896     }
898     sp_node_update_handles(node);
900     sp_nodepath_update_statusbar(node->subpath->nodepath);
902     return node;
905 /**
906  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
907  * adjacent segments from lines to curves.
908 */
909 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
911     bool p_line = (node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos);
912     bool n_line = (node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos);
914     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
915         if (p_line && n_line) {
916             // only if both adjacent segments are lines, 
917             // convert both to curves:
919             // BEFORE:
920             {
921             node->code = NR_CURVETO;
922             NR::Point delta;
923             if (node->n.other != NULL)
924                 delta = node->n.other->pos - node->p.other->pos;
925             else
926                 delta = node->pos - node->p.other->pos;
927             node->p.pos = node->pos - delta / 4;
928             sp_node_update_handles(node);
929             }
931             // AFTER:
932             {
933             node->n.other->code = NR_CURVETO;
934             NR::Point delta;
935             if (node->p.other != NULL)
936                 delta = node->p.other->pos - node->n.other->pos;
937             else
938                 delta = node->pos - node->n.other->pos;
939             node->n.pos = node->pos - delta / 4;
940             sp_node_update_handles(node);
941             }
942         }
943     }
945     sp_nodepath_set_node_type (node, type);
948 /**
949  * Move node to point, and adjust its and neighbouring handles.
950  */
951 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
953     NR::Point delta = p - node->pos;
954     node->pos = p;
956     node->p.pos += delta;
957     node->n.pos += delta;
959     Inkscape::NodePath::Node *node_p = NULL;
960     Inkscape::NodePath::Node *node_n = NULL;
962     if (node->p.other) {
963         if (node->code == NR_LINETO) {
964             sp_node_adjust_handle(node, 1);
965             sp_node_adjust_handle(node->p.other, -1);
966             node_p = node->p.other;
967         }
968     }
969     if (node->n.other) {
970         if (node->n.other->code == NR_LINETO) {
971             sp_node_adjust_handle(node, -1);
972             sp_node_adjust_handle(node->n.other, 1);
973             node_n = node->n.other;
974         }
975     }
977     // this function is only called from batch movers that will update display at the end
978     // themselves, so here we just move all the knots without emitting move signals, for speed
979     sp_node_update_handles(node, false);
980     if (node_n) {
981         sp_node_update_handles(node_n, false);
982     }
983     if (node_p) {
984         sp_node_update_handles(node_p, false);
985     }
988 /**
989  * Call sp_node_moveto() for node selection and handle possible snapping.
990  */
991 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
992                                             bool const snap = true)
994     NR::Coord best = NR_HUGE;
995     NR::Point delta(dx, dy);
996     NR::Point best_pt = delta;
998     if (snap) {
999         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
1000         
1001         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1002             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1003             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
1004             if (s.getDistance() < best) {
1005                 best = s.getDistance();
1006                 best_pt = s.getPoint() - n->pos;
1007             }
1008         }
1009     }
1011     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1012         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1013         sp_node_moveto(n, n->pos + best_pt);
1014     }
1016     // do not update repr here so that node dragging is acceptably fast
1017     update_object(nodepath);
1020 /**
1021 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1022 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1023 near x = 0.
1024  */
1025 double
1026 sculpt_profile (double x, double alpha, guint profile)
1028     if (x >= 1)
1029         return 0;
1030     if (x <= 0)
1031         return 1;
1033     switch (profile) {
1034         case SCULPT_PROFILE_LINEAR:
1035         return 1 - x;
1036         case SCULPT_PROFILE_BELL:
1037         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1038         case SCULPT_PROFILE_ELLIPTIC:
1039         return sqrt(1 - x*x);
1040     }
1042     return 1;
1045 double
1046 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1048     // extremely primitive for now, don't have time to look for the real one
1049     double lower = NR::L2(b - a);
1050     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1051     return (lower + upper)/2;
1054 void
1055 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1057     n->pos = n->origin + delta;
1058     n->n.pos = n->n.origin + delta_n;
1059     n->p.pos = n->p.origin + delta_p;
1060     sp_node_adjust_handles(n);
1061     sp_node_update_handles(n, false);
1064 /**
1065  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1066  * on how far they are from the dragged node n.
1067  */
1068 static void 
1069 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1071     g_assert (n);
1072     g_assert (nodepath);
1073     g_assert (n->subpath->nodepath == nodepath);
1075     double pressure = n->knot->pressure;
1076     if (pressure == 0)
1077         pressure = 0.5; // default
1078     pressure = CLAMP (pressure, 0.2, 0.8);
1080     // map pressure to alpha = 1/5 ... 5
1081     double alpha = 1 - 2 * fabs(pressure - 0.5);
1082     if (pressure > 0.5)
1083         alpha = 1/alpha;
1085     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1087     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1088         // Only one subpath has selected nodes:
1089         // use linear mode, where the distance from n to node being dragged is calculated along the path
1091         double n_sel_range = 0, p_sel_range = 0;
1092         guint n_nodes = 0, p_nodes = 0;
1093         guint n_sel_nodes = 0, p_sel_nodes = 0;
1095         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1096         {
1097             double n_range = 0, p_range = 0;
1098             bool n_going = true, p_going = true;
1099             Inkscape::NodePath::Node *n_node = n;
1100             Inkscape::NodePath::Node *p_node = n;
1101             do {
1102                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1103                 if (n_node && n_going)
1104                     n_node = n_node->n.other;
1105                 if (n_node == NULL) {
1106                     n_going = false;
1107                 } else {
1108                     n_nodes ++;
1109                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1110                     if (n_node->selected) {
1111                         n_sel_nodes ++;
1112                         n_sel_range = n_range;
1113                     }
1114                     if (n_node == p_node) {
1115                         n_going = false;
1116                         p_going = false;
1117                     }
1118                 }
1119                 if (p_node && p_going)
1120                     p_node = p_node->p.other;
1121                 if (p_node == NULL) {
1122                     p_going = false;
1123                 } else {
1124                     p_nodes ++;
1125                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1126                     if (p_node->selected) {
1127                         p_sel_nodes ++;
1128                         p_sel_range = p_range;
1129                     }
1130                     if (p_node == n_node) {
1131                         n_going = false;
1132                         p_going = false;
1133                     }
1134                 }
1135             } while (n_going || p_going);
1136         }
1138         // Second pass: actually move nodes in this subpath
1139         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1140         {
1141             double n_range = 0, p_range = 0;
1142             bool n_going = true, p_going = true;
1143             Inkscape::NodePath::Node *n_node = n;
1144             Inkscape::NodePath::Node *p_node = n;
1145             do {
1146                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1147                 if (n_node && n_going)
1148                     n_node = n_node->n.other;
1149                 if (n_node == NULL) {
1150                     n_going = false;
1151                 } else {
1152                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1153                     if (n_node->selected) {
1154                         sp_nodepath_move_node_and_handles (n_node, 
1155                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1156                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1157                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1158                     }
1159                     if (n_node == p_node) {
1160                         n_going = false;
1161                         p_going = false;
1162                     }
1163                 }
1164                 if (p_node && p_going)
1165                     p_node = p_node->p.other;
1166                 if (p_node == NULL) {
1167                     p_going = false;
1168                 } else {
1169                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1170                     if (p_node->selected) {
1171                         sp_nodepath_move_node_and_handles (p_node, 
1172                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1173                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1174                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1175                     }
1176                     if (p_node == n_node) {
1177                         n_going = false;
1178                         p_going = false;
1179                     }
1180                 }
1181             } while (n_going || p_going);
1182         }
1184     } else {
1185         // Multiple subpaths have selected nodes:
1186         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1187         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1188         // fix the pear-like shape when sculpting e.g. a ring
1190         // First pass: calculate range
1191         gdouble direct_range = 0;
1192         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1193             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1194             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1195                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1196                 if (node->selected) {
1197                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1198                 }
1199             }
1200         }
1202         // Second pass: actually move nodes
1203         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1204             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1205             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1206                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1207                 if (node->selected) {
1208                     if (direct_range > 1e-6) {
1209                         sp_nodepath_move_node_and_handles (node,
1210                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1211                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1212                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1213                     } else {
1214                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1215                     }
1217                 }
1218             }
1219         }
1220     }
1222     // do not update repr here so that node dragging is acceptably fast
1223     update_object(nodepath);
1227 /**
1228  * Move node selection to point, adjust its and neighbouring handles,
1229  * handle possible snapping, and commit the change with possible undo.
1230  */
1231 void
1232 sp_node_selected_move(gdouble dx, gdouble dy)
1234     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1235     if (!nodepath) return;
1237     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1239     if (dx == 0) {
1240         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1241     } else if (dy == 0) {
1242         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1243     } else {
1244         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1245     }
1248 /**
1249  * Move node selection off screen and commit the change.
1250  */
1251 void
1252 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1254     // borrowed from sp_selection_move_screen in selection-chemistry.c
1255     // we find out the current zoom factor and divide deltas by it
1256     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1258     gdouble zoom = desktop->current_zoom();
1259     gdouble zdx = dx / zoom;
1260     gdouble zdy = dy / zoom;
1262     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1263     if (!nodepath) return;
1265     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1267     if (dx == 0) {
1268         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1269     } else if (dy == 0) {
1270         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1271     } else {
1272         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1273     }
1276 /** If they don't yet exist, creates knot and line for the given side of the node */
1277 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1279     if (!side->knot) {
1280         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"));
1282         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1283         side->knot->setSize (7);
1284         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1285         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1286         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1287         sp_knot_update_ctrl(side->knot);
1289         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1290         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1291         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1292         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1293         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1294         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1295     }
1297     if (!side->line) {
1298         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1299                                         SP_TYPE_CTRLLINE, NULL);
1300     }
1303 /**
1304  * Ensure the given handle of the node is visible/invisible, update its screen position
1305  */
1306 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1308     g_assert(node != NULL);
1310    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1311     NRPathcode code = sp_node_path_code_from_side(node, side);
1313     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1315     if (show_handle) {
1316         if (!side->knot) { // No handle knot at all
1317             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1318             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1319             side->knot->pos = side->pos;
1320             if (side->knot->item) 
1321                 SP_CTRL(side->knot->item)->moveto(side->pos);
1322             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1323             sp_knot_show(side->knot);
1324         } else {
1325             if (side->knot->pos != side->pos) { // only if it's really moved
1326                 if (fire_move_signals) {
1327                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1328                 } else {
1329                     sp_knot_moveto(side->knot, &side->pos);
1330                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1331                 }
1332             }
1333             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1334                 sp_knot_show(side->knot);
1335             }
1336         }
1337         sp_canvas_item_show(side->line);
1338     } else {
1339         if (side->knot) {
1340             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1341                 sp_knot_hide(side->knot);
1342             }
1343         }
1344         if (side->line) {
1345             sp_canvas_item_hide(side->line);
1346         }
1347     }
1350 /**
1351  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1352  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1353  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1354  * updated; otherwise, just move the knots silently (used in batch moves).
1355  */
1356 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1358     g_assert(node != NULL);
1360     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1361         sp_knot_show(node->knot);
1362     }
1364     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1365         if (fire_move_signals)
1366             sp_knot_set_position(node->knot, &node->pos, 0);
1367         else 
1368             sp_knot_moveto(node->knot, &node->pos);
1369     }
1371     gboolean show_handles = node->selected;
1372     if (node->p.other != NULL) {
1373         if (node->p.other->selected) show_handles = TRUE;
1374     }
1375     if (node->n.other != NULL) {
1376         if (node->n.other->selected) show_handles = TRUE;
1377     }
1379     if (node->subpath->nodepath->show_handles == false)
1380         show_handles = FALSE;
1382     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1383     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1386 /**
1387  * Call sp_node_update_handles() for all nodes on subpath.
1388  */
1389 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1391     g_assert(subpath != NULL);
1393     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1394         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1395     }
1398 /**
1399  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1400  */
1401 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1403     g_assert(nodepath != NULL);
1405     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1406         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1407     }
1410 void
1411 sp_nodepath_show_handles(bool show)
1413     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1414     if (nodepath == NULL) return;
1416     nodepath->show_handles = show;
1417     sp_nodepath_update_handles(nodepath);
1420 /**
1421  * Adds all selected nodes in nodepath to list.
1422  */
1423 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1425     StlConv<Node *>::list(l, selected);
1426 /// \todo this adds a copying, rework when the selection becomes a stl list
1429 /**
1430  * Align selected nodes on the specified axis.
1431  */
1432 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1434     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1435         return;
1436     }
1438     if ( !nodepath->selected->next ) { // only one node selected
1439         return;
1440     }
1441    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1442     NR::Point dest(pNode->pos);
1443     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1444         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1445         if (pNode) {
1446             dest[axis] = pNode->pos[axis];
1447             sp_node_moveto(pNode, dest);
1448         }
1449     }
1451     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1454 /// Helper struct.
1455 struct NodeSort
1457    Inkscape::NodePath::Node *_node;
1458     NR::Coord _coord;
1459     /// \todo use vectorof pointers instead of calling copy ctor
1460     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1461         _node(node), _coord(node->pos[axis])
1462     {}
1464 };
1466 static bool operator<(NodeSort const &a, NodeSort const &b)
1468     return (a._coord < b._coord);
1471 /**
1472  * Distribute selected nodes on the specified axis.
1473  */
1474 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1476     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1477         return;
1478     }
1480     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1481         return;
1482     }
1484    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1485     std::vector<NodeSort> sorted;
1486     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1487         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1488         if (pNode) {
1489             NodeSort n(pNode, axis);
1490             sorted.push_back(n);
1491             //dest[axis] = pNode->pos[axis];
1492             //sp_node_moveto(pNode, dest);
1493         }
1494     }
1495     std::sort(sorted.begin(), sorted.end());
1496     unsigned int len = sorted.size();
1497     //overall bboxes span
1498     float dist = (sorted.back()._coord -
1499                   sorted.front()._coord);
1500     //new distance between each bbox
1501     float step = (dist) / (len - 1);
1502     float pos = sorted.front()._coord;
1503     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1504           it < sorted.end();
1505           it ++ )
1506     {
1507         NR::Point dest((*it)._node->pos);
1508         dest[axis] = pos;
1509         sp_node_moveto((*it)._node, dest);
1510         pos += step;
1511     }
1513     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1517 /**
1518  * Call sp_nodepath_line_add_node() for all selected segments.
1519  */
1520 void
1521 sp_node_selected_add_node(void)
1523     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1524     if (!nodepath) {
1525         return;
1526     }
1528     GList *nl = NULL;
1530     int n_added = 0;
1532     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1533        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1534         g_assert(t->selected);
1535         if (t->p.other && t->p.other->selected) {
1536             nl = g_list_prepend(nl, t);
1537         }
1538     }
1540     while (nl) {
1541        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1542        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1543        sp_nodepath_node_select(n, TRUE, FALSE);
1544        n_added ++;
1545        nl = g_list_remove(nl, t);
1546     }
1548     /** \todo fixme: adjust ? */
1549     sp_nodepath_update_handles(nodepath);
1551     if (n_added > 1) {
1552         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1553     } else if (n_added > 0) {
1554         sp_nodepath_update_repr(nodepath, _("Add node"));
1555     }
1557     sp_nodepath_update_statusbar(nodepath);
1560 /**
1561  * Select segment nearest to point
1562  */
1563 void
1564 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1566     if (!nodepath) {
1567         return;
1568     }
1570     sp_nodepath_ensure_livarot_path(nodepath);
1571     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1572     if (!maybe_position) {
1573         return;
1574     }
1575     Path::cut_position position = *maybe_position;
1577     //find segment to segment
1578     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1580     //fixme: this can return NULL, so check before proceeding.
1581     g_return_if_fail(e != NULL);
1582     
1583     gboolean force = FALSE;
1584     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1585         force = TRUE;
1586     }
1587     sp_nodepath_node_select(e, (gboolean) toggle, force);
1588     if (e->p.other)
1589         sp_nodepath_node_select(e->p.other, TRUE, force);
1591     sp_nodepath_update_handles(nodepath);
1593     sp_nodepath_update_statusbar(nodepath);
1596 /**
1597  * Add a node nearest to point
1598  */
1599 void
1600 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1602     if (!nodepath) {
1603         return;
1604     }
1606     sp_nodepath_ensure_livarot_path(nodepath);
1607     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1608     if (!maybe_position) {
1609         return;
1610     }
1611     Path::cut_position position = *maybe_position;
1613     //find segment to split
1614     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1616     //don't know why but t seems to flip for lines
1617     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1618         position.t = 1.0 - position.t;
1619     }
1620     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1621     sp_nodepath_node_select(n, FALSE, TRUE);
1623     /* fixme: adjust ? */
1624     sp_nodepath_update_handles(nodepath);
1626     sp_nodepath_update_repr(nodepath, _("Add node"));
1628     sp_nodepath_update_statusbar(nodepath);
1631 /*
1632  * Adjusts a segment so that t moves by a certain delta for dragging
1633  * converts lines to curves
1634  *
1635  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1636  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1637  */
1638 void
1639 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1641     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1643     //fixme: e and e->p can be NULL, so check for those before proceeding
1644     g_return_if_fail(e != NULL);
1645     g_return_if_fail(&e->p != NULL);
1646     
1647     /* feel good is an arbitrary parameter that distributes the delta between handles
1648      * if t of the drag point is less than 1/6 distance form the endpoint only
1649      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1650      */
1651     double feel_good;
1652     if (t <= 1.0 / 6.0)
1653         feel_good = 0;
1654     else if (t <= 0.5)
1655         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1656     else if (t <= 5.0 / 6.0)
1657         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1658     else
1659         feel_good = 1;
1661     //if we're dragging a line convert it to a curve
1662     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1663         sp_nodepath_set_line_type(e, NR_CURVETO);
1664     }
1666     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1667     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1668     e->p.other->n.pos += offsetcoord0;
1669     e->p.pos += offsetcoord1;
1671     // adjust handles of adjacent nodes where necessary
1672     sp_node_adjust_handle(e,1);
1673     sp_node_adjust_handle(e->p.other,-1);
1675     sp_nodepath_update_handles(e->subpath->nodepath);
1677     update_object(e->subpath->nodepath);
1679     sp_nodepath_update_statusbar(e->subpath->nodepath);
1683 /**
1684  * Call sp_nodepath_break() for all selected segments.
1685  */
1686 void sp_node_selected_break()
1688     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1689     if (!nodepath) return;
1691     GList *temp = NULL;
1692     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1693        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1694        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1695         if (nn == NULL) continue; // no break, no new node
1696         temp = g_list_prepend(temp, nn);
1697     }
1699     if (temp) {
1700         sp_nodepath_deselect(nodepath);
1701     }
1702     for (GList *l = temp; l != NULL; l = l->next) {
1703         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1704     }
1706     sp_nodepath_update_handles(nodepath);
1708     sp_nodepath_update_repr(nodepath, _("Break path"));
1711 /**
1712  * Duplicate the selected node(s).
1713  */
1714 void sp_node_selected_duplicate()
1716     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1717     if (!nodepath) {
1718         return;
1719     }
1721     GList *temp = NULL;
1722     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1723        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1724        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1725         if (nn == NULL) continue; // could not duplicate
1726         temp = g_list_prepend(temp, nn);
1727     }
1729     if (temp) {
1730         sp_nodepath_deselect(nodepath);
1731     }
1732     for (GList *l = temp; l != NULL; l = l->next) {
1733         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1734     }
1736     sp_nodepath_update_handles(nodepath);
1738     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1741 /**
1742  *  Join two nodes by merging them into one.
1743  */
1744 void sp_node_selected_join()
1746     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1747     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1749     if (g_list_length(nodepath->selected) != 2) {
1750         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1751         return;
1752     }
1754    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1755    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1757     g_assert(a != b);
1758     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1759         // someone tried to join an orphan node (i.e. a single-node subpath).
1760         // this is not worth an error message, just fail silently.
1761         return;
1762     }
1764     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1765         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1766         return;
1767     }
1769     /* a and b are endpoints */
1771     NR::Point c;
1772     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1773         c = a->pos;
1774     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1775         c = b->pos;
1776     } else {
1777         c = (a->pos + b->pos) / 2;
1778     }
1780     if (a->subpath == b->subpath) {
1781        Inkscape::NodePath::SubPath *sp = a->subpath;
1782         sp_nodepath_subpath_close(sp);
1783         sp_node_moveto (sp->first, c);
1785         sp_nodepath_update_handles(sp->nodepath);
1786         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1787         return;
1788     }
1790     /* a and b are separate subpaths */
1791    Inkscape::NodePath::SubPath *sa = a->subpath;
1792    Inkscape::NodePath::SubPath *sb = b->subpath;
1793     NR::Point p;
1794    Inkscape::NodePath::Node *n;
1795     NRPathcode code;
1796     if (a == sa->first) {
1797         p = sa->first->n.pos;
1798         code = (NRPathcode)sa->first->n.other->code;
1799        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1800         n = sa->last;
1801         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1802         n = n->p.other;
1803         while (n) {
1804             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1805             n = n->p.other;
1806             if (n == sa->first) n = NULL;
1807         }
1808         sp_nodepath_subpath_destroy(sa);
1809         sa = t;
1810     } else if (a == sa->last) {
1811         p = sa->last->p.pos;
1812         code = (NRPathcode)sa->last->code;
1813         sp_nodepath_node_destroy(sa->last);
1814     } else {
1815         code = NR_END;
1816         g_assert_not_reached();
1817     }
1819     if (b == sb->first) {
1820         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1821         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1822             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1823         }
1824     } else if (b == sb->last) {
1825         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1826         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1827             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1828         }
1829     } else {
1830         g_assert_not_reached();
1831     }
1832     /* and now destroy sb */
1834     sp_nodepath_subpath_destroy(sb);
1836     sp_nodepath_update_handles(sa->nodepath);
1838     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1840     sp_nodepath_update_statusbar(nodepath);
1843 /**
1844  *  Join two nodes by adding a segment between them.
1845  */
1846 void sp_node_selected_join_segment()
1848     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1849     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1851     if (g_list_length(nodepath->selected) != 2) {
1852         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1853         return;
1854     }
1856    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1857    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1859     g_assert(a != b);
1860     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1861         // someone tried to join an orphan node (i.e. a single-node subpath).
1862         // this is not worth an error message, just fail silently.
1863         return;
1864     }
1866     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1867         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1868         return;
1869     }
1871     if (a->subpath == b->subpath) {
1872        Inkscape::NodePath::SubPath *sp = a->subpath;
1874         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1875         sp->closed = TRUE;
1877         sp->first->p.other = sp->last;
1878         sp->last->n.other  = sp->first;
1880         sp_node_handle_mirror_p_to_n(sp->last);
1881         sp_node_handle_mirror_n_to_p(sp->first);
1883         sp->first->code = sp->last->code;
1884         sp->first       = sp->last;
1886         sp_nodepath_update_handles(sp->nodepath);
1888         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1890         return;
1891     }
1893     /* a and b are separate subpaths */
1894    Inkscape::NodePath::SubPath *sa = a->subpath;
1895    Inkscape::NodePath::SubPath *sb = b->subpath;
1897    Inkscape::NodePath::Node *n;
1898     NR::Point p;
1899     NRPathcode code;
1900     if (a == sa->first) {
1901         code = (NRPathcode) sa->first->n.other->code;
1902        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1903         n = sa->last;
1904         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1905         for (n = n->p.other; n != NULL; n = n->p.other) {
1906             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1907         }
1908         sp_nodepath_subpath_destroy(sa);
1909         sa = t;
1910     } else if (a == sa->last) {
1911         code = (NRPathcode)sa->last->code;
1912     } else {
1913         code = NR_END;
1914         g_assert_not_reached();
1915     }
1917     if (b == sb->first) {
1918         n = sb->first;
1919         sp_node_handle_mirror_p_to_n(sa->last);
1920         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1921         sp_node_handle_mirror_n_to_p(sa->last);
1922         for (n = n->n.other; n != NULL; n = n->n.other) {
1923             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1924         }
1925     } else if (b == sb->last) {
1926         n = sb->last;
1927         sp_node_handle_mirror_p_to_n(sa->last);
1928         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1929         sp_node_handle_mirror_n_to_p(sa->last);
1930         for (n = n->p.other; n != NULL; n = n->p.other) {
1931             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1932         }
1933     } else {
1934         g_assert_not_reached();
1935     }
1936     /* and now destroy sb */
1938     sp_nodepath_subpath_destroy(sb);
1940     sp_nodepath_update_handles(sa->nodepath);
1942     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1945 /**
1946  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1947  */
1948 void sp_node_delete_preserve(GList *nodes_to_delete)
1950     GSList *nodepaths = NULL;
1951     
1952     while (nodes_to_delete) {
1953         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1954         Inkscape::NodePath::SubPath *sp = node->subpath;
1955         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1956         Inkscape::NodePath::Node *sample_cursor = NULL;
1957         Inkscape::NodePath::Node *sample_end = NULL;
1958         Inkscape::NodePath::Node *delete_cursor = node;
1959         bool just_delete = false;
1960         
1961         //find the start of this contiguous selection
1962         //move left to the first node that is not selected
1963         //or the start of the non-closed path
1964         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1965             delete_cursor = curr;
1966         }
1968         //just delete at the beginning of an open path
1969         if (!delete_cursor->p.other) {
1970             sample_cursor = delete_cursor;
1971             just_delete = true;
1972         } else {
1973             sample_cursor = delete_cursor->p.other;
1974         }
1975         
1976         //calculate points for each segment
1977         int rate = 5;
1978         float period = 1.0 / rate;
1979         std::vector<NR::Point> data;
1980         if (!just_delete) {
1981             data.push_back(sample_cursor->pos);
1982             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1983                 //just delete at the end of an open path
1984                 if (!sp->closed && curr == sp->last) {
1985                     just_delete = true;
1986                     break;
1987                 }
1988                 
1989                 //sample points on the contiguous selected segment
1990                 NR::Point *bez;
1991                 bez = new NR::Point [4];
1992                 bez[0] = curr->pos;
1993                 bez[1] = curr->n.pos;
1994                 bez[2] = curr->n.other->p.pos;
1995                 bez[3] = curr->n.other->pos;
1996                 for (int i=1; i<rate; i++) {
1997                     gdouble t = i * period;
1998                     NR::Point p = bezier_pt(3, bez, t);
1999                     data.push_back(p);
2000                 }
2001                 data.push_back(curr->n.other->pos);
2003                 sample_end = curr->n.other;
2004                 //break if we've come full circle or hit the end of the selection
2005                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2006                     break;
2007                 }
2008             }
2009         }
2011         if (!just_delete) {
2012             //calculate the best fitting single segment and adjust the endpoints
2013             NR::Point *adata;
2014             adata = new NR::Point [data.size()];
2015             copy(data.begin(), data.end(), adata);
2016             
2017             NR::Point *bez;
2018             bez = new NR::Point [4];
2019             //would decreasing error create a better fitting approximation?
2020             gdouble error = 1.0;
2021             gint ret;
2022             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2024             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2025             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2026             //the resulting nodes behave as expected.
2027             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2028             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2029             
2030             //adjust endpoints
2031             sample_cursor->n.pos = bez[1];
2032             sample_end->p.pos = bez[2];
2033         }
2034        
2035         //destroy this contiguous selection
2036         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2037             Inkscape::NodePath::Node *temp = delete_cursor;
2038             if (delete_cursor->n.other == delete_cursor) {
2039                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2040                 delete_cursor = NULL; 
2041             } else {
2042                 delete_cursor = delete_cursor->n.other;
2043             }
2044             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2045             sp_nodepath_node_destroy(temp);
2046         }
2048         sp_nodepath_update_handles(nodepath);
2050         if (!g_slist_find(nodepaths, nodepath))
2051             nodepaths = g_slist_prepend (nodepaths, nodepath);
2052     }
2054     for (GSList *i = nodepaths; i; i = i->next) {
2055         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2056         // different nodepaths will give us one undo event per nodepath
2057         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2059         // if the entire nodepath is removed, delete the selected object.
2060         if (nodepath->subpaths == NULL ||
2061             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2062             //at least 2
2063             sp_nodepath_get_node_count(nodepath) < 2) {
2064             SPDocument *document = sp_desktop_document (nodepath->desktop);
2065             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2066             //delete this nodepath's object, not the entire selection! (though at this time, this
2067             //does not matter)
2068             sp_selection_delete();
2069             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2070                               _("Delete nodes"));
2071         } else {
2072             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2073             sp_nodepath_update_statusbar(nodepath);
2074         }
2075     }
2077     g_slist_free (nodepaths);
2080 /**
2081  * Delete one or more selected nodes.
2082  */
2083 void sp_node_selected_delete()
2085     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2086     if (!nodepath) return;
2087     if (!nodepath->selected) return;
2089     /** \todo fixme: do it the right way */
2090     while (nodepath->selected) {
2091        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2092         sp_nodepath_node_destroy(node);
2093     }
2096     //clean up the nodepath (such as for trivial subpaths)
2097     sp_nodepath_cleanup(nodepath);
2099     sp_nodepath_update_handles(nodepath);
2101     // if the entire nodepath is removed, delete the selected object.
2102     if (nodepath->subpaths == NULL ||
2103         sp_nodepath_get_node_count(nodepath) < 2) {
2104         SPDocument *document = sp_desktop_document (nodepath->desktop);
2105         sp_selection_delete();
2106         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2107                           _("Delete nodes"));
2108         return;
2109     }
2111     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2113     sp_nodepath_update_statusbar(nodepath);
2116 /**
2117  * Delete one or more segments between two selected nodes.
2118  * This is the code for 'split'.
2119  */
2120 void
2121 sp_node_selected_delete_segment(void)
2123    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2124    Inkscape::NodePath::Node *curr, *next;     //Iterators
2126     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2127     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2129     if (g_list_length(nodepath->selected) != 2) {
2130         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2131                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2132         return;
2133     }
2135     //Selected nodes, not inclusive
2136    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2137    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2139     if ( ( a==b)                       ||  //same node
2140          (a->subpath  != b->subpath )  ||  //not the same path
2141          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2142          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2143     {
2144         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2145                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2146         return;
2147     }
2149     //###########################################
2150     //# BEGIN EDITS
2151     //###########################################
2152     //##################################
2153     //# CLOSED PATH
2154     //##################################
2155     if (a->subpath->closed) {
2158         gboolean reversed = FALSE;
2160         //Since we can go in a circle, we need to find the shorter distance.
2161         //  a->b or b->a
2162         start = end = NULL;
2163         int distance    = 0;
2164         int minDistance = 0;
2165         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2166             if (curr==b) {
2167                 //printf("a to b:%d\n", distance);
2168                 start = a;//go from a to b
2169                 end   = b;
2170                 minDistance = distance;
2171                 //printf("A to B :\n");
2172                 break;
2173             }
2174             distance++;
2175         }
2177         //try again, the other direction
2178         distance = 0;
2179         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2180             if (curr==a) {
2181                 //printf("b to a:%d\n", distance);
2182                 if (distance < minDistance) {
2183                     start    = b;  //we go from b to a
2184                     end      = a;
2185                     reversed = TRUE;
2186                     //printf("B to A\n");
2187                 }
2188                 break;
2189             }
2190             distance++;
2191         }
2194         //Copy everything from 'end' to 'start' to a new subpath
2195        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2196         for (curr=end ; curr ; curr=curr->n.other) {
2197             NRPathcode code = (NRPathcode) curr->code;
2198             if (curr == end)
2199                 code = NR_MOVETO;
2200             sp_nodepath_node_new(t, NULL,
2201                                  (Inkscape::NodePath::NodeType)curr->type, code,
2202                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2203             if (curr == start)
2204                 break;
2205         }
2206         sp_nodepath_subpath_destroy(a->subpath);
2209     }
2213     //##################################
2214     //# OPEN PATH
2215     //##################################
2216     else {
2218         //We need to get the direction of the list between A and B
2219         //Can we walk from a to b?
2220         start = end = NULL;
2221         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2222             if (curr==b) {
2223                 start = a;  //did it!  we go from a to b
2224                 end   = b;
2225                 //printf("A to B\n");
2226                 break;
2227             }
2228         }
2229         if (!start) {//didn't work?  let's try the other direction
2230             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2231                 if (curr==a) {
2232                     start = b;  //did it!  we go from b to a
2233                     end   = a;
2234                     //printf("B to A\n");
2235                     break;
2236                 }
2237             }
2238         }
2239         if (!start) {
2240             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2241                                                      _("Cannot find path between nodes."));
2242             return;
2243         }
2247         //Copy everything after 'end' to a new subpath
2248        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2249         for (curr=end ; curr ; curr=curr->n.other) {
2250             NRPathcode code = (NRPathcode) curr->code;
2251             if (curr == end)
2252                 code = NR_MOVETO;
2253             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2254                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2255         }
2257         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2258         for (curr = start->n.other ; curr  ; curr=next) {
2259             next = curr->n.other;
2260             sp_nodepath_node_destroy(curr);
2261         }
2263     }
2264     //###########################################
2265     //# END EDITS
2266     //###########################################
2268     //clean up the nodepath (such as for trivial subpaths)
2269     sp_nodepath_cleanup(nodepath);
2271     sp_nodepath_update_handles(nodepath);
2273     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2275     sp_nodepath_update_statusbar(nodepath);
2278 /**
2279  * Call sp_nodepath_set_line() for all selected segments.
2280  */
2281 void
2282 sp_node_selected_set_line_type(NRPathcode code)
2284     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2285     if (nodepath == NULL) return;
2287     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2288        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2289         g_assert(n->selected);
2290         if (n->p.other && n->p.other->selected) {
2291             sp_nodepath_set_line_type(n, code);
2292         }
2293     }
2295     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2298 /**
2299  * Call sp_nodepath_convert_node_type() for all selected nodes.
2300  */
2301 void
2302 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2304     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2305     if (nodepath == NULL) return;
2307     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2308         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2309     }
2311     sp_nodepath_update_repr(nodepath, _("Change node type"));
2314 /**
2315  * Change select status of node, update its own and neighbour handles.
2316  */
2317 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2319     node->selected = selected;
2321     if (selected) {
2322         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2323         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2324         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2325         sp_knot_update_ctrl(node->knot);
2326     } else {
2327         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2328         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2329         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2330         sp_knot_update_ctrl(node->knot);
2331     }
2333     sp_node_update_handles(node);
2334     if (node->n.other) sp_node_update_handles(node->n.other);
2335     if (node->p.other) sp_node_update_handles(node->p.other);
2338 /**
2339 \brief Select a node
2340 \param node     The node to select
2341 \param incremental   If true, add to selection, otherwise deselect others
2342 \param override   If true, always select this node, otherwise toggle selected status
2343 */
2344 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2346     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2348     if (incremental) {
2349         if (override) {
2350             if (!g_list_find(nodepath->selected, node)) {
2351                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2352             }
2353             sp_node_set_selected(node, TRUE);
2354         } else { // toggle
2355             if (node->selected) {
2356                 g_assert(g_list_find(nodepath->selected, node));
2357                 nodepath->selected = g_list_remove(nodepath->selected, node);
2358             } else {
2359                 g_assert(!g_list_find(nodepath->selected, node));
2360                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2361             }
2362             sp_node_set_selected(node, !node->selected);
2363         }
2364     } else {
2365         sp_nodepath_deselect(nodepath);
2366         nodepath->selected = g_list_prepend(nodepath->selected, node);
2367         sp_node_set_selected(node, TRUE);
2368     }
2370     sp_nodepath_update_statusbar(nodepath);
2374 /**
2375 \brief Deselect all nodes in the nodepath
2376 */
2377 void
2378 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2380     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2382     while (nodepath->selected) {
2383         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2384         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2385     }
2386     sp_nodepath_update_statusbar(nodepath);
2389 /**
2390 \brief Select or invert selection of all nodes in the nodepath
2391 */
2392 void
2393 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2395     if (!nodepath) return;
2397     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2398        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2399         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2400            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2401            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2402         }
2403     }
2406 /**
2407  * If nothing selected, does the same as sp_nodepath_select_all();
2408  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2409  * (i.e., similar to "select all in layer", with the "selected" subpaths
2410  * being treated as "layers" in the path).
2411  */
2412 void
2413 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2415     if (!nodepath) return;
2417     if (g_list_length (nodepath->selected) == 0) {
2418         sp_nodepath_select_all (nodepath, invert);
2419         return;
2420     }
2422     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2423     GSList *subpaths = NULL;
2425     for (GList *l = copy; l != NULL; l = l->next) {
2426         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2427         Inkscape::NodePath::SubPath *subpath = n->subpath;
2428         if (!g_slist_find (subpaths, subpath))
2429             subpaths = g_slist_prepend (subpaths, subpath);
2430     }
2432     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2433         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2434         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2435             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2436             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2437         }
2438     }
2440     g_slist_free (subpaths);
2441     g_list_free (copy);
2444 /**
2445  * \brief Select the node after the last selected; if none is selected,
2446  * select the first within path.
2447  */
2448 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2450     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2452    Inkscape::NodePath::Node *last = NULL;
2453     if (nodepath->selected) {
2454         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2455            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2456             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2457             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2458                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2459                 if (node->selected) {
2460                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2461                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2462                             if (spl->next) { // there's a next subpath
2463                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2464                                 last = subpath_next->first;
2465                             } else if (spl->prev) { // there's a previous subpath
2466                                 last = NULL; // to be set later to the first node of first subpath
2467                             } else {
2468                                 last = node->n.other;
2469                             }
2470                         } else {
2471                             last = node->n.other;
2472                         }
2473                     } else {
2474                         if (node->n.other) {
2475                             last = node->n.other;
2476                         } else {
2477                             if (spl->next) { // there's a next subpath
2478                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2479                                 last = subpath_next->first;
2480                             } else if (spl->prev) { // there's a previous subpath
2481                                 last = NULL; // to be set later to the first node of first subpath
2482                             } else {
2483                                 last = (Inkscape::NodePath::Node *) subpath->first;
2484                             }
2485                         }
2486                     }
2487                 }
2488             }
2489         }
2490         sp_nodepath_deselect(nodepath);
2491     }
2493     if (last) { // there's at least one more node after selected
2494         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2495     } else { // no more nodes, select the first one in first subpath
2496        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2497         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2498     }
2501 /**
2502  * \brief Select the node before the first selected; if none is selected,
2503  * select the last within path
2504  */
2505 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2507     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2509    Inkscape::NodePath::Node *last = NULL;
2510     if (nodepath->selected) {
2511         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2512            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2513             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2514                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2515                 if (node->selected) {
2516                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2517                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2518                             if (spl->prev) { // there's a prev subpath
2519                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2520                                 last = subpath_prev->last;
2521                             } else if (spl->next) { // there's a next subpath
2522                                 last = NULL; // to be set later to the last node of last subpath
2523                             } else {
2524                                 last = node->p.other;
2525                             }
2526                         } else {
2527                             last = node->p.other;
2528                         }
2529                     } else {
2530                         if (node->p.other) {
2531                             last = node->p.other;
2532                         } else {
2533                             if (spl->prev) { // there's a prev subpath
2534                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2535                                 last = subpath_prev->last;
2536                             } else if (spl->next) { // there's a next subpath
2537                                 last = NULL; // to be set later to the last node of last subpath
2538                             } else {
2539                                 last = (Inkscape::NodePath::Node *) subpath->last;
2540                             }
2541                         }
2542                     }
2543                 }
2544             }
2545         }
2546         sp_nodepath_deselect(nodepath);
2547     }
2549     if (last) { // there's at least one more node before selected
2550         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2551     } else { // no more nodes, select the last one in last subpath
2552         GList *spl = g_list_last(nodepath->subpaths);
2553        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2554         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2555     }
2558 /**
2559  * \brief Select all nodes that are within the rectangle.
2560  */
2561 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2563     if (!incremental) {
2564         sp_nodepath_deselect(nodepath);
2565     }
2567     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2568        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2569         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2570            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2572             if (b.contains(node->pos)) {
2573                 sp_nodepath_node_select(node, TRUE, TRUE);
2574             }
2575         }
2576     }
2580 void
2581 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2583     g_assert (n);
2584     g_assert (nodepath);
2585     g_assert (n->subpath->nodepath == nodepath);
2587     if (g_list_length (nodepath->selected) == 0) {
2588         if (grow > 0) {
2589             sp_nodepath_node_select(n, TRUE, TRUE);
2590         }
2591         return;
2592     }
2594     if (g_list_length (nodepath->selected) == 1) {
2595         if (grow < 0) {
2596             sp_nodepath_deselect (nodepath);
2597             return;
2598         }
2599     }
2601         double n_sel_range = 0, p_sel_range = 0;
2602             Inkscape::NodePath::Node *farthest_n_node = n;
2603             Inkscape::NodePath::Node *farthest_p_node = n;
2605         // Calculate ranges
2606         {
2607             double n_range = 0, p_range = 0;
2608             bool n_going = true, p_going = true;
2609             Inkscape::NodePath::Node *n_node = n;
2610             Inkscape::NodePath::Node *p_node = n;
2611             do {
2612                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2613                 if (n_node && n_going)
2614                     n_node = n_node->n.other;
2615                 if (n_node == NULL) {
2616                     n_going = false;
2617                 } else {
2618                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2619                     if (n_node->selected) {
2620                         n_sel_range = n_range;
2621                         farthest_n_node = n_node;
2622                     }
2623                     if (n_node == p_node) {
2624                         n_going = false;
2625                         p_going = false;
2626                     }
2627                 }
2628                 if (p_node && p_going)
2629                     p_node = p_node->p.other;
2630                 if (p_node == NULL) {
2631                     p_going = false;
2632                 } else {
2633                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2634                     if (p_node->selected) {
2635                         p_sel_range = p_range;
2636                         farthest_p_node = p_node;
2637                     }
2638                     if (p_node == n_node) {
2639                         n_going = false;
2640                         p_going = false;
2641                     }
2642                 }
2643             } while (n_going || p_going);
2644         }
2646     if (grow > 0) {
2647         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2648                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2649         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2650                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2651         }
2652     } else {
2653         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2654                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2655         } else if (farthest_p_node && farthest_p_node->selected) {
2656                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2657         }
2658     }
2661 void
2662 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2664     g_assert (n);
2665     g_assert (nodepath);
2666     g_assert (n->subpath->nodepath == nodepath);
2668     if (g_list_length (nodepath->selected) == 0) {
2669         if (grow > 0) {
2670             sp_nodepath_node_select(n, TRUE, TRUE);
2671         }
2672         return;
2673     }
2675     if (g_list_length (nodepath->selected) == 1) {
2676         if (grow < 0) {
2677             sp_nodepath_deselect (nodepath);
2678             return;
2679         }
2680     }
2682     Inkscape::NodePath::Node *farthest_selected = NULL;
2683     double farthest_dist = 0;
2685     Inkscape::NodePath::Node *closest_unselected = NULL;
2686     double closest_dist = NR_HUGE;
2688     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2689        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2690         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2691            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2692            if (node == n)
2693                continue;
2694            if (node->selected) {
2695                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2696                    farthest_dist = NR::L2(node->pos - n->pos);
2697                    farthest_selected = node;
2698                }
2699            } else {
2700                if (NR::L2(node->pos - n->pos) < closest_dist) {
2701                    closest_dist = NR::L2(node->pos - n->pos);
2702                    closest_unselected = node;
2703                }
2704            }
2705         }
2706     }
2708     if (grow > 0) {
2709         if (closest_unselected) {
2710             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2711         }
2712     } else {
2713         if (farthest_selected) {
2714             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2715         }
2716     }
2720 /**
2721 \brief  Saves all nodes' and handles' current positions in their origin members
2722 */
2723 void
2724 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2726     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2727        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2728         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2729            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2730            n->origin = n->pos;
2731            n->p.origin = n->p.pos;
2732            n->n.origin = n->n.pos;
2733         }
2734     }
2735
2737 /**
2738 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2739 */
2740 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2742     if (!nodepath->selected) {
2743         return NULL;
2744     }
2746     GList *r = NULL;
2747     guint i = 0;
2748     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2749        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2750         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2751            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2752             i++;
2753             if (node->selected) {
2754                 r = g_list_append(r, GINT_TO_POINTER(i));
2755             }
2756         }
2757     }
2758     return r;
2761 /**
2762 \brief  Restores selection by selecting nodes whose positions are in the list
2763 */
2764 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2766     sp_nodepath_deselect(nodepath);
2768     guint i = 0;
2769     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2770        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2771         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2772            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2773             i++;
2774             if (g_list_find(r, GINT_TO_POINTER(i))) {
2775                 sp_nodepath_node_select(node, TRUE, TRUE);
2776             }
2777         }
2778     }
2782 /**
2783 \brief Adjusts handle according to node type and line code.
2784 */
2785 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2787     double len, otherlen, linelen;
2789     g_assert(node);
2791    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2792    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2794     /** \todo fixme: */
2795     if (me->other == NULL) return;
2796     if (other->other == NULL) return;
2798     /* I have line */
2800     NRPathcode mecode, ocode;
2801     if (which_adjust == 1) {
2802         mecode = (NRPathcode)me->other->code;
2803         ocode = (NRPathcode)node->code;
2804     } else {
2805         mecode = (NRPathcode)node->code;
2806         ocode = (NRPathcode)other->other->code;
2807     }
2809     if (mecode == NR_LINETO) return;
2811     /* I am curve */
2813     if (other->other == NULL) return;
2815     /* Other has line */
2817     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2819     NR::Point delta;
2820     if (ocode == NR_LINETO) {
2821         /* other is lineto, we are either smooth or symm */
2822        Inkscape::NodePath::Node *othernode = other->other;
2823         len = NR::L2(me->pos - node->pos);
2824         delta = node->pos - othernode->pos;
2825         linelen = NR::L2(delta);
2826         if (linelen < 1e-18) 
2827             return;
2828         me->pos = node->pos + (len / linelen)*delta;
2829         return;
2830     }
2832     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2834         me->pos = 2 * node->pos - other->pos;
2835         return;
2836     }
2838     /* We are smooth */
2840     len = NR::L2(me->pos - node->pos);
2841     delta = other->pos - node->pos;
2842     otherlen = NR::L2(delta);
2843     if (otherlen < 1e-18) return;
2845     me->pos = node->pos - (len / otherlen) * delta;
2848 /**
2849  \brief Adjusts both handles according to node type and line code
2850  */
2851 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2853     g_assert(node);
2855     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2857     /* we are either smooth or symm */
2859     if (node->p.other == NULL) return;
2861     if (node->n.other == NULL) return;
2863     if (node->code == NR_LINETO) {
2864         if (node->n.other->code == NR_LINETO) return;
2865         sp_node_adjust_handle(node, 1);
2866         return;
2867     }
2869     if (node->n.other->code == NR_LINETO) {
2870         if (node->code == NR_LINETO) return;
2871         sp_node_adjust_handle(node, -1);
2872         return;
2873     }
2875     /* both are curves */
2876     NR::Point const delta( node->n.pos - node->p.pos );
2878     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2879         node->p.pos = node->pos - delta / 2;
2880         node->n.pos = node->pos + delta / 2;
2881         return;
2882     }
2884     /* We are smooth */
2885     double plen = NR::L2(node->p.pos - node->pos);
2886     if (plen < 1e-18) return;
2887     double nlen = NR::L2(node->n.pos - node->pos);
2888     if (nlen < 1e-18) return;
2889     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2890     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2893 /**
2894  * Node event callback.
2895  */
2896 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2898     gboolean ret = FALSE;
2899     switch (event->type) {
2900         case GDK_ENTER_NOTIFY:
2901             active_node = n;
2902             break;
2903         case GDK_LEAVE_NOTIFY:
2904             active_node = NULL;
2905             break;
2906         case GDK_SCROLL:
2907             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
2908                 switch (event->scroll.direction) {
2909                     case GDK_SCROLL_UP:
2910                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2911                         break;
2912                     case GDK_SCROLL_DOWN:
2913                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2914                         break;
2915                     default:
2916                         break;
2917                 }
2918                 ret = TRUE;
2919             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
2920                 switch (event->scroll.direction) {
2921                     case GDK_SCROLL_UP:
2922                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2923                         break;
2924                     case GDK_SCROLL_DOWN:
2925                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2926                         break;
2927                     default:
2928                         break;
2929                 }
2930                 ret = TRUE;
2931             }
2932             break;
2933         case GDK_KEY_PRESS:
2934             switch (get_group0_keyval (&event->key)) {
2935                 case GDK_space:
2936                     if (event->key.state & GDK_BUTTON1_MASK) {
2937                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2938                         stamp_repr(nodepath);
2939                         ret = TRUE;
2940                     }
2941                     break;
2942                 case GDK_Page_Up:
2943                     if (event->key.state & GDK_CONTROL_MASK) {
2944                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2945                     } else {
2946                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2947                     }
2948                     break;
2949                 case GDK_Page_Down:
2950                     if (event->key.state & GDK_CONTROL_MASK) {
2951                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2952                     } else {
2953                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2954                     }
2955                     break;
2956                 default:
2957                     break;
2958             }
2959             break;
2960         default:
2961             break;
2962     }
2964     return ret;
2967 /**
2968  * Handle keypress on node; directly called.
2969  */
2970 gboolean node_key(GdkEvent *event)
2972     Inkscape::NodePath::Path *np;
2974     // there is no way to verify nodes so set active_node to nil when deleting!!
2975     if (active_node == NULL) return FALSE;
2977     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2978         gint ret = FALSE;
2979         switch (get_group0_keyval (&event->key)) {
2980             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2981             case GDK_BackSpace:
2982                 np = active_node->subpath->nodepath;
2983                 sp_nodepath_node_destroy(active_node);
2984                 sp_nodepath_update_repr(np, _("Delete node"));
2985                 active_node = NULL;
2986                 ret = TRUE;
2987                 break;
2988             case GDK_c:
2989                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2990                 ret = TRUE;
2991                 break;
2992             case GDK_s:
2993                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2994                 ret = TRUE;
2995                 break;
2996             case GDK_y:
2997                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2998                 ret = TRUE;
2999                 break;
3000             case GDK_b:
3001                 sp_nodepath_node_break(active_node);
3002                 ret = TRUE;
3003                 break;
3004         }
3005         return ret;
3006     }
3007     return FALSE;
3010 /**
3011  * Mouseclick on node callback.
3012  */
3013 static void node_clicked(SPKnot *knot, guint state, gpointer data)
3015    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3017     if (state & GDK_CONTROL_MASK) {
3018         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3020         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3021             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3022                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3023             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3024                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3025             } else {
3026                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3027             }
3028             sp_nodepath_update_repr(nodepath, _("Change node type"));
3029             sp_nodepath_update_statusbar(nodepath);
3031         } else { //ctrl+alt+click: delete node
3032             GList *node_to_delete = NULL;
3033             node_to_delete = g_list_append(node_to_delete, n);
3034             sp_node_delete_preserve(node_to_delete);
3035         }
3037     } else {
3038         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3039     }
3042 /**
3043  * Mouse grabbed node callback.
3044  */
3045 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3047    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3049     if (!n->selected) {
3050         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3051     }
3053     n->is_dragging = true;
3054     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3056     sp_nodepath_remember_origins (n->subpath->nodepath);
3059 /**
3060  * Mouse ungrabbed node callback.
3061  */
3062 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
3064    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3066    n->dragging_out = NULL;
3067    n->is_dragging = false;
3068    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3070    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3073 /**
3074  * The point on a line, given by its angle, closest to the given point.
3075  * \param p  A point.
3076  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3077  * \param closest  Pointer to the point struct where the result is stored.
3078  * \todo FIXME: use dot product perhaps?
3079  */
3080 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3082     if (a == HUGE_VAL) { // vertical
3083         *closest = NR::Point(0, (*p)[NR::Y]);
3084     } else {
3085         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3086         (*closest)[NR::Y] = a * (*closest)[NR::X];
3087     }
3090 /**
3091  * Distance from the point to a line given by its angle.
3092  * \param p  A point.
3093  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3094  */
3095 static double point_line_distance(NR::Point *p, double a)
3097     NR::Point c;
3098     point_line_closest(p, a, &c);
3099     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]));
3102 /**
3103  * Callback for node "request" signal.
3104  * \todo fixme: This goes to "moved" event? (lauris)
3105  */
3106 static gboolean
3107 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3109     double yn, xn, yp, xp;
3110     double an, ap, na, pa;
3111     double d_an, d_ap, d_na, d_pa;
3112     gboolean collinear = FALSE;
3113     NR::Point c;
3114     NR::Point pr;
3116    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3118    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3119    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3121        NR::Point mouse = (*p);
3123        if (!n->dragging_out) {
3124            // This is the first drag-out event; find out which handle to drag out
3125            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3126            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3128            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3129                return FALSE;
3131            Inkscape::NodePath::NodeSide *opposite;
3132            if (appr_p > appr_n) { // closer to p
3133                n->dragging_out = &n->p;
3134                opposite = &n->n;
3135                n->code = NR_CURVETO;
3136            } else if (appr_p < appr_n) { // closer to n
3137                n->dragging_out = &n->n;
3138                opposite = &n->p;
3139                n->n.other->code = NR_CURVETO;
3140            } else { // p and n nodes are the same
3141                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3142                    n->dragging_out = &n->p;
3143                    opposite = &n->n;
3144                    n->code = NR_CURVETO;
3145                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3146                    n->dragging_out = &n->n;
3147                    opposite = &n->p;
3148                    n->n.other->code = NR_CURVETO;
3149                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3150                    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);
3151                    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);
3152                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3153                        n->dragging_out = &n->n;
3154                        opposite = &n->p;
3155                        n->n.other->code = NR_CURVETO;
3156                    } else { // closer to other's n handle
3157                        n->dragging_out = &n->p;
3158                        opposite = &n->n;
3159                        n->code = NR_CURVETO;
3160                    }
3161                }
3162            }
3164            // if there's another handle, make sure the one we drag out starts parallel to it
3165            if (opposite->pos != n->pos) {
3166                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3167            }
3169            // knots might not be created yet!
3170            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3171            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3172        }
3174        // pass this on to the handle-moved callback
3175        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3176        sp_node_update_handles(n);
3177        return TRUE;
3178    }
3180     if (state & GDK_CONTROL_MASK) { // constrained motion
3182         // calculate relative distances of handles
3183         // n handle:
3184         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3185         xn = n->n.pos[NR::X] - n->pos[NR::X];
3186         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3187         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3188             if (n->n.other) { // if there is the next point
3189                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3190                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3191                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3192             }
3193         }
3194         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3195         if (yn < 0) { xn = -xn; yn = -yn; }
3197         // p handle:
3198         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3199         xp = n->p.pos[NR::X] - n->pos[NR::X];
3200         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3201         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3202             if (n->p.other) {
3203                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3204                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3205                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3206             }
3207         }
3208         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3209         if (yp < 0) { xp = -xp; yp = -yp; }
3211         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3212             // sliding on handles, only if at least one of the handles is non-vertical
3213             // (otherwise it's the same as ctrl+drag anyway)
3215             // calculate angles of the handles
3216             if (xn == 0) {
3217                 if (yn == 0) { // no handle, consider it the continuation of the other one
3218                     an = 0;
3219                     collinear = TRUE;
3220                 }
3221                 else an = 0; // vertical; set the angle to horizontal
3222             } else an = yn/xn;
3224             if (xp == 0) {
3225                 if (yp == 0) { // no handle, consider it the continuation of the other one
3226                     ap = an;
3227                 }
3228                 else ap = 0; // vertical; set the angle to horizontal
3229             } else  ap = yp/xp;
3231             if (collinear) an = ap;
3233             // angles of the perpendiculars; HUGE_VAL means vertical
3234             if (an == 0) na = HUGE_VAL; else na = -1/an;
3235             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3237             // mouse point relative to the node's original pos
3238             pr = (*p) - n->origin;
3240             // distances to the four lines (two handles and two perpendiculars)
3241             d_an = point_line_distance(&pr, an);
3242             d_na = point_line_distance(&pr, na);
3243             d_ap = point_line_distance(&pr, ap);
3244             d_pa = point_line_distance(&pr, pa);
3246             // find out which line is the closest, save its closest point in c
3247             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3248                 point_line_closest(&pr, an, &c);
3249             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3250                 point_line_closest(&pr, ap, &c);
3251             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3252                 point_line_closest(&pr, na, &c);
3253             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3254                 point_line_closest(&pr, pa, &c);
3255             }
3257             // move the node to the closest point
3258             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3259                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3260                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3262         } else {  // constraining to hor/vert
3264             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3265                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3266             } else { // snap to vert
3267                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3268             }
3269         }
3270     } else { // move freely
3271         if (n->is_dragging) {
3272             if (state & GDK_MOD1_MASK) { // sculpt
3273                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3274             } else {
3275                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3276                                             (*p)[NR::X] - n->pos[NR::X],
3277                                             (*p)[NR::Y] - n->pos[NR::Y],
3278                                             (state & GDK_SHIFT_MASK) == 0);
3279             }
3280         }
3281     }
3283     n->subpath->nodepath->desktop->scroll_to_point(p);
3285     return TRUE;
3288 /**
3289  * Node handle clicked callback.
3290  */
3291 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3293    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3295     if (state & GDK_CONTROL_MASK) { // "delete" handle
3296         if (n->p.knot == knot) {
3297             n->p.pos = n->pos;
3298         } else if (n->n.knot == knot) {
3299             n->n.pos = n->pos;
3300         }
3301         sp_node_update_handles(n);
3302         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3303         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3304         sp_nodepath_update_statusbar(nodepath);
3306     } else { // just select or add to selection, depending in Shift
3307         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3308     }
3311 /**
3312  * Node handle grabbed callback.
3313  */
3314 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3316    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3318     if (!n->selected) {
3319         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3320     }
3322     // remember the origin point of the handle
3323     if (n->p.knot == knot) {
3324         n->p.origin_radial = n->p.pos - n->pos;
3325     } else if (n->n.knot == knot) {
3326         n->n.origin_radial = n->n.pos - n->pos;
3327     } else {
3328         g_assert_not_reached();
3329     }
3331     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3334 /**
3335  * Node handle ungrabbed callback.
3336  */
3337 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3339    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3341     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3342     if (n->p.knot == knot) {
3343         n->p.origin_radial.a = 0;
3344         sp_knot_set_position(knot, &n->p.pos, state);
3345     } else if (n->n.knot == knot) {
3346         n->n.origin_radial.a = 0;
3347         sp_knot_set_position(knot, &n->n.pos, state);
3348     } else {
3349         g_assert_not_reached();
3350     }
3352     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3355 /**
3356  * Node handle "request" signal callback.
3357  */
3358 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3360     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3362     Inkscape::NodePath::NodeSide *me, *opposite;
3363     gint which;
3364     if (n->p.knot == knot) {
3365         me = &n->p;
3366         opposite = &n->n;
3367         which = -1;
3368     } else if (n->n.knot == knot) {
3369         me = &n->n;
3370         opposite = &n->p;
3371         which = 1;
3372     } else {
3373         me = opposite = NULL;
3374         which = 0;
3375         g_assert_not_reached();
3376     }
3378     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3380     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3382     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3383         /* We are smooth node adjacent with line */
3384         NR::Point const delta = *p - n->pos;
3385         NR::Coord const len = NR::L2(delta);
3386         Inkscape::NodePath::Node *othernode = opposite->other;
3387         NR::Point const ndelta = n->pos - othernode->pos;
3388         NR::Coord const linelen = NR::L2(ndelta);
3389         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3390             NR::Coord const scal = dot(delta, ndelta) / linelen;
3391             (*p) = n->pos + (scal / linelen) * ndelta;
3392         }
3393         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3394     } else {
3395         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3396     }
3398     sp_node_adjust_handle(n, -which);
3400     return FALSE;
3403 /**
3404  * Node handle moved callback.
3405  */
3406 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3408    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3410    Inkscape::NodePath::NodeSide *me;
3411    Inkscape::NodePath::NodeSide *other;
3412     if (n->p.knot == knot) {
3413         me = &n->p;
3414         other = &n->n;
3415     } else if (n->n.knot == knot) {
3416         me = &n->n;
3417         other = &n->p;
3418     } else {
3419         me = NULL;
3420         other = NULL;
3421         g_assert_not_reached();
3422     }
3424     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3425     Radial rme(me->pos - n->pos);
3426     Radial rother(other->pos - n->pos);
3427     Radial rnew(*p - n->pos);
3429     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3430         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3431         /* 0 interpreted as "no snapping". */
3433         // The closest PI/snaps angle, starting from zero.
3434         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3435         if (me->origin_radial.a == HUGE_VAL) {
3436             // ortho doesn't exist: original handle was zero length.
3437             rnew.a = a_snapped;
3438         } else {
3439             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3440              * its opposite and perpendiculars). */
3441             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3443             // Snap to the closest.
3444             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3445                        ? a_snapped
3446                        : a_ortho );
3447         }
3448     }
3450     if (state & GDK_MOD1_MASK) {
3451         // lock handle length
3452         rnew.r = me->origin_radial.r;
3453     }
3455     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3456         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3457         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3458         rother.a += rnew.a - rme.a;
3459         other->pos = NR::Point(rother) + n->pos;
3460         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3461         sp_knot_set_position(other->knot, &other->pos, 0);
3462     }
3464     me->pos = NR::Point(rnew) + n->pos;
3465     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3467     // move knot, but without emitting the signal:
3468     // we cannot emit a "moved" signal because we're now processing it
3469     sp_knot_moveto(me->knot, &(me->pos));
3471     update_object(n->subpath->nodepath);
3473     /* status text */
3474     SPDesktop *desktop = n->subpath->nodepath->desktop;
3475     if (!desktop) return;
3476     SPEventContext *ec = desktop->event_context;
3477     if (!ec) return;
3478     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3479     if (!mc) return;
3481     double degrees = 180 / M_PI * rnew.a;
3482     if (degrees > 180) degrees -= 360;
3483     if (degrees < -180) degrees += 360;
3484     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3485         degrees = angle_to_compass (degrees);
3487     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3489     mc->setF(Inkscape::NORMAL_MESSAGE,
3490          _("<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);
3492     g_string_free(length, TRUE);
3495 /**
3496  * Node handle event callback.
3497  */
3498 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3500     gboolean ret = FALSE;
3501     switch (event->type) {
3502         case GDK_KEY_PRESS:
3503             switch (get_group0_keyval (&event->key)) {
3504                 case GDK_space:
3505                     if (event->key.state & GDK_BUTTON1_MASK) {
3506                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3507                         stamp_repr(nodepath);
3508                         ret = TRUE;
3509                     }
3510                     break;
3511                 default:
3512                     break;
3513             }
3514             break;
3515         default:
3516             break;
3517     }
3519     return ret;
3522 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3523                                  Radial &rme, Radial &rother, gboolean const both)
3525     rme.a += angle;
3526     if ( both
3527          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3528          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3529     {
3530         rother.a += angle;
3531     }
3534 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3535                                         Radial &rme, Radial &rother, gboolean const both)
3537     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3539     gdouble r;
3540     if ( both
3541          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3542          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3543     {
3544         r = MAX(rme.r, rother.r);
3545     } else {
3546         r = rme.r;
3547     }
3549     gdouble const weird_angle = atan2(norm_angle, r);
3550 /* Bulia says norm_angle is just the visible distance that the
3551  * object's end must travel on the screen.  Left as 'angle' for want of
3552  * a better name.*/
3554     rme.a += weird_angle;
3555     if ( both
3556          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3557          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3558     {
3559         rother.a += weird_angle;
3560     }
3563 /**
3564  * Rotate one node.
3565  */
3566 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3568     Inkscape::NodePath::NodeSide *me, *other;
3569     bool both = false;
3571     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3572     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3574     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3575         me = &(n->p);
3576         other = &(n->n);
3577     } else if (!n->p.other) {
3578         me = &(n->n);
3579         other = &(n->p);
3580     } else {
3581         if (which > 0) { // right handle
3582             if (xn > xp) {
3583                 me = &(n->n);
3584                 other = &(n->p);
3585             } else {
3586                 me = &(n->p);
3587                 other = &(n->n);
3588             }
3589         } else if (which < 0){ // left handle
3590             if (xn <= xp) {
3591                 me = &(n->n);
3592                 other = &(n->p);
3593             } else {
3594                 me = &(n->p);
3595                 other = &(n->n);
3596             }
3597         } else { // both handles
3598             me = &(n->n);
3599             other = &(n->p);
3600             both = true;
3601         }
3602     }
3604     Radial rme(me->pos - n->pos);
3605     Radial rother(other->pos - n->pos);
3607     if (screen) {
3608         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3609     } else {
3610         node_rotate_one_internal (*n, angle, rme, rother, both);
3611     }
3613     me->pos = n->pos + NR::Point(rme);
3615     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3616         other->pos =  n->pos + NR::Point(rother);
3617     }
3619     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3620     // so here we just move all the knots without emitting move signals, for speed
3621     sp_node_update_handles(n, false);
3624 /**
3625  * Rotate selected nodes.
3626  */
3627 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3629     if (!nodepath || !nodepath->selected) return;
3631     if (g_list_length(nodepath->selected) == 1) {
3632        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3633         node_rotate_one (n, angle, which, screen);
3634     } else {
3635        // rotate as an object:
3637         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3638         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3639         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3640             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3641             box.expandTo (n->pos); // contain all selected nodes
3642         }
3644         gdouble rot;
3645         if (screen) {
3646             gdouble const zoom = nodepath->desktop->current_zoom();
3647             gdouble const zmove = angle / zoom;
3648             gdouble const r = NR::L2(box.max() - box.midpoint());
3649             rot = atan2(zmove, r);
3650         } else {
3651             rot = angle;
3652         }
3654         NR::Matrix t =
3655             NR::Matrix (NR::translate(-box.midpoint())) *
3656             NR::Matrix (NR::rotate(rot)) *
3657             NR::Matrix (NR::translate(box.midpoint()));
3659         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3660             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3661             n->pos *= t;
3662             n->n.pos *= t;
3663             n->p.pos *= t;
3664             sp_node_update_handles(n, false);
3665         }
3666     }
3668     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3671 /**
3672  * Scale one node.
3673  */
3674 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3676     bool both = false;
3677     Inkscape::NodePath::NodeSide *me, *other;
3679     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3680     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3682     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3683         me = &(n->p);
3684         other = &(n->n);
3685         n->code = NR_CURVETO;
3686     } else if (!n->p.other) {
3687         me = &(n->n);
3688         other = &(n->p);
3689         if (n->n.other)
3690             n->n.other->code = NR_CURVETO;
3691     } else {
3692         if (which > 0) { // right handle
3693             if (xn > xp) {
3694                 me = &(n->n);
3695                 other = &(n->p);
3696                 if (n->n.other)
3697                     n->n.other->code = NR_CURVETO;
3698             } else {
3699                 me = &(n->p);
3700                 other = &(n->n);
3701                 n->code = NR_CURVETO;
3702             }
3703         } else if (which < 0){ // left handle
3704             if (xn <= xp) {
3705                 me = &(n->n);
3706                 other = &(n->p);
3707                 if (n->n.other)
3708                     n->n.other->code = NR_CURVETO;
3709             } else {
3710                 me = &(n->p);
3711                 other = &(n->n);
3712                 n->code = NR_CURVETO;
3713             }
3714         } else { // both handles
3715             me = &(n->n);
3716             other = &(n->p);
3717             both = true;
3718             n->code = NR_CURVETO;
3719             if (n->n.other)
3720                 n->n.other->code = NR_CURVETO;
3721         }
3722     }
3724     Radial rme(me->pos - n->pos);
3725     Radial rother(other->pos - n->pos);
3727     rme.r += grow;
3728     if (rme.r < 0) rme.r = 0;
3729     if (rme.a == HUGE_VAL) {
3730         if (me->other) { // if direction is unknown, initialize it towards the next node
3731             Radial rme_next(me->other->pos - n->pos);
3732             rme.a = rme_next.a;
3733         } else { // if there's no next, initialize to 0
3734             rme.a = 0;
3735         }
3736     }
3737     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3738         rother.r += grow;
3739         if (rother.r < 0) rother.r = 0;
3740         if (rother.a == HUGE_VAL) {
3741             rother.a = rme.a + M_PI;
3742         }
3743     }
3745     me->pos = n->pos + NR::Point(rme);
3747     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3748         other->pos = n->pos + NR::Point(rother);
3749     }
3751     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3752     // so here we just move all the knots without emitting move signals, for speed
3753     sp_node_update_handles(n, false);
3756 /**
3757  * Scale selected nodes.
3758  */
3759 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3761     if (!nodepath || !nodepath->selected) return;
3763     if (g_list_length(nodepath->selected) == 1) {
3764         // scale handles of the single selected node
3765         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3766         node_scale_one (n, grow, which);
3767     } else {
3768         // scale nodes as an "object":
3770         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3771         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3772         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3773             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3774             box.expandTo (n->pos); // contain all selected nodes
3775         }
3777         double scale = (box.maxExtent() + grow)/box.maxExtent();
3779         NR::Matrix t =
3780             NR::Matrix (NR::translate(-box.midpoint())) *
3781             NR::Matrix (NR::scale(scale, scale)) *
3782             NR::Matrix (NR::translate(box.midpoint()));
3784         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3785             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3786             n->pos *= t;
3787             n->n.pos *= t;
3788             n->p.pos *= t;
3789             sp_node_update_handles(n, false);
3790         }
3791     }
3793     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3796 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3798     if (!nodepath) return;
3799     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3802 /**
3803  * Flip selected nodes horizontally/vertically.
3804  */
3805 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3807     if (!nodepath || !nodepath->selected) return;
3809     if (g_list_length(nodepath->selected) == 1) {
3810         // flip handles of the single selected node
3811         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3812         double temp = n->p.pos[axis];
3813         n->p.pos[axis] = n->n.pos[axis];
3814         n->n.pos[axis] = temp;
3815         sp_node_update_handles(n, false);
3816     } else {
3817         // scale nodes as an "object":
3819         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3820         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3821         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3822             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3823             box.expandTo (n->pos); // contain all selected nodes
3824         }
3826         NR::Matrix t =
3827             NR::Matrix (NR::translate(-box.midpoint())) *
3828             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3829             NR::Matrix (NR::translate(box.midpoint()));
3831         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3832             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3833             n->pos *= t;
3834             n->n.pos *= t;
3835             n->p.pos *= t;
3836             sp_node_update_handles(n, false);
3837         }
3838     }
3840     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3843 //-----------------------------------------------
3844 /**
3845  * Return new subpath under given nodepath.
3846  */
3847 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3849     g_assert(nodepath);
3850     g_assert(nodepath->desktop);
3852    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3854     s->nodepath = nodepath;
3855     s->closed = FALSE;
3856     s->nodes = NULL;
3857     s->first = NULL;
3858     s->last = NULL;
3860     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3861     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3862     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3864     return s;
3867 /**
3868  * Destroy nodes in subpath, then subpath itself.
3869  */
3870 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3872     g_assert(subpath);
3873     g_assert(subpath->nodepath);
3874     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3876     while (subpath->nodes) {
3877         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3878     }
3880     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3882     g_free(subpath);
3885 /**
3886  * Link head to tail in subpath.
3887  */
3888 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3890     g_assert(!sp->closed);
3891     g_assert(sp->last != sp->first);
3892     g_assert(sp->first->code == NR_MOVETO);
3894     sp->closed = TRUE;
3896     //Link the head to the tail
3897     sp->first->p.other = sp->last;
3898     sp->last->n.other  = sp->first;
3899     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3900     sp->first          = sp->last;
3902     //Remove the extra end node
3903     sp_nodepath_node_destroy(sp->last->n.other);
3906 /**
3907  * Open closed (loopy) subpath at node.
3908  */
3909 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3911     g_assert(sp->closed);
3912     g_assert(n->subpath == sp);
3913     g_assert(sp->first == sp->last);
3915     /* We create new startpoint, current node will become last one */
3917    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3918                                                 &n->pos, &n->pos, &n->n.pos);
3921     sp->closed        = FALSE;
3923     //Unlink to make a head and tail
3924     sp->first         = new_path;
3925     sp->last          = n;
3926     n->n.other        = NULL;
3927     new_path->p.other = NULL;
3930 /**
3931  * Returns area in triangle given by points; may be negative.
3932  */
3933 inline double
3934 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3936     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]);
3939 /**
3940  * Return new node in subpath with given properties.
3941  * \param pos Position of node.
3942  * \param ppos Handle position in previous direction
3943  * \param npos Handle position in previous direction
3944  */
3945 Inkscape::NodePath::Node *
3946 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)
3948     g_assert(sp);
3949     g_assert(sp->nodepath);
3950     g_assert(sp->nodepath->desktop);
3952     if (nodechunk == NULL)
3953         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3955     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3957     n->subpath  = sp;
3959     if (type != Inkscape::NodePath::NODE_NONE) {
3960         // use the type from sodipodi:nodetypes
3961         n->type = type;
3962     } else {
3963         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3964             // points are (almost) collinear
3965             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3966                 // endnode, or a node with a retracted handle
3967                 n->type = Inkscape::NodePath::NODE_CUSP;
3968             } else {
3969                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3970             }
3971         } else {
3972             n->type = Inkscape::NodePath::NODE_CUSP;
3973         }
3974     }
3976     n->code     = code;
3977     n->selected = FALSE;
3978     n->pos      = *pos;
3979     n->p.pos    = *ppos;
3980     n->n.pos    = *npos;
3982     n->dragging_out = NULL;
3984     Inkscape::NodePath::Node *prev;
3985     if (next) {
3986         //g_assert(g_list_find(sp->nodes, next));
3987         prev = next->p.other;
3988     } else {
3989         prev = sp->last;
3990     }
3992     if (prev)
3993         prev->n.other = n;
3994     else
3995         sp->first = n;
3997     if (next)
3998         next->p.other = n;
3999     else
4000         sp->last = n;
4002     n->p.other = prev;
4003     n->n.other = next;
4005     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"));
4006     sp_knot_set_position(n->knot, pos, 0);
4008     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4009     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4010     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4011     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4012     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4013     sp_knot_update_ctrl(n->knot);
4015     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4016     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4017     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4018     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4019     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4020     sp_knot_show(n->knot);
4022     // We only create handle knots and lines on demand
4023     n->p.knot = NULL;
4024     n->p.line = NULL;
4025     n->n.knot = NULL;
4026     n->n.line = NULL;
4028     sp->nodes = g_list_prepend(sp->nodes, n);
4030     return n;
4033 /**
4034  * Destroy node and its knots, link neighbors in subpath.
4035  */
4036 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4038     g_assert(node);
4039     g_assert(node->subpath);
4040     g_assert(SP_IS_KNOT(node->knot));
4042    Inkscape::NodePath::SubPath *sp = node->subpath;
4044     if (node->selected) { // first, deselect
4045         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4046         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4047     }
4049     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4051     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4052     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4053     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4054     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4055     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4056     g_object_unref(G_OBJECT(node->knot));
4058     if (node->p.knot) {
4059         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4060         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4061         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4062         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4063         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4064         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4065         g_object_unref(G_OBJECT(node->p.knot));
4066         node->p.knot = NULL;
4067     }
4069     if (node->n.knot) {
4070         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4071         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4072         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4073         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4074         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4075         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4076         g_object_unref(G_OBJECT(node->n.knot));
4077         node->n.knot = NULL;
4078     }
4080     if (node->p.line)
4081         gtk_object_destroy(GTK_OBJECT(node->p.line));
4082     if (node->n.line)
4083         gtk_object_destroy(GTK_OBJECT(node->n.line));
4085     if (sp->nodes) { // there are others nodes on the subpath
4086         if (sp->closed) {
4087             if (sp->first == node) {
4088                 g_assert(sp->last == node);
4089                 sp->first = node->n.other;
4090                 sp->last = sp->first;
4091             }
4092             node->p.other->n.other = node->n.other;
4093             node->n.other->p.other = node->p.other;
4094         } else {
4095             if (sp->first == node) {
4096                 sp->first = node->n.other;
4097                 sp->first->code = NR_MOVETO;
4098             }
4099             if (sp->last == node) sp->last = node->p.other;
4100             if (node->p.other) node->p.other->n.other = node->n.other;
4101             if (node->n.other) node->n.other->p.other = node->p.other;
4102         }
4103     } else { // this was the last node on subpath
4104         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4105     }
4107     g_mem_chunk_free(nodechunk, node);
4110 /**
4111  * Returns one of the node's two sides.
4112  * \param which Indicates which side.
4113  * \return Pointer to previous node side if which==-1, next if which==1.
4114  */
4115 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4117     g_assert(node);
4119     switch (which) {
4120         case -1:
4121             return &node->p;
4122         case 1:
4123             return &node->n;
4124         default:
4125             break;
4126     }
4128     g_assert_not_reached();
4130     return NULL;
4133 /**
4134  * Return the other side of the node, given one of its sides.
4135  */
4136 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4138     g_assert(node);
4140     if (me == &node->p) return &node->n;
4141     if (me == &node->n) return &node->p;
4143     g_assert_not_reached();
4145     return NULL;
4148 /**
4149  * Return NRPathcode on the given side of the node.
4150  */
4151 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4153     g_assert(node);
4155     if (me == &node->p) {
4156         if (node->p.other) return (NRPathcode)node->code;
4157         return NR_MOVETO;
4158     }
4160     if (me == &node->n) {
4161         if (node->n.other) return (NRPathcode)node->n.other->code;
4162         return NR_MOVETO;
4163     }
4165     g_assert_not_reached();
4167     return NR_END;
4170 /**
4171  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4172  */
4173 Inkscape::NodePath::Node *
4174 sp_nodepath_get_node_by_index(int index)
4176     Inkscape::NodePath::Node *e = NULL;
4178     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4179     if (!nodepath) {
4180         return e;
4181     }
4183     //find segment
4184     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4186         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4187         int n = g_list_length(sp->nodes);
4188         if (sp->closed) {
4189             n++;
4190         }
4192         //if the piece belongs to this subpath grab it
4193         //otherwise move onto the next subpath
4194         if (index < n) {
4195             e = sp->first;
4196             for (int i = 0; i < index; ++i) {
4197                 e = e->n.other;
4198             }
4199             break;
4200         } else {
4201             if (sp->closed) {
4202                 index -= (n+1);
4203             } else {
4204                 index -= n;
4205             }
4206         }
4207     }
4209     return e;
4212 /**
4213  * Returns plain text meaning of node type.
4214  */
4215 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4217     unsigned retracted = 0;
4218     bool endnode = false;
4220     for (int which = -1; which <= 1; which += 2) {
4221         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4222         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4223             retracted ++;
4224         if (!side->other)
4225             endnode = true;
4226     }
4228     if (retracted == 0) {
4229         if (endnode) {
4230                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4231                 return _("end node");
4232         } else {
4233             switch (node->type) {
4234                 case Inkscape::NodePath::NODE_CUSP:
4235                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4236                     return _("cusp");
4237                 case Inkscape::NodePath::NODE_SMOOTH:
4238                     // TRANSLATORS: "smooth" is an adjective here
4239                     return _("smooth");
4240                 case Inkscape::NodePath::NODE_SYMM:
4241                     return _("symmetric");
4242             }
4243         }
4244     } else if (retracted == 1) {
4245         if (endnode) {
4246             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4247             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4248         } else {
4249             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4250         }
4251     } else {
4252         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4253     }
4255     return NULL;
4258 /**
4259  * Handles content of statusbar as long as node tool is active.
4260  */
4261 void
4262 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4264     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");
4265     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4267     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4268     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4269     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4270     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4272     SPDesktop *desktop = NULL;
4273     if (nodepath) {
4274         desktop = nodepath->desktop;
4275     } else {
4276         desktop = SP_ACTIVE_DESKTOP;
4277     }
4279     SPEventContext *ec = desktop->event_context;
4280     if (!ec) return;
4281     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4282     if (!mc) return;
4284     if (selected_nodes == 0) {
4285         Inkscape::Selection *sel = desktop->selection;
4286         if (!sel || sel->isEmpty()) {
4287             mc->setF(Inkscape::NORMAL_MESSAGE,
4288                      _("Select a single object to edit its nodes or handles."));
4289         } else {
4290             if (nodepath) {
4291             mc->setF(Inkscape::NORMAL_MESSAGE,
4292                      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.",
4293                               "<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.",
4294                               total_nodes),
4295                      total_nodes);
4296             } else {
4297                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4298                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4299                 } else {
4300                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4301                 }
4302             }
4303         }
4304     } else if (nodepath && selected_nodes == 1) {
4305         mc->setF(Inkscape::NORMAL_MESSAGE,
4306                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4307                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4308                           total_nodes),
4309                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4310     } else {
4311         if (selected_subpaths > 1) {
4312             mc->setF(Inkscape::NORMAL_MESSAGE,
4313                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4314                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4315                               total_nodes),
4316                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4317         } else {
4318             mc->setF(Inkscape::NORMAL_MESSAGE,
4319                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4320                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4321                               total_nodes),
4322                      selected_nodes, total_nodes, when_selected);
4323         }
4324     }
4328 /*
4329   Local Variables:
4330   mode:c++
4331   c-file-style:"stroustrup"
4332   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4333   indent-tabs-mode:nil
4334   fill-column:99
4335   End:
4336 */
4337 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :