Code

fa80f3baad0b579078c32c1168e854c95751230b
[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/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include <glibmm/i18n.h>
23 #include "libnr/n-art-bpath.h"
24 #include "libnr/nr-path.h"
25 #include "helper/units.h"
26 #include "knot.h"
27 #include "inkscape.h"
28 #include "document.h"
29 #include "sp-namedview.h"
30 #include "desktop.h"
31 #include "desktop-handles.h"
32 #include "snap.h"
33 #include "message-stack.h"
34 #include "message-context.h"
35 #include "node-context.h"
36 #include "shape-editor.h"
37 #include "selection-chemistry.h"
38 #include "selection.h"
39 #include "xml/repr.h"
40 #include "prefs-utils.h"
41 #include "sp-metrics.h"
42 #include "sp-path.h"
43 #include "libnr/nr-matrix-ops.h"
44 #include "splivarot.h"
45 #include "svg/svg.h"
46 #include "verbs.h"
47 #include "display/bezier-utils.h"
48 #include <vector>
49 #include <algorithm>
50 #include "live_effects/lpeobject.h"
52 class NR::Matrix;
54 /// \todo
55 /// evil evil evil. FIXME: conflict of two different Path classes!
56 /// There is a conflict in the namespace between two classes named Path.
57 /// #include "sp-flowtext.h"
58 /// #include "sp-flowregion.h"
60 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
61 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
62 GType sp_flowregion_get_type (void);
63 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
64 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
65 GType sp_flowtext_get_type (void);
66 // end evil workaround
68 #include "helper/stlport.h"
71 /// \todo fixme: Implement these via preferences */
73 #define NODE_FILL          0xbfbfbf00
74 #define NODE_STROKE        0x000000ff
75 #define NODE_FILL_HI       0xff000000
76 #define NODE_STROKE_HI     0x000000ff
77 #define NODE_FILL_SEL      0x0000ffff
78 #define NODE_STROKE_SEL    0x000000ff
79 #define NODE_FILL_SEL_HI   0xff000000
80 #define NODE_STROKE_SEL_HI 0x000000ff
81 #define KNOT_FILL          0xffffffff
82 #define KNOT_STROKE        0x000000ff
83 #define KNOT_FILL_HI       0xff000000
84 #define KNOT_STROKE_HI     0x000000ff
86 static GMemChunk *nodechunk = NULL;
88 /* Creation from object */
90 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
91 static gchar *parse_nodetypes(gchar const *types, gint length);
93 /* Object updating */
95 static void stamp_repr(Inkscape::NodePath::Path *np);
96 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
97 static gchar *create_typestr(Inkscape::NodePath::Path *np);
99 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
101 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
103 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
105 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
107 /* Adjust handle placement, if the node or the other handle is moved */
108 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
109 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
111 /* Node event callbacks */
112 static void node_clicked(SPKnot *knot, guint state, gpointer data);
113 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
114 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
115 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
117 /* Handle event callbacks */
118 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
119 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
120 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
121 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
122 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
123 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
125 /* Constructors and destructors */
127 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
128 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
129 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
130 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
131 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
132                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
133 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
135 /* Helpers */
137 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
138 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
139 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
141 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
142 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
144 // active_node indicates mouseover node
145 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
147 /**
148  * \brief Creates new nodepath from item
149  */
150 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
152     Inkscape::XML::Node *repr = object->repr;
154     /** \todo
155      * FIXME: remove this. We don't want to edit paths inside flowtext.
156      * Instead we will build our flowtext with cloned paths, so that the
157      * real paths are outside the flowtext and thus editable as usual.
158      */
159     if (SP_IS_FLOWTEXT(object)) {
160         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
161             if SP_IS_FLOWREGION(child) {
162                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
163                 if (grandchild && SP_IS_PATH(grandchild)) {
164                     object = SP_ITEM(grandchild);
165                     break;
166                 }
167             }
168         }
169     }
171     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
173     if (curve == NULL)
174         return NULL;
176     NArtBpath *bpath = sp_curve_first_bpath(curve);
177     gint length = curve->end;
178     if (length == 0) {
179         sp_curve_unref(curve);
180         return NULL; // prevent crash for one-node paths
181     }
183     //Create new nodepath
184     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
185     if (!np) {
186         sp_curve_unref(curve);
187         return NULL;
188     }
190     // Set defaults
191     np->desktop     = desktop;
192     np->object      = object;
193     np->subpaths    = NULL;
194     np->selected    = NULL;
195     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
196     np->livarot_path = NULL;
197     np->local_change = 0;
198     np->show_handles = show_handles;
199     np->helper_path = NULL;
200     np->curve = sp_curve_copy(curve);
201     np->show_helperpath = false;
202     np->straight_path = false;
203     if (IS_LIVEPATHEFFECT(object) && item) {
204         np->item = item;
205     } else {
206         np->item = SP_ITEM(object);
207     }
209     // we need to update item's transform from the repr here,
210     // because they may be out of sync when we respond
211     // to a change in repr by regenerating nodepath     --bb
212     sp_object_read_attr(SP_OBJECT(np->item), "transform");
214     np->i2d  = sp_item_i2d_affine(np->item);
215     np->d2i  = np->i2d.inverse();
217     np->repr = repr;
218     if (repr_key_in) { // apparantly the object is an LPEObject
219         np->repr_key = g_strdup(repr_key_in);
220         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
221         np->show_helperpath = true;
222     } else {
223         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
224         if ( SP_SHAPE(np->object)->path_effect_href ) {
225             np->repr_key = g_strdup("inkscape:original-d");
226             np->show_helperpath = true;
227         } else {
228             np->repr_key = g_strdup("d");
229         }
230     }
232     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
233     gchar *typestr = parse_nodetypes(nodetypes, length);
235     // create the subpath(s) from the bpath
236     NArtBpath *b = bpath;
237     while (b->code != NR_END) {
238         b = subpath_from_bpath(np, b, typestr + (b - bpath));
239     }
241     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
242     np->subpaths = g_list_reverse(np->subpaths);
244     g_free(typestr);
245     sp_curve_unref(curve);
247     // create the livarot representation from the same item
248     sp_nodepath_ensure_livarot_path(np);
250     // Draw helper curve
251     if (np->show_helperpath) {
252         SPCurve *helper_curve = sp_curve_copy(np->curve);
253         sp_curve_transform(helper_curve, np->i2d );
254         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
255         sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(np->helper_path), 0xff0000ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
256         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
257         sp_canvas_item_show(np->helper_path);
258         sp_curve_unref(helper_curve);
259     }
261     return np;
264 /**
265  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
266  */
267 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
269     if (!np)  //soft fail, like delete
270         return;
272     while (np->subpaths) {
273         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
274     }
276     //Inform the ShapeEditor that made me, if any, that I am gone.
277     if (np->shape_editor)
278         np->shape_editor->nodepath_destroyed();
280     g_assert(!np->selected);
282     if (np->livarot_path) {
283         delete np->livarot_path;
284         np->livarot_path = NULL;
285     }
286     
287     if (np->helper_path) {
288         GtkObject *temp = np->helper_path;
289         np->helper_path = NULL;
290         gtk_object_destroy(temp);
291     }
292     if (np->curve) {
293         sp_curve_unref(np->curve);
294         np->curve = NULL;
295     }
297     if (np->repr_key) {
298         g_free(np->repr_key);
299         np->repr_key = NULL;
300     }
301     if (np->repr_nodetypes_key) {
302         g_free(np->repr_nodetypes_key);
303         np->repr_nodetypes_key = NULL;
304     }
306     np->desktop = NULL;
308     g_free(np);
312 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
314     if (np && np->livarot_path == NULL) {
315         SPCurve *curve = create_curve(np);
316         NArtBpath *bpath = SP_CURVE_BPATH(curve);
317         np->livarot_path = bpath_to_Path(bpath);
319         if (np->livarot_path)
320             np->livarot_path->ConvertWithBackData(0.01);
322         sp_curve_unref(curve);
323     }
327 /**
328  *  Return the node count of a given NodeSubPath.
329  */
330 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
332     if (!subpath)
333         return 0;
334     gint nodeCount = g_list_length(subpath->nodes);
335     return nodeCount;
338 /**
339  *  Return the node count of a given NodePath.
340  */
341 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
343     if (!np)
344         return 0;
345     gint nodeCount = 0;
346     for (GList *item = np->subpaths ; item ; item=item->next) {
347        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
348         nodeCount += g_list_length(subpath->nodes);
349     }
350     return nodeCount;
353 /**
354  *  Return the subpath count of a given NodePath.
355  */
356 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
358     if (!np)
359         return 0;
360     return g_list_length (np->subpaths);
363 /**
364  *  Return the selected node count of a given NodePath.
365  */
366 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
368     if (!np)
369         return 0;
370     return g_list_length (np->selected);
373 /**
374  *  Return the number of subpaths where nodes are selected in a given NodePath.
375  */
376 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
378     if (!np)
379         return 0;
380     if (!np->selected)
381         return 0;
382     if (!np->selected->next)
383         return 1;
384     gint count = 0;
385     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
386         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
387         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
388             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
389             if (node->selected) {
390                 count ++;
391                 break;
392             }
393         }
394     }
395     return count;
397  
398 /**
399  * Clean up a nodepath after editing.
400  *
401  * Currently we are deleting trivial subpaths.
402  */
403 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
405     GList *badSubPaths = NULL;
407     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
408     for (GList *l = nodepath->subpaths; l ; l=l->next) {
409        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
410        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
411             badSubPaths = g_list_append(badSubPaths, sp);
412     }
414     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
415     //also removes the subpath from nodepath->subpaths
416     for (GList *l = badSubPaths; l ; l=l->next) {
417        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
418         sp_nodepath_subpath_destroy(sp);
419     }
421     g_list_free(badSubPaths);
424 /**
425  * Create new nodepath from b, make it subpath of np.
426  * \param t The node type.
427  * \todo Fixme: t should be a proper type, rather than gchar
428  */
429 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
431     NR::Point ppos, pos, npos;
433     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
435     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
436     bool const closed = (b->code == NR_MOVETO);
438     pos = NR::Point(b->x3, b->y3) * np->i2d;
439     if (b[1].code == NR_CURVETO) {
440         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
441     } else {
442         npos = pos;
443     }
444     Inkscape::NodePath::Node *n;
445     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
446     g_assert(sp->first == n);
447     g_assert(sp->last  == n);
449     b++;
450     t++;
451     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
452         pos = NR::Point(b->x3, b->y3) * np->i2d;
453         if (b->code == NR_CURVETO) {
454             ppos = NR::Point(b->x2, b->y2) * np->i2d;
455         } else {
456             ppos = pos;
457         }
458         if (b[1].code == NR_CURVETO) {
459             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
460         } else {
461             npos = pos;
462         }
463         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
464         b++;
465         t++;
466     }
468     if (closed) sp_nodepath_subpath_close(sp);
470     return b;
473 /**
474  * Convert from sodipodi:nodetypes to new style type string.
475  */
476 static gchar *parse_nodetypes(gchar const *types, gint length)
478     g_assert(length > 0);
480     gchar *typestr = g_new(gchar, length + 1);
482     gint pos = 0;
484     if (types) {
485         for (gint i = 0; types[i] && ( i < length ); i++) {
486             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
487             if (types[i] != '\0') {
488                 switch (types[i]) {
489                     case 's':
490                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
491                         break;
492                     case 'z':
493                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
494                         break;
495                     case 'c':
496                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
497                         break;
498                     default:
499                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
500                         break;
501                 }
502             }
503         }
504     }
506     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
508     return typestr;
511 /**
512  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
513  * updated but repr is not (for speed). Used during curve and node drag.
514  */
515 static void update_object(Inkscape::NodePath::Path *np)
517     g_assert(np);
519     sp_curve_unref(np->curve);
520     np->curve = create_curve(np);
522     sp_nodepath_set_curve(np, np->curve);
524     if (np->show_helperpath) {
525         SPCurve * helper_curve = sp_curve_copy(np->curve);
526         sp_curve_transform(helper_curve, np->i2d );
527         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
528         sp_curve_unref(helper_curve);
529     }
532 /**
533  * Update XML path node with data from path object.
534  */
535 static void update_repr_internal(Inkscape::NodePath::Path *np)
537     g_assert(np);
539     Inkscape::XML::Node *repr = np->object->repr;
541     sp_curve_unref(np->curve);
542     np->curve = create_curve(np);
543     
544     gchar *typestr = create_typestr(np);
545     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
547     // determine if path has an effect applied and write to correct "d" attribute.
548     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
549         np->local_change++;
550         repr->setAttribute(np->repr_key, svgpath);
551     }
553     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
554         np->local_change++;
555         repr->setAttribute(np->repr_nodetypes_key, typestr);
556     }
558     g_free(svgpath);
559     g_free(typestr);
561     if (np->show_helperpath) {
562         SPCurve * helper_curve = sp_curve_copy(np->curve);
563         sp_curve_transform(helper_curve, np->i2d );
564         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
565         sp_curve_unref(helper_curve);
566     }
567  }
569 /**
570  * Update XML path node with data from path object, commit changes forever.
571  */
572 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
574     //fixme: np can be NULL, so check before proceeding
575     g_return_if_fail(np != NULL);
577     if (np->livarot_path) {
578         delete np->livarot_path;
579         np->livarot_path = NULL;
580     }
582     update_repr_internal(np);
583     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
584     
585     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
586                      annotation);
589 /**
590  * Update XML path node with data from path object, commit changes with undo.
591  */
592 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
594     if (np->livarot_path) {
595         delete np->livarot_path;
596         np->livarot_path = NULL;
597     }
599     update_repr_internal(np);
600     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
601                            annotation);
604 /**
605  * Make duplicate of path, replace corresponding XML node in tree, commit.
606  */
607 static void stamp_repr(Inkscape::NodePath::Path *np)
609     g_assert(np);
611     Inkscape::XML::Node *old_repr = np->object->repr;
612     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
614     // remember the position of the item
615     gint pos = old_repr->position();
616     // remember parent
617     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
619     SPCurve *curve = create_curve(np);
620     gchar *typestr = create_typestr(np);
622     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
624     new_repr->setAttribute(np->repr_key, svgpath);
625     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
627     // add the new repr to the parent
628     parent->appendChild(new_repr);
629     // move to the saved position
630     new_repr->setPosition(pos > 0 ? pos : 0);
632     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
633                      _("Stamp"));
635     Inkscape::GC::release(new_repr);
636     g_free(svgpath);
637     g_free(typestr);
638     sp_curve_unref(curve);
641 /**
642  * Create curve from path.
643  */
644 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
646     SPCurve *curve = sp_curve_new();
648     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
649        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
650         sp_curve_moveto(curve,
651                         sp->first->pos * np->d2i);
652        Inkscape::NodePath::Node *n = sp->first->n.other;
653         while (n) {
654             NR::Point const end_pt = n->pos * np->d2i;
655             switch (n->code) {
656                 case NR_LINETO:
657                     sp_curve_lineto(curve, end_pt);
658                     break;
659                 case NR_CURVETO:
660                     sp_curve_curveto(curve,
661                                      n->p.other->n.pos * np->d2i,
662                                      n->p.pos * np->d2i,
663                                      end_pt);
664                     break;
665                 default:
666                     g_assert_not_reached();
667                     break;
668             }
669             if (n != sp->last) {
670                 n = n->n.other;
671             } else {
672                 n = NULL;
673             }
674         }
675         if (sp->closed) {
676             sp_curve_closepath(curve);
677         }
678     }
680     return curve;
683 /**
684  * Convert path type string to sodipodi:nodetypes style.
685  */
686 static gchar *create_typestr(Inkscape::NodePath::Path *np)
688     gchar *typestr = g_new(gchar, 32);
689     gint len = 32;
690     gint pos = 0;
692     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
693        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
695         if (pos >= len) {
696             typestr = g_renew(gchar, typestr, len + 32);
697             len += 32;
698         }
700         typestr[pos++] = 'c';
702        Inkscape::NodePath::Node *n;
703         n = sp->first->n.other;
704         while (n) {
705             gchar code;
707             switch (n->type) {
708                 case Inkscape::NodePath::NODE_CUSP:
709                     code = 'c';
710                     break;
711                 case Inkscape::NodePath::NODE_SMOOTH:
712                     code = 's';
713                     break;
714                 case Inkscape::NodePath::NODE_SYMM:
715                     code = 'z';
716                     break;
717                 default:
718                     g_assert_not_reached();
719                     code = '\0';
720                     break;
721             }
723             if (pos >= len) {
724                 typestr = g_renew(gchar, typestr, len + 32);
725                 len += 32;
726             }
728             typestr[pos++] = code;
730             if (n != sp->last) {
731                 n = n->n.other;
732             } else {
733                 n = NULL;
734             }
735         }
736     }
738     if (pos >= len) {
739         typestr = g_renew(gchar, typestr, len + 1);
740         len += 1;
741     }
743     typestr[pos++] = '\0';
745     return typestr;
748 /**
749  * Returns current path in context. // later eliminate this function at all!
750  */
751 static Inkscape::NodePath::Path *sp_nodepath_current()
753     if (!SP_ACTIVE_DESKTOP) {
754         return NULL;
755     }
757     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
759     if (!SP_IS_NODE_CONTEXT(event_context)) {
760         return NULL;
761     }
763     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
768 /**
769  \brief Fills node and handle positions for three nodes, splitting line
770   marked by end at distance t.
771  */
772 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
774     g_assert(new_path != NULL);
775     g_assert(end      != NULL);
777     g_assert(end->p.other == new_path);
778    Inkscape::NodePath::Node *start = new_path->p.other;
779     g_assert(start);
781     if (end->code == NR_LINETO) {
782         new_path->type =Inkscape::NodePath::NODE_CUSP;
783         new_path->code = NR_LINETO;
784         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
785     } else {
786         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
787         new_path->code = NR_CURVETO;
788         gdouble s      = 1 - t;
789         for (int dim = 0; dim < 2; dim++) {
790             NR::Coord const f000 = start->pos[dim];
791             NR::Coord const f001 = start->n.pos[dim];
792             NR::Coord const f011 = end->p.pos[dim];
793             NR::Coord const f111 = end->pos[dim];
794             NR::Coord const f00t = s * f000 + t * f001;
795             NR::Coord const f01t = s * f001 + t * f011;
796             NR::Coord const f11t = s * f011 + t * f111;
797             NR::Coord const f0tt = s * f00t + t * f01t;
798             NR::Coord const f1tt = s * f01t + t * f11t;
799             NR::Coord const fttt = s * f0tt + t * f1tt;
800             start->n.pos[dim]    = f00t;
801             new_path->p.pos[dim] = f0tt;
802             new_path->pos[dim]   = fttt;
803             new_path->n.pos[dim] = f1tt;
804             end->p.pos[dim]      = f11t;
805         }
806     }
809 /**
810  * Adds new node on direct line between two nodes, activates handles of all
811  * three nodes.
812  */
813 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
815     g_assert(end);
816     g_assert(end->subpath);
817     g_assert(g_list_find(end->subpath->nodes, end));
819    Inkscape::NodePath::Node *start = end->p.other;
820     g_assert( start->n.other == end );
821    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
822                                                end,
823                                                (NRPathcode)end->code == NR_LINETO? 
824                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
825                                                (NRPathcode)end->code,
826                                                &start->pos, &start->pos, &start->n.pos);
827     sp_nodepath_line_midpoint(newnode, end, t);
829     sp_node_adjust_handles(start);
830     sp_node_update_handles(start);
831     sp_node_update_handles(newnode);
832     sp_node_adjust_handles(end);
833     sp_node_update_handles(end);
835     return newnode;
838 /**
839 \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
840 */
841 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
843     g_assert(node);
844     g_assert(node->subpath);
845     g_assert(g_list_find(node->subpath->nodes, node));
847    Inkscape::NodePath::SubPath *sp = node->subpath;
848     Inkscape::NodePath::Path *np    = sp->nodepath;
850     if (sp->closed) {
851         sp_nodepath_subpath_open(sp, node);
852         return sp->first;
853     } else {
854         // no break for end nodes
855         if (node == sp->first) return NULL;
856         if (node == sp->last ) return NULL;
858         // create a new subpath
859        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
861         // duplicate the break node as start of the new subpath
862        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
864         while (node->n.other) { // copy the remaining nodes into the new subpath
865            Inkscape::NodePath::Node *n  = node->n.other;
866            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);
867             if (n->selected) {
868                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
869             }
870             sp_nodepath_node_destroy(n); // remove the point on the original subpath
871         }
873         return newnode;
874     }
877 /**
878  * Duplicate node and connect to neighbours.
879  */
880 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
882     g_assert(node);
883     g_assert(node->subpath);
884     g_assert(g_list_find(node->subpath->nodes, node));
886    Inkscape::NodePath::SubPath *sp = node->subpath;
888     NRPathcode code = (NRPathcode) node->code;
889     if (code == NR_MOVETO) { // if node is the endnode,
890         node->code = NR_LINETO; // new one is inserted before it, so change that to line
891     }
893     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
895     if (!node->n.other || !node->p.other) // if node is an endnode, select it
896         return node;
897     else
898         return newnode; // otherwise select the newly created node
901 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
903     node->p.pos = (node->pos + (node->pos - node->n.pos));
906 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
908     node->n.pos = (node->pos + (node->pos - node->p.pos));
911 /**
912  * Change line type at node, with side effects on neighbours.
913  */
914 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
916     g_assert(end);
917     g_assert(end->subpath);
918     g_assert(end->p.other);
920     if (end->code == static_cast< guint > ( code ) )
921         return;
923    Inkscape::NodePath::Node *start = end->p.other;
925     end->code = code;
927     if (code == NR_LINETO) {
928         if (start->code == NR_LINETO) {
929             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
930         }
931         if (end->n.other) {
932             if (end->n.other->code == NR_LINETO) {
933                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
934             }
935         }
936     } else {
937         NR::Point delta = end->pos - start->pos;
938         start->n.pos = start->pos + delta / 3;
939         end->p.pos = end->pos - delta / 3;
940         sp_node_adjust_handle(start, 1);
941         sp_node_adjust_handle(end, -1);
942     }
944     sp_node_update_handles(start);
945     sp_node_update_handles(end);
948 /**
949  * Change node type, and its handles accordingly.
950  */
951 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
953     g_assert(node);
954     g_assert(node->subpath);
956     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
957         return node;
959     if ((node->p.other != NULL) && (node->n.other != NULL)) {
960         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
961             type =Inkscape::NodePath::NODE_CUSP;
962         }
963     }
965     node->type = type;
967     if (node->type == Inkscape::NodePath::NODE_CUSP) {
968         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
969         node->knot->setSize (node->selected? 11 : 9);
970         sp_knot_update_ctrl(node->knot);
971     } else {
972         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
973         node->knot->setSize (node->selected? 9 : 7);
974         sp_knot_update_ctrl(node->knot);
975     }
977     // if one of handles is mouseovered, preserve its position
978     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
979         sp_node_adjust_handle(node, 1);
980     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
981         sp_node_adjust_handle(node, -1);
982     } else {
983         sp_node_adjust_handles(node);
984     }
986     sp_node_update_handles(node);
988     sp_nodepath_update_statusbar(node->subpath->nodepath);
990     return node;
993 /**
994  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
995  * adjacent segments from lines to curves.
996 */
997 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
999     bool p_line = (node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos);
1000     bool n_line = (node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos);
1002     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1003         if (p_line && n_line) {
1004             // only if both adjacent segments are lines, 
1005             // convert both to curves:
1007             // BEFORE:
1008             {
1009             node->code = NR_CURVETO;
1010             NR::Point delta = node->n.other->pos - node->p.other->pos;
1011             node->p.pos = node->pos - delta / 4;
1012             }
1014             // AFTER:
1015             {
1016             node->n.other->code = NR_CURVETO;
1017             NR::Point delta = node->p.other->pos - node->n.other->pos;
1018             node->n.pos = node->pos - delta / 4;
1019             }
1021             sp_node_update_handles(node);
1022         }
1023     }
1025     sp_nodepath_set_node_type (node, type);
1028 /**
1029  * Move node to point, and adjust its and neighbouring handles.
1030  */
1031 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1033     NR::Point delta = p - node->pos;
1034     node->pos = p;
1036     node->p.pos += delta;
1037     node->n.pos += delta;
1039     Inkscape::NodePath::Node *node_p = NULL;
1040     Inkscape::NodePath::Node *node_n = NULL;
1042     if (node->p.other) {
1043         if (node->code == NR_LINETO) {
1044             sp_node_adjust_handle(node, 1);
1045             sp_node_adjust_handle(node->p.other, -1);
1046             node_p = node->p.other;
1047         }
1048     }
1049     if (node->n.other) {
1050         if (node->n.other->code == NR_LINETO) {
1051             sp_node_adjust_handle(node, -1);
1052             sp_node_adjust_handle(node->n.other, 1);
1053             node_n = node->n.other;
1054         }
1055     }
1057     // this function is only called from batch movers that will update display at the end
1058     // themselves, so here we just move all the knots without emitting move signals, for speed
1059     sp_node_update_handles(node, false);
1060     if (node_n) {
1061         sp_node_update_handles(node_n, false);
1062     }
1063     if (node_p) {
1064         sp_node_update_handles(node_p, false);
1065     }
1068 /**
1069  * Call sp_node_moveto() for node selection and handle possible snapping.
1070  */
1071 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1072                                             bool const snap = true)
1074     NR::Coord best = NR_HUGE;
1075     NR::Point delta(dx, dy);
1076     NR::Point best_pt = delta;
1078     if (snap) {
1079         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
1080         
1081         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1082             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1083             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, SP_PATH(n->subpath->nodepath->item));
1084             if (s.getDistance() < best) {
1085                 best = s.getDistance();
1086                 best_pt = s.getPoint() - n->pos;
1087             }
1088         }
1089     }
1091     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1092         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1093         sp_node_moveto(n, n->pos + best_pt);
1094     }
1096     // do not update repr here so that node dragging is acceptably fast
1097     update_object(nodepath);
1100 /**
1101 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1102 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1103 near x = 0.
1104  */
1105 double
1106 sculpt_profile (double x, double alpha, guint profile)
1108     if (x >= 1)
1109         return 0;
1110     if (x <= 0)
1111         return 1;
1113     switch (profile) {
1114         case SCULPT_PROFILE_LINEAR:
1115         return 1 - x;
1116         case SCULPT_PROFILE_BELL:
1117         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1118         case SCULPT_PROFILE_ELLIPTIC:
1119         return sqrt(1 - x*x);
1120     }
1122     return 1;
1125 double
1126 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1128     // extremely primitive for now, don't have time to look for the real one
1129     double lower = NR::L2(b - a);
1130     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1131     return (lower + upper)/2;
1134 void
1135 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1137     n->pos = n->origin + delta;
1138     n->n.pos = n->n.origin + delta_n;
1139     n->p.pos = n->p.origin + delta_p;
1140     sp_node_adjust_handles(n);
1141     sp_node_update_handles(n, false);
1144 /**
1145  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1146  * on how far they are from the dragged node n.
1147  */
1148 static void 
1149 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1151     g_assert (n);
1152     g_assert (nodepath);
1153     g_assert (n->subpath->nodepath == nodepath);
1155     double pressure = n->knot->pressure;
1156     if (pressure == 0)
1157         pressure = 0.5; // default
1158     pressure = CLAMP (pressure, 0.2, 0.8);
1160     // map pressure to alpha = 1/5 ... 5
1161     double alpha = 1 - 2 * fabs(pressure - 0.5);
1162     if (pressure > 0.5)
1163         alpha = 1/alpha;
1165     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1167     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1168         // Only one subpath has selected nodes:
1169         // use linear mode, where the distance from n to node being dragged is calculated along the path
1171         double n_sel_range = 0, p_sel_range = 0;
1172         guint n_nodes = 0, p_nodes = 0;
1173         guint n_sel_nodes = 0, p_sel_nodes = 0;
1175         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1176         {
1177             double n_range = 0, p_range = 0;
1178             bool n_going = true, p_going = true;
1179             Inkscape::NodePath::Node *n_node = n;
1180             Inkscape::NodePath::Node *p_node = n;
1181             do {
1182                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1183                 if (n_node && n_going)
1184                     n_node = n_node->n.other;
1185                 if (n_node == NULL) {
1186                     n_going = false;
1187                 } else {
1188                     n_nodes ++;
1189                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1190                     if (n_node->selected) {
1191                         n_sel_nodes ++;
1192                         n_sel_range = n_range;
1193                     }
1194                     if (n_node == p_node) {
1195                         n_going = false;
1196                         p_going = false;
1197                     }
1198                 }
1199                 if (p_node && p_going)
1200                     p_node = p_node->p.other;
1201                 if (p_node == NULL) {
1202                     p_going = false;
1203                 } else {
1204                     p_nodes ++;
1205                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1206                     if (p_node->selected) {
1207                         p_sel_nodes ++;
1208                         p_sel_range = p_range;
1209                     }
1210                     if (p_node == n_node) {
1211                         n_going = false;
1212                         p_going = false;
1213                     }
1214                 }
1215             } while (n_going || p_going);
1216         }
1218         // Second pass: actually move nodes in this subpath
1219         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1220         {
1221             double n_range = 0, p_range = 0;
1222             bool n_going = true, p_going = true;
1223             Inkscape::NodePath::Node *n_node = n;
1224             Inkscape::NodePath::Node *p_node = n;
1225             do {
1226                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1227                 if (n_node && n_going)
1228                     n_node = n_node->n.other;
1229                 if (n_node == NULL) {
1230                     n_going = false;
1231                 } else {
1232                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1233                     if (n_node->selected) {
1234                         sp_nodepath_move_node_and_handles (n_node, 
1235                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1236                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1237                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1238                     }
1239                     if (n_node == p_node) {
1240                         n_going = false;
1241                         p_going = false;
1242                     }
1243                 }
1244                 if (p_node && p_going)
1245                     p_node = p_node->p.other;
1246                 if (p_node == NULL) {
1247                     p_going = false;
1248                 } else {
1249                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1250                     if (p_node->selected) {
1251                         sp_nodepath_move_node_and_handles (p_node, 
1252                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1253                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1254                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1255                     }
1256                     if (p_node == n_node) {
1257                         n_going = false;
1258                         p_going = false;
1259                     }
1260                 }
1261             } while (n_going || p_going);
1262         }
1264     } else {
1265         // Multiple subpaths have selected nodes:
1266         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1267         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1268         // fix the pear-like shape when sculpting e.g. a ring
1270         // First pass: calculate range
1271         gdouble direct_range = 0;
1272         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1273             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1274             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1275                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1276                 if (node->selected) {
1277                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1278                 }
1279             }
1280         }
1282         // Second pass: actually move nodes
1283         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1284             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1285             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1286                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1287                 if (node->selected) {
1288                     if (direct_range > 1e-6) {
1289                         sp_nodepath_move_node_and_handles (node,
1290                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1291                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1292                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1293                     } else {
1294                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1295                     }
1297                 }
1298             }
1299         }
1300     }
1302     // do not update repr here so that node dragging is acceptably fast
1303     update_object(nodepath);
1307 /**
1308  * Move node selection to point, adjust its and neighbouring handles,
1309  * handle possible snapping, and commit the change with possible undo.
1310  */
1311 void
1312 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1314     if (!nodepath) return;
1316     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1318     if (dx == 0) {
1319         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1320     } else if (dy == 0) {
1321         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1322     } else {
1323         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1324     }
1327 /**
1328  * Move node selection off screen and commit the change.
1329  */
1330 void
1331 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1333     // borrowed from sp_selection_move_screen in selection-chemistry.c
1334     // we find out the current zoom factor and divide deltas by it
1335     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1337     gdouble zoom = desktop->current_zoom();
1338     gdouble zdx = dx / zoom;
1339     gdouble zdy = dy / zoom;
1341     if (!nodepath) return;
1343     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1345     if (dx == 0) {
1346         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1347     } else if (dy == 0) {
1348         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1349     } else {
1350         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1351     }
1354 /** If they don't yet exist, creates knot and line for the given side of the node */
1355 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1357     if (!side->knot) {
1358         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"));
1360         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1361         side->knot->setSize (7);
1362         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1363         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1364         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1365         sp_knot_update_ctrl(side->knot);
1367         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1368         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1369         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1370         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1371         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1372         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1373     }
1375     if (!side->line) {
1376         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1377                                         SP_TYPE_CTRLLINE, NULL);
1378     }
1381 /**
1382  * Ensure the given handle of the node is visible/invisible, update its screen position
1383  */
1384 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1386     g_assert(node != NULL);
1388    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1389     NRPathcode code = sp_node_path_code_from_side(node, side);
1391     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1393     if (show_handle) {
1394         if (!side->knot) { // No handle knot at all
1395             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1396             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1397             side->knot->pos = side->pos;
1398             if (side->knot->item) 
1399                 SP_CTRL(side->knot->item)->moveto(side->pos);
1400             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1401             sp_knot_show(side->knot);
1402         } else {
1403             if (side->knot->pos != side->pos) { // only if it's really moved
1404                 if (fire_move_signals) {
1405                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1406                 } else {
1407                     sp_knot_moveto(side->knot, &side->pos);
1408                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1409                 }
1410             }
1411             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1412                 sp_knot_show(side->knot);
1413             }
1414         }
1415         sp_canvas_item_show(side->line);
1416     } else {
1417         if (side->knot) {
1418             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1419                 sp_knot_hide(side->knot);
1420             }
1421         }
1422         if (side->line) {
1423             sp_canvas_item_hide(side->line);
1424         }
1425     }
1428 /**
1429  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1430  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1431  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1432  * updated; otherwise, just move the knots silently (used in batch moves).
1433  */
1434 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1436     g_assert(node != NULL);
1438     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1439         sp_knot_show(node->knot);
1440     }
1442     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1443         if (fire_move_signals)
1444             sp_knot_set_position(node->knot, &node->pos, 0);
1445         else 
1446             sp_knot_moveto(node->knot, &node->pos);
1447     }
1449     gboolean show_handles = node->selected;
1450     if (node->p.other != NULL) {
1451         if (node->p.other->selected) show_handles = TRUE;
1452     }
1453     if (node->n.other != NULL) {
1454         if (node->n.other->selected) show_handles = TRUE;
1455     }
1457     if (node->subpath->nodepath->show_handles == false)
1458         show_handles = FALSE;
1460     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1461     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1464 /**
1465  * Call sp_node_update_handles() for all nodes on subpath.
1466  */
1467 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1469     g_assert(subpath != NULL);
1471     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1472         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1473     }
1476 /**
1477  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1478  */
1479 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1481     g_assert(nodepath != NULL);
1483     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1484         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1485     }
1488 void
1489 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1491     if (nodepath == NULL) return;
1493     nodepath->show_handles = show;
1494     sp_nodepath_update_handles(nodepath);
1497 /**
1498  * Adds all selected nodes in nodepath to list.
1499  */
1500 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1502     StlConv<Node *>::list(l, selected);
1503 /// \todo this adds a copying, rework when the selection becomes a stl list
1506 /**
1507  * Align selected nodes on the specified axis.
1508  */
1509 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1511     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1512         return;
1513     }
1515     if ( !nodepath->selected->next ) { // only one node selected
1516         return;
1517     }
1518    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1519     NR::Point dest(pNode->pos);
1520     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1521         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1522         if (pNode) {
1523             dest[axis] = pNode->pos[axis];
1524             sp_node_moveto(pNode, dest);
1525         }
1526     }
1528     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1531 /// Helper struct.
1532 struct NodeSort
1534    Inkscape::NodePath::Node *_node;
1535     NR::Coord _coord;
1536     /// \todo use vectorof pointers instead of calling copy ctor
1537     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1538         _node(node), _coord(node->pos[axis])
1539     {}
1541 };
1543 static bool operator<(NodeSort const &a, NodeSort const &b)
1545     return (a._coord < b._coord);
1548 /**
1549  * Distribute selected nodes on the specified axis.
1550  */
1551 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1553     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1554         return;
1555     }
1557     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1558         return;
1559     }
1561    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1562     std::vector<NodeSort> sorted;
1563     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1564         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1565         if (pNode) {
1566             NodeSort n(pNode, axis);
1567             sorted.push_back(n);
1568             //dest[axis] = pNode->pos[axis];
1569             //sp_node_moveto(pNode, dest);
1570         }
1571     }
1572     std::sort(sorted.begin(), sorted.end());
1573     unsigned int len = sorted.size();
1574     //overall bboxes span
1575     float dist = (sorted.back()._coord -
1576                   sorted.front()._coord);
1577     //new distance between each bbox
1578     float step = (dist) / (len - 1);
1579     float pos = sorted.front()._coord;
1580     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1581           it < sorted.end();
1582           it ++ )
1583     {
1584         NR::Point dest((*it)._node->pos);
1585         dest[axis] = pos;
1586         sp_node_moveto((*it)._node, dest);
1587         pos += step;
1588     }
1590     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1594 /**
1595  * Call sp_nodepath_line_add_node() for all selected segments.
1596  */
1597 void
1598 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1600     if (!nodepath) {
1601         return;
1602     }
1604     GList *nl = NULL;
1606     int n_added = 0;
1608     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1609        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1610         g_assert(t->selected);
1611         if (t->p.other && t->p.other->selected) {
1612             nl = g_list_prepend(nl, t);
1613         }
1614     }
1616     while (nl) {
1617        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1618        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1619        sp_nodepath_node_select(n, TRUE, FALSE);
1620        n_added ++;
1621        nl = g_list_remove(nl, t);
1622     }
1624     /** \todo fixme: adjust ? */
1625     sp_nodepath_update_handles(nodepath);
1627     if (n_added > 1) {
1628         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1629     } else if (n_added > 0) {
1630         sp_nodepath_update_repr(nodepath, _("Add node"));
1631     }
1633     sp_nodepath_update_statusbar(nodepath);
1636 /**
1637  * Select segment nearest to point
1638  */
1639 void
1640 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1642     if (!nodepath) {
1643         return;
1644     }
1646     sp_nodepath_ensure_livarot_path(nodepath);
1647     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1648     if (!maybe_position) {
1649         return;
1650     }
1651     Path::cut_position position = *maybe_position;
1653     //find segment to segment
1654     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1656     //fixme: this can return NULL, so check before proceeding.
1657     g_return_if_fail(e != NULL);
1658     
1659     gboolean force = FALSE;
1660     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1661         force = TRUE;
1662     }
1663     sp_nodepath_node_select(e, (gboolean) toggle, force);
1664     if (e->p.other)
1665         sp_nodepath_node_select(e->p.other, TRUE, force);
1667     sp_nodepath_update_handles(nodepath);
1669     sp_nodepath_update_statusbar(nodepath);
1672 /**
1673  * Add a node nearest to point
1674  */
1675 void
1676 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1678     if (!nodepath) {
1679         return;
1680     }
1682     sp_nodepath_ensure_livarot_path(nodepath);
1683     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1684     if (!maybe_position) {
1685         return;
1686     }
1687     Path::cut_position position = *maybe_position;
1689     //find segment to split
1690     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1692     //don't know why but t seems to flip for lines
1693     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1694         position.t = 1.0 - position.t;
1695     }
1696     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1697     sp_nodepath_node_select(n, FALSE, TRUE);
1699     /* fixme: adjust ? */
1700     sp_nodepath_update_handles(nodepath);
1702     sp_nodepath_update_repr(nodepath, _("Add node"));
1704     sp_nodepath_update_statusbar(nodepath);
1707 /*
1708  * Adjusts a segment so that t moves by a certain delta for dragging
1709  * converts lines to curves
1710  *
1711  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1712  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1713  */
1714 void
1715 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1717     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1719     //fixme: e and e->p can be NULL, so check for those before proceeding
1720     g_return_if_fail(e != NULL);
1721     g_return_if_fail(&e->p != NULL);
1722     
1723     /* feel good is an arbitrary parameter that distributes the delta between handles
1724      * if t of the drag point is less than 1/6 distance form the endpoint only
1725      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1726      */
1727     double feel_good;
1728     if (t <= 1.0 / 6.0)
1729         feel_good = 0;
1730     else if (t <= 0.5)
1731         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1732     else if (t <= 5.0 / 6.0)
1733         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1734     else
1735         feel_good = 1;
1737     //if we're dragging a line convert it to a curve
1738     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1739         sp_nodepath_set_line_type(e, NR_CURVETO);
1740     }
1742     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1743     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1744     e->p.other->n.pos += offsetcoord0;
1745     e->p.pos += offsetcoord1;
1747     // adjust handles of adjacent nodes where necessary
1748     sp_node_adjust_handle(e,1);
1749     sp_node_adjust_handle(e->p.other,-1);
1751     sp_nodepath_update_handles(e->subpath->nodepath);
1753     update_object(e->subpath->nodepath);
1755     sp_nodepath_update_statusbar(e->subpath->nodepath);
1759 /**
1760  * Call sp_nodepath_break() for all selected segments.
1761  */
1762 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1764     if (!nodepath) return;
1766     GList *temp = NULL;
1767     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1768        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1769        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1770         if (nn == NULL) continue; // no break, no new node
1771         temp = g_list_prepend(temp, nn);
1772     }
1774     if (temp) {
1775         sp_nodepath_deselect(nodepath);
1776     }
1777     for (GList *l = temp; l != NULL; l = l->next) {
1778         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1779     }
1781     sp_nodepath_update_handles(nodepath);
1783     sp_nodepath_update_repr(nodepath, _("Break path"));
1786 /**
1787  * Duplicate the selected node(s).
1788  */
1789 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1791     if (!nodepath) {
1792         return;
1793     }
1795     GList *temp = NULL;
1796     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1797        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1798        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1799         if (nn == NULL) continue; // could not duplicate
1800         temp = g_list_prepend(temp, nn);
1801     }
1803     if (temp) {
1804         sp_nodepath_deselect(nodepath);
1805     }
1806     for (GList *l = temp; l != NULL; l = l->next) {
1807         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1808     }
1810     sp_nodepath_update_handles(nodepath);
1812     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1815 /**
1816  *  Join two nodes by merging them into one.
1817  */
1818 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
1820     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1822     if (g_list_length(nodepath->selected) != 2) {
1823         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1824         return;
1825     }
1827    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1828    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1830     g_assert(a != b);
1831     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1832         // someone tried to join an orphan node (i.e. a single-node subpath).
1833         // this is not worth an error message, just fail silently.
1834         return;
1835     }
1837     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1838         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1839         return;
1840     }
1842     /* a and b are endpoints */
1844     NR::Point c;
1845     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1846         c = a->pos;
1847     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1848         c = b->pos;
1849     } else {
1850         c = (a->pos + b->pos) / 2;
1851     }
1853     if (a->subpath == b->subpath) {
1854        Inkscape::NodePath::SubPath *sp = a->subpath;
1855         sp_nodepath_subpath_close(sp);
1856         sp_node_moveto (sp->first, c);
1858         sp_nodepath_update_handles(sp->nodepath);
1859         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1860         return;
1861     }
1863     /* a and b are separate subpaths */
1864    Inkscape::NodePath::SubPath *sa = a->subpath;
1865    Inkscape::NodePath::SubPath *sb = b->subpath;
1866     NR::Point p;
1867    Inkscape::NodePath::Node *n;
1868     NRPathcode code;
1869     if (a == sa->first) {
1870         p = sa->first->n.pos;
1871         code = (NRPathcode)sa->first->n.other->code;
1872        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1873         n = sa->last;
1874         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1875         n = n->p.other;
1876         while (n) {
1877             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1878             n = n->p.other;
1879             if (n == sa->first) n = NULL;
1880         }
1881         sp_nodepath_subpath_destroy(sa);
1882         sa = t;
1883     } else if (a == sa->last) {
1884         p = sa->last->p.pos;
1885         code = (NRPathcode)sa->last->code;
1886         sp_nodepath_node_destroy(sa->last);
1887     } else {
1888         code = NR_END;
1889         g_assert_not_reached();
1890     }
1892     if (b == sb->first) {
1893         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1894         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1895             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1896         }
1897     } else if (b == sb->last) {
1898         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1899         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1900             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1901         }
1902     } else {
1903         g_assert_not_reached();
1904     }
1905     /* and now destroy sb */
1907     sp_nodepath_subpath_destroy(sb);
1909     sp_nodepath_update_handles(sa->nodepath);
1911     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1913     sp_nodepath_update_statusbar(nodepath);
1916 /**
1917  *  Join two nodes by adding a segment between them.
1918  */
1919 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
1921     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1923     if (g_list_length(nodepath->selected) != 2) {
1924         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1925         return;
1926     }
1928    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1929    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1931     g_assert(a != b);
1932     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1933         // someone tried to join an orphan node (i.e. a single-node subpath).
1934         // this is not worth an error message, just fail silently.
1935         return;
1936     }
1938     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1939         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1940         return;
1941     }
1943     if (a->subpath == b->subpath) {
1944        Inkscape::NodePath::SubPath *sp = a->subpath;
1946         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1947         sp->closed = TRUE;
1949         sp->first->p.other = sp->last;
1950         sp->last->n.other  = sp->first;
1952         sp_node_handle_mirror_p_to_n(sp->last);
1953         sp_node_handle_mirror_n_to_p(sp->first);
1955         sp->first->code = sp->last->code;
1956         sp->first       = sp->last;
1958         sp_nodepath_update_handles(sp->nodepath);
1960         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1962         return;
1963     }
1965     /* a and b are separate subpaths */
1966    Inkscape::NodePath::SubPath *sa = a->subpath;
1967    Inkscape::NodePath::SubPath *sb = b->subpath;
1969    Inkscape::NodePath::Node *n;
1970     NR::Point p;
1971     NRPathcode code;
1972     if (a == sa->first) {
1973         code = (NRPathcode) sa->first->n.other->code;
1974        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1975         n = sa->last;
1976         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1977         for (n = n->p.other; n != NULL; n = n->p.other) {
1978             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1979         }
1980         sp_nodepath_subpath_destroy(sa);
1981         sa = t;
1982     } else if (a == sa->last) {
1983         code = (NRPathcode)sa->last->code;
1984     } else {
1985         code = NR_END;
1986         g_assert_not_reached();
1987     }
1989     if (b == sb->first) {
1990         n = sb->first;
1991         sp_node_handle_mirror_p_to_n(sa->last);
1992         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1993         sp_node_handle_mirror_n_to_p(sa->last);
1994         for (n = n->n.other; n != NULL; n = n->n.other) {
1995             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1996         }
1997     } else if (b == sb->last) {
1998         n = sb->last;
1999         sp_node_handle_mirror_p_to_n(sa->last);
2000         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2001         sp_node_handle_mirror_n_to_p(sa->last);
2002         for (n = n->p.other; n != NULL; n = n->p.other) {
2003             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2004         }
2005     } else {
2006         g_assert_not_reached();
2007     }
2008     /* and now destroy sb */
2010     sp_nodepath_subpath_destroy(sb);
2012     sp_nodepath_update_handles(sa->nodepath);
2014     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2017 /**
2018  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2019  */
2020 void sp_node_delete_preserve(GList *nodes_to_delete)
2022     GSList *nodepaths = NULL;
2023     
2024     while (nodes_to_delete) {
2025         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2026         Inkscape::NodePath::SubPath *sp = node->subpath;
2027         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2028         Inkscape::NodePath::Node *sample_cursor = NULL;
2029         Inkscape::NodePath::Node *sample_end = NULL;
2030         Inkscape::NodePath::Node *delete_cursor = node;
2031         bool just_delete = false;
2032         
2033         //find the start of this contiguous selection
2034         //move left to the first node that is not selected
2035         //or the start of the non-closed path
2036         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2037             delete_cursor = curr;
2038         }
2040         //just delete at the beginning of an open path
2041         if (!delete_cursor->p.other) {
2042             sample_cursor = delete_cursor;
2043             just_delete = true;
2044         } else {
2045             sample_cursor = delete_cursor->p.other;
2046         }
2047         
2048         //calculate points for each segment
2049         int rate = 5;
2050         float period = 1.0 / rate;
2051         std::vector<NR::Point> data;
2052         if (!just_delete) {
2053             data.push_back(sample_cursor->pos);
2054             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2055                 //just delete at the end of an open path
2056                 if (!sp->closed && curr == sp->last) {
2057                     just_delete = true;
2058                     break;
2059                 }
2060                 
2061                 //sample points on the contiguous selected segment
2062                 NR::Point *bez;
2063                 bez = new NR::Point [4];
2064                 bez[0] = curr->pos;
2065                 bez[1] = curr->n.pos;
2066                 bez[2] = curr->n.other->p.pos;
2067                 bez[3] = curr->n.other->pos;
2068                 for (int i=1; i<rate; i++) {
2069                     gdouble t = i * period;
2070                     NR::Point p = bezier_pt(3, bez, t);
2071                     data.push_back(p);
2072                 }
2073                 data.push_back(curr->n.other->pos);
2075                 sample_end = curr->n.other;
2076                 //break if we've come full circle or hit the end of the selection
2077                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2078                     break;
2079                 }
2080             }
2081         }
2083         if (!just_delete) {
2084             //calculate the best fitting single segment and adjust the endpoints
2085             NR::Point *adata;
2086             adata = new NR::Point [data.size()];
2087             copy(data.begin(), data.end(), adata);
2088             
2089             NR::Point *bez;
2090             bez = new NR::Point [4];
2091             //would decreasing error create a better fitting approximation?
2092             gdouble error = 1.0;
2093             gint ret;
2094             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2096             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2097             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2098             //the resulting nodes behave as expected.
2099             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2100             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2101             
2102             //adjust endpoints
2103             sample_cursor->n.pos = bez[1];
2104             sample_end->p.pos = bez[2];
2105         }
2106        
2107         //destroy this contiguous selection
2108         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2109             Inkscape::NodePath::Node *temp = delete_cursor;
2110             if (delete_cursor->n.other == delete_cursor) {
2111                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2112                 delete_cursor = NULL; 
2113             } else {
2114                 delete_cursor = delete_cursor->n.other;
2115             }
2116             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2117             sp_nodepath_node_destroy(temp);
2118         }
2120         sp_nodepath_update_handles(nodepath);
2122         if (!g_slist_find(nodepaths, nodepath))
2123             nodepaths = g_slist_prepend (nodepaths, nodepath);
2124     }
2126     for (GSList *i = nodepaths; i; i = i->next) {
2127         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2128         // different nodepaths will give us one undo event per nodepath
2129         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2131         // if the entire nodepath is removed, delete the selected object.
2132         if (nodepath->subpaths == NULL ||
2133             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2134             //at least 2
2135             sp_nodepath_get_node_count(nodepath) < 2) {
2136             SPDocument *document = sp_desktop_document (nodepath->desktop);
2137             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2138             //delete this nodepath's object, not the entire selection! (though at this time, this
2139             //does not matter)
2140             sp_selection_delete();
2141             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2142                               _("Delete nodes"));
2143         } else {
2144             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2145             sp_nodepath_update_statusbar(nodepath);
2146         }
2147     }
2149     g_slist_free (nodepaths);
2152 /**
2153  * Delete one or more selected nodes.
2154  */
2155 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2157     if (!nodepath) return;
2158     if (!nodepath->selected) return;
2160     /** \todo fixme: do it the right way */
2161     while (nodepath->selected) {
2162        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2163         sp_nodepath_node_destroy(node);
2164     }
2167     //clean up the nodepath (such as for trivial subpaths)
2168     sp_nodepath_cleanup(nodepath);
2170     sp_nodepath_update_handles(nodepath);
2172     // if the entire nodepath is removed, delete the selected object.
2173     if (nodepath->subpaths == NULL ||
2174         sp_nodepath_get_node_count(nodepath) < 2) {
2175         SPDocument *document = sp_desktop_document (nodepath->desktop);
2176         sp_selection_delete();
2177         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2178                           _("Delete nodes"));
2179         return;
2180     }
2182     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2184     sp_nodepath_update_statusbar(nodepath);
2187 /**
2188  * Delete one or more segments between two selected nodes.
2189  * This is the code for 'split'.
2190  */
2191 void
2192 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2194    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2195    Inkscape::NodePath::Node *curr, *next;     //Iterators
2197     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2199     if (g_list_length(nodepath->selected) != 2) {
2200         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2201                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2202         return;
2203     }
2205     //Selected nodes, not inclusive
2206    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2207    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2209     if ( ( a==b)                       ||  //same node
2210          (a->subpath  != b->subpath )  ||  //not the same path
2211          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2212          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2213     {
2214         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2215                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2216         return;
2217     }
2219     //###########################################
2220     //# BEGIN EDITS
2221     //###########################################
2222     //##################################
2223     //# CLOSED PATH
2224     //##################################
2225     if (a->subpath->closed) {
2228         gboolean reversed = FALSE;
2230         //Since we can go in a circle, we need to find the shorter distance.
2231         //  a->b or b->a
2232         start = end = NULL;
2233         int distance    = 0;
2234         int minDistance = 0;
2235         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2236             if (curr==b) {
2237                 //printf("a to b:%d\n", distance);
2238                 start = a;//go from a to b
2239                 end   = b;
2240                 minDistance = distance;
2241                 //printf("A to B :\n");
2242                 break;
2243             }
2244             distance++;
2245         }
2247         //try again, the other direction
2248         distance = 0;
2249         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2250             if (curr==a) {
2251                 //printf("b to a:%d\n", distance);
2252                 if (distance < minDistance) {
2253                     start    = b;  //we go from b to a
2254                     end      = a;
2255                     reversed = TRUE;
2256                     //printf("B to A\n");
2257                 }
2258                 break;
2259             }
2260             distance++;
2261         }
2264         //Copy everything from 'end' to 'start' to a new subpath
2265        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2266         for (curr=end ; curr ; curr=curr->n.other) {
2267             NRPathcode code = (NRPathcode) curr->code;
2268             if (curr == end)
2269                 code = NR_MOVETO;
2270             sp_nodepath_node_new(t, NULL,
2271                                  (Inkscape::NodePath::NodeType)curr->type, code,
2272                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2273             if (curr == start)
2274                 break;
2275         }
2276         sp_nodepath_subpath_destroy(a->subpath);
2279     }
2283     //##################################
2284     //# OPEN PATH
2285     //##################################
2286     else {
2288         //We need to get the direction of the list between A and B
2289         //Can we walk from a to b?
2290         start = end = NULL;
2291         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2292             if (curr==b) {
2293                 start = a;  //did it!  we go from a to b
2294                 end   = b;
2295                 //printf("A to B\n");
2296                 break;
2297             }
2298         }
2299         if (!start) {//didn't work?  let's try the other direction
2300             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2301                 if (curr==a) {
2302                     start = b;  //did it!  we go from b to a
2303                     end   = a;
2304                     //printf("B to A\n");
2305                     break;
2306                 }
2307             }
2308         }
2309         if (!start) {
2310             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2311                                                      _("Cannot find path between nodes."));
2312             return;
2313         }
2317         //Copy everything after 'end' to a new subpath
2318        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2319         for (curr=end ; curr ; curr=curr->n.other) {
2320             NRPathcode code = (NRPathcode) curr->code;
2321             if (curr == end)
2322                 code = NR_MOVETO;
2323             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2324                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2325         }
2327         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2328         for (curr = start->n.other ; curr  ; curr=next) {
2329             next = curr->n.other;
2330             sp_nodepath_node_destroy(curr);
2331         }
2333     }
2334     //###########################################
2335     //# END EDITS
2336     //###########################################
2338     //clean up the nodepath (such as for trivial subpaths)
2339     sp_nodepath_cleanup(nodepath);
2341     sp_nodepath_update_handles(nodepath);
2343     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2345     sp_nodepath_update_statusbar(nodepath);
2348 /**
2349  * Call sp_nodepath_set_line() for all selected segments.
2350  */
2351 void
2352 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2354     if (nodepath == NULL) return;
2356     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2357        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2358         g_assert(n->selected);
2359         if (n->p.other && n->p.other->selected) {
2360             sp_nodepath_set_line_type(n, code);
2361         }
2362     }
2364     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2367 /**
2368  * Call sp_nodepath_convert_node_type() for all selected nodes.
2369  */
2370 void
2371 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2373     if (nodepath == NULL) return;
2375     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2376         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2377     }
2379     sp_nodepath_update_repr(nodepath, _("Change node type"));
2382 /**
2383  * Change select status of node, update its own and neighbour handles.
2384  */
2385 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2387     node->selected = selected;
2389     if (selected) {
2390         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2391         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2392         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2393         sp_knot_update_ctrl(node->knot);
2394     } else {
2395         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2396         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2397         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2398         sp_knot_update_ctrl(node->knot);
2399     }
2401     sp_node_update_handles(node);
2402     if (node->n.other) sp_node_update_handles(node->n.other);
2403     if (node->p.other) sp_node_update_handles(node->p.other);
2406 /**
2407 \brief Select a node
2408 \param node     The node to select
2409 \param incremental   If true, add to selection, otherwise deselect others
2410 \param override   If true, always select this node, otherwise toggle selected status
2411 */
2412 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2414     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2416     if (incremental) {
2417         if (override) {
2418             if (!g_list_find(nodepath->selected, node)) {
2419                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2420             }
2421             sp_node_set_selected(node, TRUE);
2422         } else { // toggle
2423             if (node->selected) {
2424                 g_assert(g_list_find(nodepath->selected, node));
2425                 nodepath->selected = g_list_remove(nodepath->selected, node);
2426             } else {
2427                 g_assert(!g_list_find(nodepath->selected, node));
2428                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2429             }
2430             sp_node_set_selected(node, !node->selected);
2431         }
2432     } else {
2433         sp_nodepath_deselect(nodepath);
2434         nodepath->selected = g_list_prepend(nodepath->selected, node);
2435         sp_node_set_selected(node, TRUE);
2436     }
2438     sp_nodepath_update_statusbar(nodepath);
2442 /**
2443 \brief Deselect all nodes in the nodepath
2444 */
2445 void
2446 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2448     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2450     while (nodepath->selected) {
2451         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2452         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2453     }
2454     sp_nodepath_update_statusbar(nodepath);
2457 /**
2458 \brief Select or invert selection of all nodes in the nodepath
2459 */
2460 void
2461 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2463     if (!nodepath) return;
2465     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2466        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2467         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2468            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2469            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2470         }
2471     }
2474 /**
2475  * If nothing selected, does the same as sp_nodepath_select_all();
2476  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2477  * (i.e., similar to "select all in layer", with the "selected" subpaths
2478  * being treated as "layers" in the path).
2479  */
2480 void
2481 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2483     if (!nodepath) return;
2485     if (g_list_length (nodepath->selected) == 0) {
2486         sp_nodepath_select_all (nodepath, invert);
2487         return;
2488     }
2490     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2491     GSList *subpaths = NULL;
2493     for (GList *l = copy; l != NULL; l = l->next) {
2494         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2495         Inkscape::NodePath::SubPath *subpath = n->subpath;
2496         if (!g_slist_find (subpaths, subpath))
2497             subpaths = g_slist_prepend (subpaths, subpath);
2498     }
2500     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2501         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2502         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2503             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2504             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2505         }
2506     }
2508     g_slist_free (subpaths);
2509     g_list_free (copy);
2512 /**
2513  * \brief Select the node after the last selected; if none is selected,
2514  * select the first within path.
2515  */
2516 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2518     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2520    Inkscape::NodePath::Node *last = NULL;
2521     if (nodepath->selected) {
2522         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2523            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2524             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2525             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2526                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2527                 if (node->selected) {
2528                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2529                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2530                             if (spl->next) { // there's a next subpath
2531                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2532                                 last = subpath_next->first;
2533                             } else if (spl->prev) { // there's a previous subpath
2534                                 last = NULL; // to be set later to the first node of first subpath
2535                             } else {
2536                                 last = node->n.other;
2537                             }
2538                         } else {
2539                             last = node->n.other;
2540                         }
2541                     } else {
2542                         if (node->n.other) {
2543                             last = node->n.other;
2544                         } else {
2545                             if (spl->next) { // there's a next subpath
2546                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2547                                 last = subpath_next->first;
2548                             } else if (spl->prev) { // there's a previous subpath
2549                                 last = NULL; // to be set later to the first node of first subpath
2550                             } else {
2551                                 last = (Inkscape::NodePath::Node *) subpath->first;
2552                             }
2553                         }
2554                     }
2555                 }
2556             }
2557         }
2558         sp_nodepath_deselect(nodepath);
2559     }
2561     if (last) { // there's at least one more node after selected
2562         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2563     } else { // no more nodes, select the first one in first subpath
2564        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2565         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2566     }
2569 /**
2570  * \brief Select the node before the first selected; if none is selected,
2571  * select the last within path
2572  */
2573 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2575     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2577    Inkscape::NodePath::Node *last = NULL;
2578     if (nodepath->selected) {
2579         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2580            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2581             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2582                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2583                 if (node->selected) {
2584                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2585                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2586                             if (spl->prev) { // there's a prev subpath
2587                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2588                                 last = subpath_prev->last;
2589                             } else if (spl->next) { // there's a next subpath
2590                                 last = NULL; // to be set later to the last node of last subpath
2591                             } else {
2592                                 last = node->p.other;
2593                             }
2594                         } else {
2595                             last = node->p.other;
2596                         }
2597                     } else {
2598                         if (node->p.other) {
2599                             last = node->p.other;
2600                         } else {
2601                             if (spl->prev) { // there's a prev subpath
2602                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2603                                 last = subpath_prev->last;
2604                             } else if (spl->next) { // there's a next subpath
2605                                 last = NULL; // to be set later to the last node of last subpath
2606                             } else {
2607                                 last = (Inkscape::NodePath::Node *) subpath->last;
2608                             }
2609                         }
2610                     }
2611                 }
2612             }
2613         }
2614         sp_nodepath_deselect(nodepath);
2615     }
2617     if (last) { // there's at least one more node before selected
2618         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2619     } else { // no more nodes, select the last one in last subpath
2620         GList *spl = g_list_last(nodepath->subpaths);
2621        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2622         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2623     }
2626 /**
2627  * \brief Select all nodes that are within the rectangle.
2628  */
2629 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2631     if (!incremental) {
2632         sp_nodepath_deselect(nodepath);
2633     }
2635     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2636        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2637         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2638            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2640             if (b.contains(node->pos)) {
2641                 sp_nodepath_node_select(node, TRUE, TRUE);
2642             }
2643         }
2644     }
2648 void
2649 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2651     g_assert (n);
2652     g_assert (nodepath);
2653     g_assert (n->subpath->nodepath == nodepath);
2655     if (g_list_length (nodepath->selected) == 0) {
2656         if (grow > 0) {
2657             sp_nodepath_node_select(n, TRUE, TRUE);
2658         }
2659         return;
2660     }
2662     if (g_list_length (nodepath->selected) == 1) {
2663         if (grow < 0) {
2664             sp_nodepath_deselect (nodepath);
2665             return;
2666         }
2667     }
2669         double n_sel_range = 0, p_sel_range = 0;
2670             Inkscape::NodePath::Node *farthest_n_node = n;
2671             Inkscape::NodePath::Node *farthest_p_node = n;
2673         // Calculate ranges
2674         {
2675             double n_range = 0, p_range = 0;
2676             bool n_going = true, p_going = true;
2677             Inkscape::NodePath::Node *n_node = n;
2678             Inkscape::NodePath::Node *p_node = n;
2679             do {
2680                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2681                 if (n_node && n_going)
2682                     n_node = n_node->n.other;
2683                 if (n_node == NULL) {
2684                     n_going = false;
2685                 } else {
2686                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2687                     if (n_node->selected) {
2688                         n_sel_range = n_range;
2689                         farthest_n_node = n_node;
2690                     }
2691                     if (n_node == p_node) {
2692                         n_going = false;
2693                         p_going = false;
2694                     }
2695                 }
2696                 if (p_node && p_going)
2697                     p_node = p_node->p.other;
2698                 if (p_node == NULL) {
2699                     p_going = false;
2700                 } else {
2701                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2702                     if (p_node->selected) {
2703                         p_sel_range = p_range;
2704                         farthest_p_node = p_node;
2705                     }
2706                     if (p_node == n_node) {
2707                         n_going = false;
2708                         p_going = false;
2709                     }
2710                 }
2711             } while (n_going || p_going);
2712         }
2714     if (grow > 0) {
2715         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2716                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2717         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2718                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2719         }
2720     } else {
2721         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2722                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2723         } else if (farthest_p_node && farthest_p_node->selected) {
2724                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2725         }
2726     }
2729 void
2730 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2732     g_assert (n);
2733     g_assert (nodepath);
2734     g_assert (n->subpath->nodepath == nodepath);
2736     if (g_list_length (nodepath->selected) == 0) {
2737         if (grow > 0) {
2738             sp_nodepath_node_select(n, TRUE, TRUE);
2739         }
2740         return;
2741     }
2743     if (g_list_length (nodepath->selected) == 1) {
2744         if (grow < 0) {
2745             sp_nodepath_deselect (nodepath);
2746             return;
2747         }
2748     }
2750     Inkscape::NodePath::Node *farthest_selected = NULL;
2751     double farthest_dist = 0;
2753     Inkscape::NodePath::Node *closest_unselected = NULL;
2754     double closest_dist = NR_HUGE;
2756     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2757        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2758         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2759            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2760            if (node == n)
2761                continue;
2762            if (node->selected) {
2763                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2764                    farthest_dist = NR::L2(node->pos - n->pos);
2765                    farthest_selected = node;
2766                }
2767            } else {
2768                if (NR::L2(node->pos - n->pos) < closest_dist) {
2769                    closest_dist = NR::L2(node->pos - n->pos);
2770                    closest_unselected = node;
2771                }
2772            }
2773         }
2774     }
2776     if (grow > 0) {
2777         if (closest_unselected) {
2778             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2779         }
2780     } else {
2781         if (farthest_selected) {
2782             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2783         }
2784     }
2788 /**
2789 \brief  Saves all nodes' and handles' current positions in their origin members
2790 */
2791 void
2792 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2794     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2795        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2796         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2797            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2798            n->origin = n->pos;
2799            n->p.origin = n->p.pos;
2800            n->n.origin = n->n.pos;
2801         }
2802     }
2803
2805 /**
2806 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2807 */
2808 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2810     if (!nodepath->selected) {
2811         return NULL;
2812     }
2814     GList *r = NULL;
2815     guint i = 0;
2816     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2817        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2818         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2819            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2820             i++;
2821             if (node->selected) {
2822                 r = g_list_append(r, GINT_TO_POINTER(i));
2823             }
2824         }
2825     }
2826     return r;
2829 /**
2830 \brief  Restores selection by selecting nodes whose positions are in the list
2831 */
2832 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2834     sp_nodepath_deselect(nodepath);
2836     guint i = 0;
2837     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2838        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2839         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2840            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2841             i++;
2842             if (g_list_find(r, GINT_TO_POINTER(i))) {
2843                 sp_nodepath_node_select(node, TRUE, TRUE);
2844             }
2845         }
2846     }
2850 /**
2851 \brief Adjusts handle according to node type and line code.
2852 */
2853 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2855     double len, otherlen, linelen;
2857     g_assert(node);
2859    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2860    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2862     /** \todo fixme: */
2863     if (me->other == NULL) return;
2864     if (other->other == NULL) return;
2866     /* I have line */
2868     NRPathcode mecode, ocode;
2869     if (which_adjust == 1) {
2870         mecode = (NRPathcode)me->other->code;
2871         ocode = (NRPathcode)node->code;
2872     } else {
2873         mecode = (NRPathcode)node->code;
2874         ocode = (NRPathcode)other->other->code;
2875     }
2877     if (mecode == NR_LINETO) return;
2879     /* I am curve */
2881     if (other->other == NULL) return;
2883     /* Other has line */
2885     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2887     NR::Point delta;
2888     if (ocode == NR_LINETO) {
2889         /* other is lineto, we are either smooth or symm */
2890        Inkscape::NodePath::Node *othernode = other->other;
2891         len = NR::L2(me->pos - node->pos);
2892         delta = node->pos - othernode->pos;
2893         linelen = NR::L2(delta);
2894         if (linelen < 1e-18) 
2895             return;
2896         me->pos = node->pos + (len / linelen)*delta;
2897         return;
2898     }
2900     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2902         me->pos = 2 * node->pos - other->pos;
2903         return;
2904     }
2906     /* We are smooth */
2908     len = NR::L2(me->pos - node->pos);
2909     delta = other->pos - node->pos;
2910     otherlen = NR::L2(delta);
2911     if (otherlen < 1e-18) return;
2913     me->pos = node->pos - (len / otherlen) * delta;
2916 /**
2917  \brief Adjusts both handles according to node type and line code
2918  */
2919 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2921     g_assert(node);
2923     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2925     /* we are either smooth or symm */
2927     if (node->p.other == NULL) return;
2929     if (node->n.other == NULL) return;
2931     if (node->code == NR_LINETO) {
2932         if (node->n.other->code == NR_LINETO) return;
2933         sp_node_adjust_handle(node, 1);
2934         return;
2935     }
2937     if (node->n.other->code == NR_LINETO) {
2938         if (node->code == NR_LINETO) return;
2939         sp_node_adjust_handle(node, -1);
2940         return;
2941     }
2943     /* both are curves */
2944     NR::Point const delta( node->n.pos - node->p.pos );
2946     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2947         node->p.pos = node->pos - delta / 2;
2948         node->n.pos = node->pos + delta / 2;
2949         return;
2950     }
2952     /* We are smooth */
2953     double plen = NR::L2(node->p.pos - node->pos);
2954     if (plen < 1e-18) return;
2955     double nlen = NR::L2(node->n.pos - node->pos);
2956     if (nlen < 1e-18) return;
2957     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2958     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2961 /**
2962  * Node event callback.
2963  */
2964 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2966     gboolean ret = FALSE;
2967     switch (event->type) {
2968         case GDK_ENTER_NOTIFY:
2969             Inkscape::NodePath::Path::active_node = n;
2970             break;
2971         case GDK_LEAVE_NOTIFY:
2972             Inkscape::NodePath::Path::active_node = NULL;
2973             break;
2974         case GDK_SCROLL:
2975             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
2976                 switch (event->scroll.direction) {
2977                     case GDK_SCROLL_UP:
2978                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2979                         break;
2980                     case GDK_SCROLL_DOWN:
2981                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2982                         break;
2983                     default:
2984                         break;
2985                 }
2986                 ret = TRUE;
2987             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
2988                 switch (event->scroll.direction) {
2989                     case GDK_SCROLL_UP:
2990                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2991                         break;
2992                     case GDK_SCROLL_DOWN:
2993                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2994                         break;
2995                     default:
2996                         break;
2997                 }
2998                 ret = TRUE;
2999             }
3000             break;
3001         case GDK_KEY_PRESS:
3002             switch (get_group0_keyval (&event->key)) {
3003                 case GDK_space:
3004                     if (event->key.state & GDK_BUTTON1_MASK) {
3005                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3006                         stamp_repr(nodepath);
3007                         ret = TRUE;
3008                     }
3009                     break;
3010                 case GDK_Page_Up:
3011                     if (event->key.state & GDK_CONTROL_MASK) {
3012                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3013                     } else {
3014                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3015                     }
3016                     break;
3017                 case GDK_Page_Down:
3018                     if (event->key.state & GDK_CONTROL_MASK) {
3019                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3020                     } else {
3021                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3022                     }
3023                     break;
3024                 default:
3025                     break;
3026             }
3027             break;
3028         default:
3029             break;
3030     }
3032     return ret;
3035 /**
3036  * Handle keypress on node; directly called.
3037  */
3038 gboolean node_key(GdkEvent *event)
3040     Inkscape::NodePath::Path *np;
3042     // there is no way to verify nodes so set active_node to nil when deleting!!
3043     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3045     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3046         gint ret = FALSE;
3047         switch (get_group0_keyval (&event->key)) {
3048             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3049             case GDK_BackSpace:
3050                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3051                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3052                 sp_nodepath_update_repr(np, _("Delete node"));
3053                 Inkscape::NodePath::Path::active_node = NULL;
3054                 ret = TRUE;
3055                 break;
3056             case GDK_c:
3057                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3058                 ret = TRUE;
3059                 break;
3060             case GDK_s:
3061                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3062                 ret = TRUE;
3063                 break;
3064             case GDK_y:
3065                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3066                 ret = TRUE;
3067                 break;
3068             case GDK_b:
3069                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3070                 ret = TRUE;
3071                 break;
3072         }
3073         return ret;
3074     }
3075     return FALSE;
3078 /**
3079  * Mouseclick on node callback.
3080  */
3081 static void node_clicked(SPKnot *knot, guint state, gpointer data)
3083    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3085     if (state & GDK_CONTROL_MASK) {
3086         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3088         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3089             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3090                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3091             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3092                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3093             } else {
3094                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3095             }
3096             sp_nodepath_update_repr(nodepath, _("Change node type"));
3097             sp_nodepath_update_statusbar(nodepath);
3099         } else { //ctrl+alt+click: delete node
3100             GList *node_to_delete = NULL;
3101             node_to_delete = g_list_append(node_to_delete, n);
3102             sp_node_delete_preserve(node_to_delete);
3103         }
3105     } else {
3106         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3107     }
3110 /**
3111  * Mouse grabbed node callback.
3112  */
3113 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3115    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3117     if (!n->selected) {
3118         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3119     }
3121     n->is_dragging = true;
3122     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3124     sp_nodepath_remember_origins (n->subpath->nodepath);
3127 /**
3128  * Mouse ungrabbed node callback.
3129  */
3130 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
3132    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3134    n->dragging_out = NULL;
3135    n->is_dragging = false;
3136    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3138    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3141 /**
3142  * The point on a line, given by its angle, closest to the given point.
3143  * \param p  A point.
3144  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3145  * \param closest  Pointer to the point struct where the result is stored.
3146  * \todo FIXME: use dot product perhaps?
3147  */
3148 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3150     if (a == HUGE_VAL) { // vertical
3151         *closest = NR::Point(0, (*p)[NR::Y]);
3152     } else {
3153         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3154         (*closest)[NR::Y] = a * (*closest)[NR::X];
3155     }
3158 /**
3159  * Distance from the point to a line given by its angle.
3160  * \param p  A point.
3161  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3162  */
3163 static double point_line_distance(NR::Point *p, double a)
3165     NR::Point c;
3166     point_line_closest(p, a, &c);
3167     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]));
3170 /**
3171  * Callback for node "request" signal.
3172  * \todo fixme: This goes to "moved" event? (lauris)
3173  */
3174 static gboolean
3175 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3177     double yn, xn, yp, xp;
3178     double an, ap, na, pa;
3179     double d_an, d_ap, d_na, d_pa;
3180     gboolean collinear = FALSE;
3181     NR::Point c;
3182     NR::Point pr;
3184    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3186    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3187    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3189        NR::Point mouse = (*p);
3191        if (!n->dragging_out) {
3192            // This is the first drag-out event; find out which handle to drag out
3193            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3194            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3196            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3197                return FALSE;
3199            Inkscape::NodePath::NodeSide *opposite;
3200            if (appr_p > appr_n) { // closer to p
3201                n->dragging_out = &n->p;
3202                opposite = &n->n;
3203                n->code = NR_CURVETO;
3204            } else if (appr_p < appr_n) { // closer to n
3205                n->dragging_out = &n->n;
3206                opposite = &n->p;
3207                n->n.other->code = NR_CURVETO;
3208            } else { // p and n nodes are the same
3209                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3210                    n->dragging_out = &n->p;
3211                    opposite = &n->n;
3212                    n->code = NR_CURVETO;
3213                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3214                    n->dragging_out = &n->n;
3215                    opposite = &n->p;
3216                    n->n.other->code = NR_CURVETO;
3217                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3218                    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);
3219                    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);
3220                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3221                        n->dragging_out = &n->n;
3222                        opposite = &n->p;
3223                        n->n.other->code = NR_CURVETO;
3224                    } else { // closer to other's n handle
3225                        n->dragging_out = &n->p;
3226                        opposite = &n->n;
3227                        n->code = NR_CURVETO;
3228                    }
3229                }
3230            }
3232            // if there's another handle, make sure the one we drag out starts parallel to it
3233            if (opposite->pos != n->pos) {
3234                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3235            }
3237            // knots might not be created yet!
3238            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3239            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3240        }
3242        // pass this on to the handle-moved callback
3243        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3244        sp_node_update_handles(n);
3245        return TRUE;
3246    }
3248     if (state & GDK_CONTROL_MASK) { // constrained motion
3250         // calculate relative distances of handles
3251         // n handle:
3252         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3253         xn = n->n.pos[NR::X] - n->pos[NR::X];
3254         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3255         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3256             if (n->n.other) { // if there is the next point
3257                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3258                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3259                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3260             }
3261         }
3262         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3263         if (yn < 0) { xn = -xn; yn = -yn; }
3265         // p handle:
3266         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3267         xp = n->p.pos[NR::X] - n->pos[NR::X];
3268         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3269         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3270             if (n->p.other) {
3271                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3272                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3273                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3274             }
3275         }
3276         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3277         if (yp < 0) { xp = -xp; yp = -yp; }
3279         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3280             // sliding on handles, only if at least one of the handles is non-vertical
3281             // (otherwise it's the same as ctrl+drag anyway)
3283             // calculate angles of the handles
3284             if (xn == 0) {
3285                 if (yn == 0) { // no handle, consider it the continuation of the other one
3286                     an = 0;
3287                     collinear = TRUE;
3288                 }
3289                 else an = 0; // vertical; set the angle to horizontal
3290             } else an = yn/xn;
3292             if (xp == 0) {
3293                 if (yp == 0) { // no handle, consider it the continuation of the other one
3294                     ap = an;
3295                 }
3296                 else ap = 0; // vertical; set the angle to horizontal
3297             } else  ap = yp/xp;
3299             if (collinear) an = ap;
3301             // angles of the perpendiculars; HUGE_VAL means vertical
3302             if (an == 0) na = HUGE_VAL; else na = -1/an;
3303             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3305             // mouse point relative to the node's original pos
3306             pr = (*p) - n->origin;
3308             // distances to the four lines (two handles and two perpendiculars)
3309             d_an = point_line_distance(&pr, an);
3310             d_na = point_line_distance(&pr, na);
3311             d_ap = point_line_distance(&pr, ap);
3312             d_pa = point_line_distance(&pr, pa);
3314             // find out which line is the closest, save its closest point in c
3315             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3316                 point_line_closest(&pr, an, &c);
3317             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3318                 point_line_closest(&pr, ap, &c);
3319             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3320                 point_line_closest(&pr, na, &c);
3321             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3322                 point_line_closest(&pr, pa, &c);
3323             }
3325             // move the node to the closest point
3326             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3327                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3328                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3330         } else {  // constraining to hor/vert
3332             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3333                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3334             } else { // snap to vert
3335                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3336             }
3337         }
3338     } else { // move freely
3339         if (n->is_dragging) {
3340             if (state & GDK_MOD1_MASK) { // sculpt
3341                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3342             } else {
3343                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3344                                             (*p)[NR::X] - n->pos[NR::X],
3345                                             (*p)[NR::Y] - n->pos[NR::Y],
3346                                             (state & GDK_SHIFT_MASK) == 0);
3347             }
3348         }
3349     }
3351     n->subpath->nodepath->desktop->scroll_to_point(p);
3353     return TRUE;
3356 /**
3357  * Node handle clicked callback.
3358  */
3359 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3361    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3363     if (state & GDK_CONTROL_MASK) { // "delete" handle
3364         if (n->p.knot == knot) {
3365             n->p.pos = n->pos;
3366         } else if (n->n.knot == knot) {
3367             n->n.pos = n->pos;
3368         }
3369         sp_node_update_handles(n);
3370         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3371         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3372         sp_nodepath_update_statusbar(nodepath);
3374     } else { // just select or add to selection, depending in Shift
3375         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3376     }
3379 /**
3380  * Node handle grabbed callback.
3381  */
3382 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3384    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3386     if (!n->selected) {
3387         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3388     }
3390     // remember the origin point of the handle
3391     if (n->p.knot == knot) {
3392         n->p.origin_radial = n->p.pos - n->pos;
3393     } else if (n->n.knot == knot) {
3394         n->n.origin_radial = n->n.pos - n->pos;
3395     } else {
3396         g_assert_not_reached();
3397     }
3399     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3402 /**
3403  * Node handle ungrabbed callback.
3404  */
3405 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3407    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3409     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3410     if (n->p.knot == knot) {
3411         n->p.origin_radial.a = 0;
3412         sp_knot_set_position(knot, &n->p.pos, state);
3413     } else if (n->n.knot == knot) {
3414         n->n.origin_radial.a = 0;
3415         sp_knot_set_position(knot, &n->n.pos, state);
3416     } else {
3417         g_assert_not_reached();
3418     }
3420     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3423 /**
3424  * Node handle "request" signal callback.
3425  */
3426 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3428     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3430     Inkscape::NodePath::NodeSide *me, *opposite;
3431     gint which;
3432     if (n->p.knot == knot) {
3433         me = &n->p;
3434         opposite = &n->n;
3435         which = -1;
3436     } else if (n->n.knot == knot) {
3437         me = &n->n;
3438         opposite = &n->p;
3439         which = 1;
3440     } else {
3441         me = opposite = NULL;
3442         which = 0;
3443         g_assert_not_reached();
3444     }
3446     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3448     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3450     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3451         /* We are smooth node adjacent with line */
3452         NR::Point const delta = *p - n->pos;
3453         NR::Coord const len = NR::L2(delta);
3454         Inkscape::NodePath::Node *othernode = opposite->other;
3455         NR::Point const ndelta = n->pos - othernode->pos;
3456         NR::Coord const linelen = NR::L2(ndelta);
3457         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3458             NR::Coord const scal = dot(delta, ndelta) / linelen;
3459             (*p) = n->pos + (scal / linelen) * ndelta;
3460         }
3461         *p = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), n->subpath->nodepath->item).getPoint();
3462     } else {
3463         *p = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, n->subpath->nodepath->item).getPoint();
3464     }
3466     sp_node_adjust_handle(n, -which);
3468     return FALSE;
3471 /**
3472  * Node handle moved callback.
3473  */
3474 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3476    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3478    Inkscape::NodePath::NodeSide *me;
3479    Inkscape::NodePath::NodeSide *other;
3480     if (n->p.knot == knot) {
3481         me = &n->p;
3482         other = &n->n;
3483     } else if (n->n.knot == knot) {
3484         me = &n->n;
3485         other = &n->p;
3486     } else {
3487         me = NULL;
3488         other = NULL;
3489         g_assert_not_reached();
3490     }
3492     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3493     Radial rme(me->pos - n->pos);
3494     Radial rother(other->pos - n->pos);
3495     Radial rnew(*p - n->pos);
3497     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3498         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3499         /* 0 interpreted as "no snapping". */
3501         // The closest PI/snaps angle, starting from zero.
3502         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3503         if (me->origin_radial.a == HUGE_VAL) {
3504             // ortho doesn't exist: original handle was zero length.
3505             rnew.a = a_snapped;
3506         } else {
3507             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3508              * its opposite and perpendiculars). */
3509             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3511             // Snap to the closest.
3512             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3513                        ? a_snapped
3514                        : a_ortho );
3515         }
3516     }
3518     if (state & GDK_MOD1_MASK) {
3519         // lock handle length
3520         rnew.r = me->origin_radial.r;
3521     }
3523     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3524         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3525         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3526         rother.a += rnew.a - rme.a;
3527         other->pos = NR::Point(rother) + n->pos;
3528         if (other->knot) {
3529             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3530             sp_knot_moveto(other->knot, &other->pos);
3531         }
3532     }
3534     me->pos = NR::Point(rnew) + n->pos;
3535     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3537     // move knot, but without emitting the signal:
3538     // we cannot emit a "moved" signal because we're now processing it
3539     sp_knot_moveto(me->knot, &(me->pos));
3541     update_object(n->subpath->nodepath);
3543     /* status text */
3544     SPDesktop *desktop = n->subpath->nodepath->desktop;
3545     if (!desktop) return;
3546     SPEventContext *ec = desktop->event_context;
3547     if (!ec) return;
3548     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3549     if (!mc) return;
3551     double degrees = 180 / M_PI * rnew.a;
3552     if (degrees > 180) degrees -= 360;
3553     if (degrees < -180) degrees += 360;
3554     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3555         degrees = angle_to_compass (degrees);
3557     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3559     mc->setF(Inkscape::NORMAL_MESSAGE,
3560          _("<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);
3562     g_string_free(length, TRUE);
3565 /**
3566  * Node handle event callback.
3567  */
3568 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3570     gboolean ret = FALSE;
3571     switch (event->type) {
3572         case GDK_KEY_PRESS:
3573             switch (get_group0_keyval (&event->key)) {
3574                 case GDK_space:
3575                     if (event->key.state & GDK_BUTTON1_MASK) {
3576                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3577                         stamp_repr(nodepath);
3578                         ret = TRUE;
3579                     }
3580                     break;
3581                 default:
3582                     break;
3583             }
3584             break;
3585         case GDK_ENTER_NOTIFY:
3586             // we use an experimentally determined threshold that seems to work fine
3587             if (NR::L2(n->pos - knot->pos) < 0.75)
3588                 Inkscape::NodePath::Path::active_node = n;
3589             break;
3590         case GDK_LEAVE_NOTIFY:
3591             // we use an experimentally determined threshold that seems to work fine
3592             if (NR::L2(n->pos - knot->pos) < 0.75)
3593                 Inkscape::NodePath::Path::active_node = NULL;
3594             break;
3595         default:
3596             break;
3597     }
3599     return ret;
3602 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3603                                  Radial &rme, Radial &rother, gboolean const both)
3605     rme.a += angle;
3606     if ( both
3607          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3608          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3609     {
3610         rother.a += angle;
3611     }
3614 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3615                                         Radial &rme, Radial &rother, gboolean const both)
3617     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3619     gdouble r;
3620     if ( both
3621          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3622          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3623     {
3624         r = MAX(rme.r, rother.r);
3625     } else {
3626         r = rme.r;
3627     }
3629     gdouble const weird_angle = atan2(norm_angle, r);
3630 /* Bulia says norm_angle is just the visible distance that the
3631  * object's end must travel on the screen.  Left as 'angle' for want of
3632  * a better name.*/
3634     rme.a += weird_angle;
3635     if ( both
3636          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3637          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3638     {
3639         rother.a += weird_angle;
3640     }
3643 /**
3644  * Rotate one node.
3645  */
3646 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3648     Inkscape::NodePath::NodeSide *me, *other;
3649     bool both = false;
3651     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3652     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3654     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3655         me = &(n->p);
3656         other = &(n->n);
3657     } else if (!n->p.other) {
3658         me = &(n->n);
3659         other = &(n->p);
3660     } else {
3661         if (which > 0) { // right handle
3662             if (xn > xp) {
3663                 me = &(n->n);
3664                 other = &(n->p);
3665             } else {
3666                 me = &(n->p);
3667                 other = &(n->n);
3668             }
3669         } else if (which < 0){ // left handle
3670             if (xn <= xp) {
3671                 me = &(n->n);
3672                 other = &(n->p);
3673             } else {
3674                 me = &(n->p);
3675                 other = &(n->n);
3676             }
3677         } else { // both handles
3678             me = &(n->n);
3679             other = &(n->p);
3680             both = true;
3681         }
3682     }
3684     Radial rme(me->pos - n->pos);
3685     Radial rother(other->pos - n->pos);
3687     if (screen) {
3688         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3689     } else {
3690         node_rotate_one_internal (*n, angle, rme, rother, both);
3691     }
3693     me->pos = n->pos + NR::Point(rme);
3695     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3696         other->pos =  n->pos + NR::Point(rother);
3697     }
3699     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3700     // so here we just move all the knots without emitting move signals, for speed
3701     sp_node_update_handles(n, false);
3704 /**
3705  * Rotate selected nodes.
3706  */
3707 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3709     if (!nodepath || !nodepath->selected) return;
3711     if (g_list_length(nodepath->selected) == 1) {
3712        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3713         node_rotate_one (n, angle, which, screen);
3714     } else {
3715        // rotate as an object:
3717         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3718         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3719         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3720             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3721             box.expandTo (n->pos); // contain all selected nodes
3722         }
3724         gdouble rot;
3725         if (screen) {
3726             gdouble const zoom = nodepath->desktop->current_zoom();
3727             gdouble const zmove = angle / zoom;
3728             gdouble const r = NR::L2(box.max() - box.midpoint());
3729             rot = atan2(zmove, r);
3730         } else {
3731             rot = angle;
3732         }
3734         NR::Point rot_center;
3735         if (Inkscape::NodePath::Path::active_node == NULL)
3736             rot_center = box.midpoint();
3737         else
3738             rot_center = Inkscape::NodePath::Path::active_node->pos;
3740         NR::Matrix t =
3741             NR::Matrix (NR::translate(-rot_center)) *
3742             NR::Matrix (NR::rotate(rot)) *
3743             NR::Matrix (NR::translate(rot_center));
3745         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3746             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3747             n->pos *= t;
3748             n->n.pos *= t;
3749             n->p.pos *= t;
3750             sp_node_update_handles(n, false);
3751         }
3752     }
3754     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3757 /**
3758  * Scale one node.
3759  */
3760 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3762     bool both = false;
3763     Inkscape::NodePath::NodeSide *me, *other;
3765     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3766     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3768     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3769         me = &(n->p);
3770         other = &(n->n);
3771         n->code = NR_CURVETO;
3772     } else if (!n->p.other) {
3773         me = &(n->n);
3774         other = &(n->p);
3775         if (n->n.other)
3776             n->n.other->code = NR_CURVETO;
3777     } else {
3778         if (which > 0) { // right handle
3779             if (xn > xp) {
3780                 me = &(n->n);
3781                 other = &(n->p);
3782                 if (n->n.other)
3783                     n->n.other->code = NR_CURVETO;
3784             } else {
3785                 me = &(n->p);
3786                 other = &(n->n);
3787                 n->code = NR_CURVETO;
3788             }
3789         } else if (which < 0){ // left handle
3790             if (xn <= xp) {
3791                 me = &(n->n);
3792                 other = &(n->p);
3793                 if (n->n.other)
3794                     n->n.other->code = NR_CURVETO;
3795             } else {
3796                 me = &(n->p);
3797                 other = &(n->n);
3798                 n->code = NR_CURVETO;
3799             }
3800         } else { // both handles
3801             me = &(n->n);
3802             other = &(n->p);
3803             both = true;
3804             n->code = NR_CURVETO;
3805             if (n->n.other)
3806                 n->n.other->code = NR_CURVETO;
3807         }
3808     }
3810     Radial rme(me->pos - n->pos);
3811     Radial rother(other->pos - n->pos);
3813     rme.r += grow;
3814     if (rme.r < 0) rme.r = 0;
3815     if (rme.a == HUGE_VAL) {
3816         if (me->other) { // if direction is unknown, initialize it towards the next node
3817             Radial rme_next(me->other->pos - n->pos);
3818             rme.a = rme_next.a;
3819         } else { // if there's no next, initialize to 0
3820             rme.a = 0;
3821         }
3822     }
3823     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3824         rother.r += grow;
3825         if (rother.r < 0) rother.r = 0;
3826         if (rother.a == HUGE_VAL) {
3827             rother.a = rme.a + M_PI;
3828         }
3829     }
3831     me->pos = n->pos + NR::Point(rme);
3833     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3834         other->pos = n->pos + NR::Point(rother);
3835     }
3837     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3838     // so here we just move all the knots without emitting move signals, for speed
3839     sp_node_update_handles(n, false);
3842 /**
3843  * Scale selected nodes.
3844  */
3845 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3847     if (!nodepath || !nodepath->selected) return;
3849     if (g_list_length(nodepath->selected) == 1) {
3850         // scale handles of the single selected node
3851         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3852         node_scale_one (n, grow, which);
3853     } else {
3854         // scale nodes as an "object":
3856         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3857         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3858         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3859             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3860             box.expandTo (n->pos); // contain all selected nodes
3861         }
3863         double scale = (box.maxExtent() + grow)/box.maxExtent();
3865         NR::Point scale_center;
3866         if (Inkscape::NodePath::Path::active_node == NULL)
3867             scale_center = box.midpoint();
3868         else
3869             scale_center = Inkscape::NodePath::Path::active_node->pos;
3871         NR::Matrix t =
3872             NR::Matrix (NR::translate(-scale_center)) *
3873             NR::Matrix (NR::scale(scale, scale)) *
3874             NR::Matrix (NR::translate(scale_center));
3876         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3877             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3878             n->pos *= t;
3879             n->n.pos *= t;
3880             n->p.pos *= t;
3881             sp_node_update_handles(n, false);
3882         }
3883     }
3885     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3888 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3890     if (!nodepath) return;
3891     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3894 /**
3895  * Flip selected nodes horizontally/vertically.
3896  */
3897 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
3899     if (!nodepath || !nodepath->selected) return;
3901     if (g_list_length(nodepath->selected) == 1 && !center) {
3902         // flip handles of the single selected node
3903         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3904         double temp = n->p.pos[axis];
3905         n->p.pos[axis] = n->n.pos[axis];
3906         n->n.pos[axis] = temp;
3907         sp_node_update_handles(n, false);
3908     } else {
3909         // scale nodes as an "object":
3911         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3912         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3913         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3914             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3915             box.expandTo (n->pos); // contain all selected nodes
3916         }
3918         if (!center) {
3919             center = box.midpoint();
3920         }
3921         NR::Matrix t =
3922             NR::Matrix (NR::translate(- *center)) *
3923             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3924             NR::Matrix (NR::translate(*center));
3926         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3927             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3928             n->pos *= t;
3929             n->n.pos *= t;
3930             n->p.pos *= t;
3931             sp_node_update_handles(n, false);
3932         }
3933     }
3935     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3938 //-----------------------------------------------
3939 /**
3940  * Return new subpath under given nodepath.
3941  */
3942 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3944     g_assert(nodepath);
3945     g_assert(nodepath->desktop);
3947    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3949     s->nodepath = nodepath;
3950     s->closed = FALSE;
3951     s->nodes = NULL;
3952     s->first = NULL;
3953     s->last = NULL;
3955     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3956     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3957     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3959     return s;
3962 /**
3963  * Destroy nodes in subpath, then subpath itself.
3964  */
3965 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3967     g_assert(subpath);
3968     g_assert(subpath->nodepath);
3969     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3971     while (subpath->nodes) {
3972         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3973     }
3975     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3977     g_free(subpath);
3980 /**
3981  * Link head to tail in subpath.
3982  */
3983 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3985     g_assert(!sp->closed);
3986     g_assert(sp->last != sp->first);
3987     g_assert(sp->first->code == NR_MOVETO);
3989     sp->closed = TRUE;
3991     //Link the head to the tail
3992     sp->first->p.other = sp->last;
3993     sp->last->n.other  = sp->first;
3994     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3995     sp->first          = sp->last;
3997     //Remove the extra end node
3998     sp_nodepath_node_destroy(sp->last->n.other);
4001 /**
4002  * Open closed (loopy) subpath at node.
4003  */
4004 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4006     g_assert(sp->closed);
4007     g_assert(n->subpath == sp);
4008     g_assert(sp->first == sp->last);
4010     /* We create new startpoint, current node will become last one */
4012    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4013                                                 &n->pos, &n->pos, &n->n.pos);
4016     sp->closed        = FALSE;
4018     //Unlink to make a head and tail
4019     sp->first         = new_path;
4020     sp->last          = n;
4021     n->n.other        = NULL;
4022     new_path->p.other = NULL;
4025 /**
4026  * Returns area in triangle given by points; may be negative.
4027  */
4028 inline double
4029 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
4031     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]);
4034 /**
4035  * Return new node in subpath with given properties.
4036  * \param pos Position of node.
4037  * \param ppos Handle position in previous direction
4038  * \param npos Handle position in previous direction
4039  */
4040 Inkscape::NodePath::Node *
4041 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)
4043     g_assert(sp);
4044     g_assert(sp->nodepath);
4045     g_assert(sp->nodepath->desktop);
4047     if (nodechunk == NULL)
4048         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4050     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4052     n->subpath  = sp;
4054     if (type != Inkscape::NodePath::NODE_NONE) {
4055         // use the type from sodipodi:nodetypes
4056         n->type = type;
4057     } else {
4058         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4059             // points are (almost) collinear
4060             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4061                 // endnode, or a node with a retracted handle
4062                 n->type = Inkscape::NodePath::NODE_CUSP;
4063             } else {
4064                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4065             }
4066         } else {
4067             n->type = Inkscape::NodePath::NODE_CUSP;
4068         }
4069     }
4071     n->code     = code;
4072     n->selected = FALSE;
4073     n->pos      = *pos;
4074     n->p.pos    = *ppos;
4075     n->n.pos    = *npos;
4077     n->dragging_out = NULL;
4079     Inkscape::NodePath::Node *prev;
4080     if (next) {
4081         //g_assert(g_list_find(sp->nodes, next));
4082         prev = next->p.other;
4083     } else {
4084         prev = sp->last;
4085     }
4087     if (prev)
4088         prev->n.other = n;
4089     else
4090         sp->first = n;
4092     if (next)
4093         next->p.other = n;
4094     else
4095         sp->last = n;
4097     n->p.other = prev;
4098     n->n.other = next;
4100     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"));
4101     sp_knot_set_position(n->knot, pos, 0);
4103     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4104     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4105     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4106     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4107     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4108     sp_knot_update_ctrl(n->knot);
4110     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4111     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4112     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4113     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4114     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4115     sp_knot_show(n->knot);
4117     // We only create handle knots and lines on demand
4118     n->p.knot = NULL;
4119     n->p.line = NULL;
4120     n->n.knot = NULL;
4121     n->n.line = NULL;
4123     sp->nodes = g_list_prepend(sp->nodes, n);
4125     return n;
4128 /**
4129  * Destroy node and its knots, link neighbors in subpath.
4130  */
4131 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4133     g_assert(node);
4134     g_assert(node->subpath);
4135     g_assert(SP_IS_KNOT(node->knot));
4137    Inkscape::NodePath::SubPath *sp = node->subpath;
4139     if (node->selected) { // first, deselect
4140         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4141         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4142     }
4144     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4146     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4147     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4148     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4149     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4150     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4151     g_object_unref(G_OBJECT(node->knot));
4153     if (node->p.knot) {
4154         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4155         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4156         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4157         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4158         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4159         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4160         g_object_unref(G_OBJECT(node->p.knot));
4161         node->p.knot = NULL;
4162     }
4164     if (node->n.knot) {
4165         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4166         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4167         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4168         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4169         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4170         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4171         g_object_unref(G_OBJECT(node->n.knot));
4172         node->n.knot = NULL;
4173     }
4175     if (node->p.line)
4176         gtk_object_destroy(GTK_OBJECT(node->p.line));
4177     if (node->n.line)
4178         gtk_object_destroy(GTK_OBJECT(node->n.line));
4180     if (sp->nodes) { // there are others nodes on the subpath
4181         if (sp->closed) {
4182             if (sp->first == node) {
4183                 g_assert(sp->last == node);
4184                 sp->first = node->n.other;
4185                 sp->last = sp->first;
4186             }
4187             node->p.other->n.other = node->n.other;
4188             node->n.other->p.other = node->p.other;
4189         } else {
4190             if (sp->first == node) {
4191                 sp->first = node->n.other;
4192                 sp->first->code = NR_MOVETO;
4193             }
4194             if (sp->last == node) sp->last = node->p.other;
4195             if (node->p.other) node->p.other->n.other = node->n.other;
4196             if (node->n.other) node->n.other->p.other = node->p.other;
4197         }
4198     } else { // this was the last node on subpath
4199         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4200     }
4202     g_mem_chunk_free(nodechunk, node);
4205 /**
4206  * Returns one of the node's two sides.
4207  * \param which Indicates which side.
4208  * \return Pointer to previous node side if which==-1, next if which==1.
4209  */
4210 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4212     g_assert(node);
4214     switch (which) {
4215         case -1:
4216             return &node->p;
4217         case 1:
4218             return &node->n;
4219         default:
4220             break;
4221     }
4223     g_assert_not_reached();
4225     return NULL;
4228 /**
4229  * Return the other side of the node, given one of its sides.
4230  */
4231 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4233     g_assert(node);
4235     if (me == &node->p) return &node->n;
4236     if (me == &node->n) return &node->p;
4238     g_assert_not_reached();
4240     return NULL;
4243 /**
4244  * Return NRPathcode on the given side of the node.
4245  */
4246 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4248     g_assert(node);
4250     if (me == &node->p) {
4251         if (node->p.other) return (NRPathcode)node->code;
4252         return NR_MOVETO;
4253     }
4255     if (me == &node->n) {
4256         if (node->n.other) return (NRPathcode)node->n.other->code;
4257         return NR_MOVETO;
4258     }
4260     g_assert_not_reached();
4262     return NR_END;
4265 /**
4266  * Return node with the given index
4267  */
4268 Inkscape::NodePath::Node *
4269 sp_nodepath_get_node_by_index(int index)
4271     Inkscape::NodePath::Node *e = NULL;
4273     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4274     if (!nodepath) {
4275         return e;
4276     }
4278     //find segment
4279     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4281         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4282         int n = g_list_length(sp->nodes);
4283         if (sp->closed) {
4284             n++;
4285         }
4287         //if the piece belongs to this subpath grab it
4288         //otherwise move onto the next subpath
4289         if (index < n) {
4290             e = sp->first;
4291             for (int i = 0; i < index; ++i) {
4292                 e = e->n.other;
4293             }
4294             break;
4295         } else {
4296             if (sp->closed) {
4297                 index -= (n+1);
4298             } else {
4299                 index -= n;
4300             }
4301         }
4302     }
4304     return e;
4307 /**
4308  * Returns plain text meaning of node type.
4309  */
4310 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4312     unsigned retracted = 0;
4313     bool endnode = false;
4315     for (int which = -1; which <= 1; which += 2) {
4316         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4317         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4318             retracted ++;
4319         if (!side->other)
4320             endnode = true;
4321     }
4323     if (retracted == 0) {
4324         if (endnode) {
4325                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4326                 return _("end node");
4327         } else {
4328             switch (node->type) {
4329                 case Inkscape::NodePath::NODE_CUSP:
4330                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4331                     return _("cusp");
4332                 case Inkscape::NodePath::NODE_SMOOTH:
4333                     // TRANSLATORS: "smooth" is an adjective here
4334                     return _("smooth");
4335                 case Inkscape::NodePath::NODE_SYMM:
4336                     return _("symmetric");
4337             }
4338         }
4339     } else if (retracted == 1) {
4340         if (endnode) {
4341             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4342             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4343         } else {
4344             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4345         }
4346     } else {
4347         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4348     }
4350     return NULL;
4353 /**
4354  * Handles content of statusbar as long as node tool is active.
4355  */
4356 void
4357 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4359     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");
4360     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4362     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4363     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4364     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4365     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4367     SPDesktop *desktop = NULL;
4368     if (nodepath) {
4369         desktop = nodepath->desktop;
4370     } else {
4371         desktop = SP_ACTIVE_DESKTOP;
4372     }
4374     SPEventContext *ec = desktop->event_context;
4375     if (!ec) return;
4376     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4377     if (!mc) return;
4379     if (selected_nodes == 0) {
4380         Inkscape::Selection *sel = desktop->selection;
4381         if (!sel || sel->isEmpty()) {
4382             mc->setF(Inkscape::NORMAL_MESSAGE,
4383                      _("Select a single object to edit its nodes or handles."));
4384         } else {
4385             if (nodepath) {
4386             mc->setF(Inkscape::NORMAL_MESSAGE,
4387                      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.",
4388                               "<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.",
4389                               total_nodes),
4390                      total_nodes);
4391             } else {
4392                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4393                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4394                 } else {
4395                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4396                 }
4397             }
4398         }
4399     } else if (nodepath && selected_nodes == 1) {
4400         mc->setF(Inkscape::NORMAL_MESSAGE,
4401                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4402                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4403                           total_nodes),
4404                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4405     } else {
4406         if (selected_subpaths > 1) {
4407             mc->setF(Inkscape::NORMAL_MESSAGE,
4408                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4409                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4410                               total_nodes),
4411                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4412         } else {
4413             mc->setF(Inkscape::NORMAL_MESSAGE,
4414                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4415                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4416                               total_nodes),
4417                      selected_nodes, total_nodes, when_selected);
4418         }
4419     }
4422 /*
4423  * returns a *copy* of the curve of that object.
4424  */
4425 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4426     if (!object)
4427         return NULL;
4429     SPCurve *curve = NULL;
4430     if (SP_IS_PATH(object)) {
4431         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4432         curve = sp_curve_copy(curve_new);
4433     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4434         const gchar *svgd = object->repr->attribute(key);
4435         if (svgd) {
4436             NArtBpath *bpath = sp_svg_read_path(svgd);
4437             SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
4438             if (curve_new) {
4439                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4440             } else {
4441                 g_free(bpath);
4442             }
4443         }
4444     }
4446     return curve;
4449 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4450     if (!np || !np->object || !curve)
4451         return;
4453     if (SP_IS_PATH(np->object)) {
4454         if (SP_SHAPE(np->object)->path_effect_href) {
4455             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4456         } else {
4457             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4458         }
4459     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4460         // FIXME: this writing to string and then reading from string is bound to be slow.
4461         // create a method to convert from curve directly to 2geom...
4462         gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
4463         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4464         g_free(svgpath);
4466         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4467     }
4470 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4471     np->show_helperpath = show;
4474 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4475     np->straight_path = true;
4476     np->show_handles = false;
4477     g_message("add code to make the path straight.");
4478     // do sp_nodepath_convert_node_type on all nodes?
4479     // search for this text !!!   "Make selected segments lines"
4483 /*
4484   Local Variables:
4485   mode:c++
4486   c-file-style:"stroustrup"
4487   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4488   indent-tabs-mode:nil
4489   fill-column:99
4490   End:
4491 */
4492 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :