Code

Don't snap node handles to the parent path, plus a small string change
[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_object_set_curve (SPObject *object, 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 *   repr_key_in should be NULL,  unless you are called Johan or really know what you are doing! (See "if (repr_key_in)" below)
150  */
151 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in)
153     Inkscape::XML::Node *repr = object->repr;
155     /** \todo
156      * FIXME: remove this. We don't want to edit paths inside flowtext.
157      * Instead we will build our flowtext with cloned paths, so that the
158      * real paths are outside the flowtext and thus editable as usual.
159      */
160     if (SP_IS_FLOWTEXT(object)) {
161         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
162             if SP_IS_FLOWREGION(child) {
163                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
164                 if (grandchild && SP_IS_PATH(grandchild)) {
165                     object = SP_ITEM(grandchild);
166                     break;
167                 }
168             }
169         }
170     }
172     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
174     if (curve == NULL)
175         return NULL;
177     NArtBpath *bpath = sp_curve_first_bpath(curve);
178     gint length = curve->end;
179     if (length == 0) {
180         sp_curve_unref(curve);
181         return NULL; // prevent crash for one-node paths
182     }
184     //Create new nodepath
185     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
186     if (!np) {
187         sp_curve_unref(curve);
188         return NULL;
189     }
191     // Set defaults
192     np->desktop     = desktop;
193     np->object      = object;
194     np->subpaths    = NULL;
195     np->selected    = NULL;
196     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
197     np->livarot_path = NULL;
198     np->local_change = 0;
199     np->show_handles = show_handles;
200     np->helper_path = NULL;
201     np->curve = sp_curve_copy(curve);
202     np->show_helperpath = false;
203     np->straight_path = false;
204     
206     // we need to update item's transform from the repr here,
207     // because they may be out of sync when we respond
208     // to a change in repr by regenerating nodepath     --bb
209     sp_object_read_attr(object, "transform");
211     np->i2d  = sp_item_i2d_affine(SP_ITEM(object));
212     np->d2i  = np->i2d.inverse();
214     np->repr = repr;
215     if (repr_key_in) {
216         np->repr_key = g_strdup(repr_key_in);
217         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
218         np->show_helperpath = true;
219     } else {
220         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
221         if ( SP_SHAPE(np->object)->path_effect_href ) {
222             np->repr_key = g_strdup("inkscape:original-d");
223             np->show_helperpath = true;
224         } else {
225             np->repr_key = g_strdup("d");
226         }
227     }
229     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
230     gchar *typestr = parse_nodetypes(nodetypes, length);
232     // create the subpath(s) from the bpath
233     NArtBpath *b = bpath;
234     while (b->code != NR_END) {
235         b = subpath_from_bpath(np, b, typestr + (b - bpath));
236     }
238     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
239     np->subpaths = g_list_reverse(np->subpaths);
241     g_free(typestr);
242     sp_curve_unref(curve);
244     // create the livarot representation from the same item
245     sp_nodepath_ensure_livarot_path(np);
247     // Draw helper curve
248     if (np->show_helperpath) {
249         SPCurve *helper_curve = sp_curve_copy(np->curve);
250         sp_curve_transform(helper_curve, np->i2d );
251         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
252         sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(np->helper_path), 0xff0000ff, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
253         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
254         sp_canvas_item_show(np->helper_path);
255         sp_curve_unref(helper_curve);
256     }
258     return np;
261 /**
262  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
263  */
264 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
266     if (!np)  //soft fail, like delete
267         return;
269     while (np->subpaths) {
270         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
271     }
273     //Inform the ShapeEditor that made me, if any, that I am gone.
274     if (np->shape_editor)
275         np->shape_editor->nodepath_destroyed();
277     g_assert(!np->selected);
279     if (np->livarot_path) {
280         delete np->livarot_path;
281         np->livarot_path = NULL;
282     }
283     
284     if (np->helper_path) {
285         GtkObject *temp = np->helper_path;
286         np->helper_path = NULL;
287         gtk_object_destroy(temp);
288     }
289     if (np->curve) {
290         sp_curve_unref(np->curve);
291         np->curve = NULL;
292     }
294     if (np->repr_key) {
295         g_free(np->repr_key);
296         np->repr_key = NULL;
297     }
298     if (np->repr_nodetypes_key) {
299         g_free(np->repr_nodetypes_key);
300         np->repr_nodetypes_key = NULL;
301     }
303     np->desktop = NULL;
305     g_free(np);
309 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
311     if (np && np->livarot_path == NULL && np->object && SP_IS_ITEM(np->object)) {
312         SPCurve *curve = create_curve(np);
313         NArtBpath *bpath = SP_CURVE_BPATH(curve);
314         np->livarot_path = bpath_to_Path(bpath);
316         if (np->livarot_path)
317             np->livarot_path->ConvertWithBackData(0.01);
319         sp_curve_unref(curve);
320     }
324 /**
325  *  Return the node count of a given NodeSubPath.
326  */
327 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
329     if (!subpath)
330         return 0;
331     gint nodeCount = g_list_length(subpath->nodes);
332     return nodeCount;
335 /**
336  *  Return the node count of a given NodePath.
337  */
338 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
340     if (!np)
341         return 0;
342     gint nodeCount = 0;
343     for (GList *item = np->subpaths ; item ; item=item->next) {
344        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
345         nodeCount += g_list_length(subpath->nodes);
346     }
347     return nodeCount;
350 /**
351  *  Return the subpath count of a given NodePath.
352  */
353 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
355     if (!np)
356         return 0;
357     return g_list_length (np->subpaths);
360 /**
361  *  Return the selected node count of a given NodePath.
362  */
363 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
365     if (!np)
366         return 0;
367     return g_list_length (np->selected);
370 /**
371  *  Return the number of subpaths where nodes are selected in a given NodePath.
372  */
373 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
375     if (!np)
376         return 0;
377     if (!np->selected)
378         return 0;
379     if (!np->selected->next)
380         return 1;
381     gint count = 0;
382     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
383         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
384         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
385             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
386             if (node->selected) {
387                 count ++;
388                 break;
389             }
390         }
391     }
392     return count;
394  
395 /**
396  * Clean up a nodepath after editing.
397  *
398  * Currently we are deleting trivial subpaths.
399  */
400 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
402     GList *badSubPaths = NULL;
404     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
405     for (GList *l = nodepath->subpaths; l ; l=l->next) {
406        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
407        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
408             badSubPaths = g_list_append(badSubPaths, sp);
409     }
411     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
412     //also removes the subpath from nodepath->subpaths
413     for (GList *l = badSubPaths; l ; l=l->next) {
414        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
415         sp_nodepath_subpath_destroy(sp);
416     }
418     g_list_free(badSubPaths);
421 /**
422  * Create new nodepath from b, make it subpath of np.
423  * \param t The node type.
424  * \todo Fixme: t should be a proper type, rather than gchar
425  */
426 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
428     NR::Point ppos, pos, npos;
430     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
432     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
433     bool const closed = (b->code == NR_MOVETO);
435     pos = NR::Point(b->x3, b->y3) * np->i2d;
436     if (b[1].code == NR_CURVETO) {
437         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
438     } else {
439         npos = pos;
440     }
441     Inkscape::NodePath::Node *n;
442     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
443     g_assert(sp->first == n);
444     g_assert(sp->last  == n);
446     b++;
447     t++;
448     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
449         pos = NR::Point(b->x3, b->y3) * np->i2d;
450         if (b->code == NR_CURVETO) {
451             ppos = NR::Point(b->x2, b->y2) * np->i2d;
452         } else {
453             ppos = pos;
454         }
455         if (b[1].code == NR_CURVETO) {
456             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
457         } else {
458             npos = pos;
459         }
460         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
461         b++;
462         t++;
463     }
465     if (closed) sp_nodepath_subpath_close(sp);
467     return b;
470 /**
471  * Convert from sodipodi:nodetypes to new style type string.
472  */
473 static gchar *parse_nodetypes(gchar const *types, gint length)
475     g_assert(length > 0);
477     gchar *typestr = g_new(gchar, length + 1);
479     gint pos = 0;
481     if (types) {
482         for (gint i = 0; types[i] && ( i < length ); i++) {
483             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
484             if (types[i] != '\0') {
485                 switch (types[i]) {
486                     case 's':
487                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
488                         break;
489                     case 'z':
490                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
491                         break;
492                     case 'c':
493                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
494                         break;
495                     default:
496                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
497                         break;
498                 }
499             }
500         }
501     }
503     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
505     return typestr;
508 /**
509  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
510  * updated but repr is not (for speed). Used during curve and node drag.
511  */
512 static void update_object(Inkscape::NodePath::Path *np)
514     g_assert(np);
516     sp_curve_unref(np->curve);
517     np->curve = create_curve(np);
519     sp_nodepath_object_set_curve(np->object, np->curve);
521     if (np->show_helperpath) {
522         SPCurve * helper_curve = sp_curve_copy(np->curve);
523         sp_curve_transform(helper_curve, np->i2d );
524         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
525         sp_curve_unref(helper_curve);
526     }
529 /**
530  * Update XML path node with data from path object.
531  */
532 static void update_repr_internal(Inkscape::NodePath::Path *np)
534     g_assert(np);
536     Inkscape::XML::Node *repr = np->object->repr;
538     sp_curve_unref(np->curve);
539     np->curve = create_curve(np);
540     
541     gchar *typestr = create_typestr(np);
542     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
544     // determine if path has an effect applied and write to correct "d" attribute.
545     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
546         np->local_change++;
547         repr->setAttribute(np->repr_key, svgpath);
548     }
550     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
551         np->local_change++;
552         repr->setAttribute(np->repr_nodetypes_key, typestr);
553     }
555     g_free(svgpath);
556     g_free(typestr);
558     if (np->show_helperpath) {
559         SPCurve * helper_curve = sp_curve_copy(np->curve);
560         sp_curve_transform(helper_curve, np->i2d );
561         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
562         sp_curve_unref(helper_curve);
563     }
564  }
566 /**
567  * Update XML path node with data from path object, commit changes forever.
568  */
569 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
571     //fixme: np can be NULL, so check before proceeding
572     g_return_if_fail(np != NULL);
574     if (np->livarot_path) {
575         delete np->livarot_path;
576         np->livarot_path = NULL;
577     }
579     update_repr_internal(np);
580     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
581     
582     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
583                      annotation);
586 /**
587  * Update XML path node with data from path object, commit changes with undo.
588  */
589 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
591     if (np->livarot_path) {
592         delete np->livarot_path;
593         np->livarot_path = NULL;
594     }
596     update_repr_internal(np);
597     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
598                            annotation);
601 /**
602  * Make duplicate of path, replace corresponding XML node in tree, commit.
603  */
604 static void stamp_repr(Inkscape::NodePath::Path *np)
606     g_assert(np);
608     Inkscape::XML::Node *old_repr = np->object->repr;
609     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
611     // remember the position of the item
612     gint pos = old_repr->position();
613     // remember parent
614     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
616     SPCurve *curve = create_curve(np);
617     gchar *typestr = create_typestr(np);
619     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
621     new_repr->setAttribute(np->repr_key, svgpath);
622     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
624     // add the new repr to the parent
625     parent->appendChild(new_repr);
626     // move to the saved position
627     new_repr->setPosition(pos > 0 ? pos : 0);
629     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
630                      _("Stamp"));
632     Inkscape::GC::release(new_repr);
633     g_free(svgpath);
634     g_free(typestr);
635     sp_curve_unref(curve);
638 /**
639  * Create curve from path.
640  */
641 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
643     SPCurve *curve = sp_curve_new();
645     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
646        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
647         sp_curve_moveto(curve,
648                         sp->first->pos * np->d2i);
649        Inkscape::NodePath::Node *n = sp->first->n.other;
650         while (n) {
651             NR::Point const end_pt = n->pos * np->d2i;
652             switch (n->code) {
653                 case NR_LINETO:
654                     sp_curve_lineto(curve, end_pt);
655                     break;
656                 case NR_CURVETO:
657                     sp_curve_curveto(curve,
658                                      n->p.other->n.pos * np->d2i,
659                                      n->p.pos * np->d2i,
660                                      end_pt);
661                     break;
662                 default:
663                     g_assert_not_reached();
664                     break;
665             }
666             if (n != sp->last) {
667                 n = n->n.other;
668             } else {
669                 n = NULL;
670             }
671         }
672         if (sp->closed) {
673             sp_curve_closepath(curve);
674         }
675     }
677     return curve;
680 /**
681  * Convert path type string to sodipodi:nodetypes style.
682  */
683 static gchar *create_typestr(Inkscape::NodePath::Path *np)
685     gchar *typestr = g_new(gchar, 32);
686     gint len = 32;
687     gint pos = 0;
689     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
690        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
692         if (pos >= len) {
693             typestr = g_renew(gchar, typestr, len + 32);
694             len += 32;
695         }
697         typestr[pos++] = 'c';
699        Inkscape::NodePath::Node *n;
700         n = sp->first->n.other;
701         while (n) {
702             gchar code;
704             switch (n->type) {
705                 case Inkscape::NodePath::NODE_CUSP:
706                     code = 'c';
707                     break;
708                 case Inkscape::NodePath::NODE_SMOOTH:
709                     code = 's';
710                     break;
711                 case Inkscape::NodePath::NODE_SYMM:
712                     code = 'z';
713                     break;
714                 default:
715                     g_assert_not_reached();
716                     code = '\0';
717                     break;
718             }
720             if (pos >= len) {
721                 typestr = g_renew(gchar, typestr, len + 32);
722                 len += 32;
723             }
725             typestr[pos++] = code;
727             if (n != sp->last) {
728                 n = n->n.other;
729             } else {
730                 n = NULL;
731             }
732         }
733     }
735     if (pos >= len) {
736         typestr = g_renew(gchar, typestr, len + 1);
737         len += 1;
738     }
740     typestr[pos++] = '\0';
742     return typestr;
745 /**
746  * Returns current path in context. // later eliminate this function at all!
747  */
748 static Inkscape::NodePath::Path *sp_nodepath_current()
750     if (!SP_ACTIVE_DESKTOP) {
751         return NULL;
752     }
754     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
756     if (!SP_IS_NODE_CONTEXT(event_context)) {
757         return NULL;
758     }
760     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
765 /**
766  \brief Fills node and handle positions for three nodes, splitting line
767   marked by end at distance t.
768  */
769 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
771     g_assert(new_path != NULL);
772     g_assert(end      != NULL);
774     g_assert(end->p.other == new_path);
775    Inkscape::NodePath::Node *start = new_path->p.other;
776     g_assert(start);
778     if (end->code == NR_LINETO) {
779         new_path->type =Inkscape::NodePath::NODE_CUSP;
780         new_path->code = NR_LINETO;
781         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
782     } else {
783         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
784         new_path->code = NR_CURVETO;
785         gdouble s      = 1 - t;
786         for (int dim = 0; dim < 2; dim++) {
787             NR::Coord const f000 = start->pos[dim];
788             NR::Coord const f001 = start->n.pos[dim];
789             NR::Coord const f011 = end->p.pos[dim];
790             NR::Coord const f111 = end->pos[dim];
791             NR::Coord const f00t = s * f000 + t * f001;
792             NR::Coord const f01t = s * f001 + t * f011;
793             NR::Coord const f11t = s * f011 + t * f111;
794             NR::Coord const f0tt = s * f00t + t * f01t;
795             NR::Coord const f1tt = s * f01t + t * f11t;
796             NR::Coord const fttt = s * f0tt + t * f1tt;
797             start->n.pos[dim]    = f00t;
798             new_path->p.pos[dim] = f0tt;
799             new_path->pos[dim]   = fttt;
800             new_path->n.pos[dim] = f1tt;
801             end->p.pos[dim]      = f11t;
802         }
803     }
806 /**
807  * Adds new node on direct line between two nodes, activates handles of all
808  * three nodes.
809  */
810 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
812     g_assert(end);
813     g_assert(end->subpath);
814     g_assert(g_list_find(end->subpath->nodes, end));
816    Inkscape::NodePath::Node *start = end->p.other;
817     g_assert( start->n.other == end );
818    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
819                                                end,
820                                                (NRPathcode)end->code == NR_LINETO? 
821                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
822                                                (NRPathcode)end->code,
823                                                &start->pos, &start->pos, &start->n.pos);
824     sp_nodepath_line_midpoint(newnode, end, t);
826     sp_node_adjust_handles(start);
827     sp_node_update_handles(start);
828     sp_node_update_handles(newnode);
829     sp_node_adjust_handles(end);
830     sp_node_update_handles(end);
832     return newnode;
835 /**
836 \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
837 */
838 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
840     g_assert(node);
841     g_assert(node->subpath);
842     g_assert(g_list_find(node->subpath->nodes, node));
844    Inkscape::NodePath::SubPath *sp = node->subpath;
845     Inkscape::NodePath::Path *np    = sp->nodepath;
847     if (sp->closed) {
848         sp_nodepath_subpath_open(sp, node);
849         return sp->first;
850     } else {
851         // no break for end nodes
852         if (node == sp->first) return NULL;
853         if (node == sp->last ) return NULL;
855         // create a new subpath
856        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
858         // duplicate the break node as start of the new subpath
859        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
861         while (node->n.other) { // copy the remaining nodes into the new subpath
862            Inkscape::NodePath::Node *n  = node->n.other;
863            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);
864             if (n->selected) {
865                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
866             }
867             sp_nodepath_node_destroy(n); // remove the point on the original subpath
868         }
870         return newnode;
871     }
874 /**
875  * Duplicate node and connect to neighbours.
876  */
877 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
879     g_assert(node);
880     g_assert(node->subpath);
881     g_assert(g_list_find(node->subpath->nodes, node));
883    Inkscape::NodePath::SubPath *sp = node->subpath;
885     NRPathcode code = (NRPathcode) node->code;
886     if (code == NR_MOVETO) { // if node is the endnode,
887         node->code = NR_LINETO; // new one is inserted before it, so change that to line
888     }
890     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
892     if (!node->n.other || !node->p.other) // if node is an endnode, select it
893         return node;
894     else
895         return newnode; // otherwise select the newly created node
898 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
900     node->p.pos = (node->pos + (node->pos - node->n.pos));
903 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
905     node->n.pos = (node->pos + (node->pos - node->p.pos));
908 /**
909  * Change line type at node, with side effects on neighbours.
910  */
911 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
913     g_assert(end);
914     g_assert(end->subpath);
915     g_assert(end->p.other);
917     if (end->code == static_cast< guint > ( code ) )
918         return;
920    Inkscape::NodePath::Node *start = end->p.other;
922     end->code = code;
924     if (code == NR_LINETO) {
925         if (start->code == NR_LINETO) {
926             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
927         }
928         if (end->n.other) {
929             if (end->n.other->code == NR_LINETO) {
930                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
931             }
932         }
933     } else {
934         NR::Point delta = end->pos - start->pos;
935         start->n.pos = start->pos + delta / 3;
936         end->p.pos = end->pos - delta / 3;
937         sp_node_adjust_handle(start, 1);
938         sp_node_adjust_handle(end, -1);
939     }
941     sp_node_update_handles(start);
942     sp_node_update_handles(end);
945 /**
946  * Change node type, and its handles accordingly.
947  */
948 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
950     g_assert(node);
951     g_assert(node->subpath);
953     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
954         return node;
956     if ((node->p.other != NULL) && (node->n.other != NULL)) {
957         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
958             type =Inkscape::NodePath::NODE_CUSP;
959         }
960     }
962     node->type = type;
964     if (node->type == Inkscape::NodePath::NODE_CUSP) {
965         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
966         node->knot->setSize (node->selected? 11 : 9);
967         sp_knot_update_ctrl(node->knot);
968     } else {
969         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
970         node->knot->setSize (node->selected? 9 : 7);
971         sp_knot_update_ctrl(node->knot);
972     }
974     // if one of handles is mouseovered, preserve its position
975     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
976         sp_node_adjust_handle(node, 1);
977     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
978         sp_node_adjust_handle(node, -1);
979     } else {
980         sp_node_adjust_handles(node);
981     }
983     sp_node_update_handles(node);
985     sp_nodepath_update_statusbar(node->subpath->nodepath);
987     return node;
990 /**
991  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
992  * adjacent segments from lines to curves.
993 */
994 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
996     bool p_line = (node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos);
997     bool n_line = (node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos);
999     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1000         if (p_line && n_line) {
1001             // only if both adjacent segments are lines, 
1002             // convert both to curves:
1004             // BEFORE:
1005             {
1006             node->code = NR_CURVETO;
1007             NR::Point delta = node->n.other->pos - node->p.other->pos;
1008             node->p.pos = node->pos - delta / 4;
1009             }
1011             // AFTER:
1012             {
1013             node->n.other->code = NR_CURVETO;
1014             NR::Point delta = node->p.other->pos - node->n.other->pos;
1015             node->n.pos = node->pos - delta / 4;
1016             }
1018             sp_node_update_handles(node);
1019         }
1020     }
1022     sp_nodepath_set_node_type (node, type);
1025 /**
1026  * Move node to point, and adjust its and neighbouring handles.
1027  */
1028 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1030     NR::Point delta = p - node->pos;
1031     node->pos = p;
1033     node->p.pos += delta;
1034     node->n.pos += delta;
1036     Inkscape::NodePath::Node *node_p = NULL;
1037     Inkscape::NodePath::Node *node_n = NULL;
1039     if (node->p.other) {
1040         if (node->code == NR_LINETO) {
1041             sp_node_adjust_handle(node, 1);
1042             sp_node_adjust_handle(node->p.other, -1);
1043             node_p = node->p.other;
1044         }
1045     }
1046     if (node->n.other) {
1047         if (node->n.other->code == NR_LINETO) {
1048             sp_node_adjust_handle(node, -1);
1049             sp_node_adjust_handle(node->n.other, 1);
1050             node_n = node->n.other;
1051         }
1052     }
1054     // this function is only called from batch movers that will update display at the end
1055     // themselves, so here we just move all the knots without emitting move signals, for speed
1056     sp_node_update_handles(node, false);
1057     if (node_n) {
1058         sp_node_update_handles(node_n, false);
1059     }
1060     if (node_p) {
1061         sp_node_update_handles(node_p, false);
1062     }
1065 /**
1066  * Call sp_node_moveto() for node selection and handle possible snapping.
1067  */
1068 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1069                                             bool const snap = true)
1071     NR::Coord best = NR_HUGE;
1072     NR::Point delta(dx, dy);
1073     NR::Point best_pt = delta;
1075     if (snap) {
1076         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
1077         
1078         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1079             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1080             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, SP_PATH(n->subpath->nodepath->object));
1081             if (s.getDistance() < best) {
1082                 best = s.getDistance();
1083                 best_pt = s.getPoint() - n->pos;
1084             }
1085         }
1086     }
1088     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1089         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1090         sp_node_moveto(n, n->pos + best_pt);
1091     }
1093     // do not update repr here so that node dragging is acceptably fast
1094     update_object(nodepath);
1097 /**
1098 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1099 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1100 near x = 0.
1101  */
1102 double
1103 sculpt_profile (double x, double alpha, guint profile)
1105     if (x >= 1)
1106         return 0;
1107     if (x <= 0)
1108         return 1;
1110     switch (profile) {
1111         case SCULPT_PROFILE_LINEAR:
1112         return 1 - x;
1113         case SCULPT_PROFILE_BELL:
1114         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1115         case SCULPT_PROFILE_ELLIPTIC:
1116         return sqrt(1 - x*x);
1117     }
1119     return 1;
1122 double
1123 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1125     // extremely primitive for now, don't have time to look for the real one
1126     double lower = NR::L2(b - a);
1127     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1128     return (lower + upper)/2;
1131 void
1132 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1134     n->pos = n->origin + delta;
1135     n->n.pos = n->n.origin + delta_n;
1136     n->p.pos = n->p.origin + delta_p;
1137     sp_node_adjust_handles(n);
1138     sp_node_update_handles(n, false);
1141 /**
1142  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1143  * on how far they are from the dragged node n.
1144  */
1145 static void 
1146 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1148     g_assert (n);
1149     g_assert (nodepath);
1150     g_assert (n->subpath->nodepath == nodepath);
1152     double pressure = n->knot->pressure;
1153     if (pressure == 0)
1154         pressure = 0.5; // default
1155     pressure = CLAMP (pressure, 0.2, 0.8);
1157     // map pressure to alpha = 1/5 ... 5
1158     double alpha = 1 - 2 * fabs(pressure - 0.5);
1159     if (pressure > 0.5)
1160         alpha = 1/alpha;
1162     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1164     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1165         // Only one subpath has selected nodes:
1166         // use linear mode, where the distance from n to node being dragged is calculated along the path
1168         double n_sel_range = 0, p_sel_range = 0;
1169         guint n_nodes = 0, p_nodes = 0;
1170         guint n_sel_nodes = 0, p_sel_nodes = 0;
1172         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1173         {
1174             double n_range = 0, p_range = 0;
1175             bool n_going = true, p_going = true;
1176             Inkscape::NodePath::Node *n_node = n;
1177             Inkscape::NodePath::Node *p_node = n;
1178             do {
1179                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1180                 if (n_node && n_going)
1181                     n_node = n_node->n.other;
1182                 if (n_node == NULL) {
1183                     n_going = false;
1184                 } else {
1185                     n_nodes ++;
1186                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1187                     if (n_node->selected) {
1188                         n_sel_nodes ++;
1189                         n_sel_range = n_range;
1190                     }
1191                     if (n_node == p_node) {
1192                         n_going = false;
1193                         p_going = false;
1194                     }
1195                 }
1196                 if (p_node && p_going)
1197                     p_node = p_node->p.other;
1198                 if (p_node == NULL) {
1199                     p_going = false;
1200                 } else {
1201                     p_nodes ++;
1202                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1203                     if (p_node->selected) {
1204                         p_sel_nodes ++;
1205                         p_sel_range = p_range;
1206                     }
1207                     if (p_node == n_node) {
1208                         n_going = false;
1209                         p_going = false;
1210                     }
1211                 }
1212             } while (n_going || p_going);
1213         }
1215         // Second pass: actually move nodes in this subpath
1216         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1217         {
1218             double n_range = 0, p_range = 0;
1219             bool n_going = true, p_going = true;
1220             Inkscape::NodePath::Node *n_node = n;
1221             Inkscape::NodePath::Node *p_node = n;
1222             do {
1223                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1224                 if (n_node && n_going)
1225                     n_node = n_node->n.other;
1226                 if (n_node == NULL) {
1227                     n_going = false;
1228                 } else {
1229                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1230                     if (n_node->selected) {
1231                         sp_nodepath_move_node_and_handles (n_node, 
1232                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1233                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1234                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1235                     }
1236                     if (n_node == p_node) {
1237                         n_going = false;
1238                         p_going = false;
1239                     }
1240                 }
1241                 if (p_node && p_going)
1242                     p_node = p_node->p.other;
1243                 if (p_node == NULL) {
1244                     p_going = false;
1245                 } else {
1246                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1247                     if (p_node->selected) {
1248                         sp_nodepath_move_node_and_handles (p_node, 
1249                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1250                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1251                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1252                     }
1253                     if (p_node == n_node) {
1254                         n_going = false;
1255                         p_going = false;
1256                     }
1257                 }
1258             } while (n_going || p_going);
1259         }
1261     } else {
1262         // Multiple subpaths have selected nodes:
1263         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1264         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1265         // fix the pear-like shape when sculpting e.g. a ring
1267         // First pass: calculate range
1268         gdouble direct_range = 0;
1269         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1270             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1271             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1272                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1273                 if (node->selected) {
1274                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1275                 }
1276             }
1277         }
1279         // Second pass: actually move nodes
1280         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1281             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1282             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1283                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1284                 if (node->selected) {
1285                     if (direct_range > 1e-6) {
1286                         sp_nodepath_move_node_and_handles (node,
1287                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1288                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1289                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1290                     } else {
1291                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1292                     }
1294                 }
1295             }
1296         }
1297     }
1299     // do not update repr here so that node dragging is acceptably fast
1300     update_object(nodepath);
1304 /**
1305  * Move node selection to point, adjust its and neighbouring handles,
1306  * handle possible snapping, and commit the change with possible undo.
1307  */
1308 void
1309 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1311     if (!nodepath) return;
1313     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1315     if (dx == 0) {
1316         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1317     } else if (dy == 0) {
1318         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1319     } else {
1320         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1321     }
1324 /**
1325  * Move node selection off screen and commit the change.
1326  */
1327 void
1328 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1330     // borrowed from sp_selection_move_screen in selection-chemistry.c
1331     // we find out the current zoom factor and divide deltas by it
1332     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1334     gdouble zoom = desktop->current_zoom();
1335     gdouble zdx = dx / zoom;
1336     gdouble zdy = dy / zoom;
1338     if (!nodepath) return;
1340     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1342     if (dx == 0) {
1343         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1344     } else if (dy == 0) {
1345         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1346     } else {
1347         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1348     }
1351 /** If they don't yet exist, creates knot and line for the given side of the node */
1352 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1354     if (!side->knot) {
1355         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"));
1357         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1358         side->knot->setSize (7);
1359         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1360         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1361         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1362         sp_knot_update_ctrl(side->knot);
1364         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1365         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1366         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1367         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1368         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1369         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1370     }
1372     if (!side->line) {
1373         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1374                                         SP_TYPE_CTRLLINE, NULL);
1375     }
1378 /**
1379  * Ensure the given handle of the node is visible/invisible, update its screen position
1380  */
1381 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1383     g_assert(node != NULL);
1385    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1386     NRPathcode code = sp_node_path_code_from_side(node, side);
1388     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1390     if (show_handle) {
1391         if (!side->knot) { // No handle knot at all
1392             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1393             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1394             side->knot->pos = side->pos;
1395             if (side->knot->item) 
1396                 SP_CTRL(side->knot->item)->moveto(side->pos);
1397             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1398             sp_knot_show(side->knot);
1399         } else {
1400             if (side->knot->pos != side->pos) { // only if it's really moved
1401                 if (fire_move_signals) {
1402                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1403                 } else {
1404                     sp_knot_moveto(side->knot, &side->pos);
1405                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1406                 }
1407             }
1408             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1409                 sp_knot_show(side->knot);
1410             }
1411         }
1412         sp_canvas_item_show(side->line);
1413     } else {
1414         if (side->knot) {
1415             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1416                 sp_knot_hide(side->knot);
1417             }
1418         }
1419         if (side->line) {
1420             sp_canvas_item_hide(side->line);
1421         }
1422     }
1425 /**
1426  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1427  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1428  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1429  * updated; otherwise, just move the knots silently (used in batch moves).
1430  */
1431 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1433     g_assert(node != NULL);
1435     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1436         sp_knot_show(node->knot);
1437     }
1439     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1440         if (fire_move_signals)
1441             sp_knot_set_position(node->knot, &node->pos, 0);
1442         else 
1443             sp_knot_moveto(node->knot, &node->pos);
1444     }
1446     gboolean show_handles = node->selected;
1447     if (node->p.other != NULL) {
1448         if (node->p.other->selected) show_handles = TRUE;
1449     }
1450     if (node->n.other != NULL) {
1451         if (node->n.other->selected) show_handles = TRUE;
1452     }
1454     if (node->subpath->nodepath->show_handles == false)
1455         show_handles = FALSE;
1457     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1458     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1461 /**
1462  * Call sp_node_update_handles() for all nodes on subpath.
1463  */
1464 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1466     g_assert(subpath != NULL);
1468     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1469         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1470     }
1473 /**
1474  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1475  */
1476 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1478     g_assert(nodepath != NULL);
1480     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1481         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1482     }
1485 void
1486 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1488     if (nodepath == NULL) return;
1490     nodepath->show_handles = show;
1491     sp_nodepath_update_handles(nodepath);
1494 /**
1495  * Adds all selected nodes in nodepath to list.
1496  */
1497 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1499     StlConv<Node *>::list(l, selected);
1500 /// \todo this adds a copying, rework when the selection becomes a stl list
1503 /**
1504  * Align selected nodes on the specified axis.
1505  */
1506 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1508     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1509         return;
1510     }
1512     if ( !nodepath->selected->next ) { // only one node selected
1513         return;
1514     }
1515    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1516     NR::Point dest(pNode->pos);
1517     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1518         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1519         if (pNode) {
1520             dest[axis] = pNode->pos[axis];
1521             sp_node_moveto(pNode, dest);
1522         }
1523     }
1525     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1528 /// Helper struct.
1529 struct NodeSort
1531    Inkscape::NodePath::Node *_node;
1532     NR::Coord _coord;
1533     /// \todo use vectorof pointers instead of calling copy ctor
1534     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1535         _node(node), _coord(node->pos[axis])
1536     {}
1538 };
1540 static bool operator<(NodeSort const &a, NodeSort const &b)
1542     return (a._coord < b._coord);
1545 /**
1546  * Distribute selected nodes on the specified axis.
1547  */
1548 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1550     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1551         return;
1552     }
1554     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1555         return;
1556     }
1558    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1559     std::vector<NodeSort> sorted;
1560     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1561         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1562         if (pNode) {
1563             NodeSort n(pNode, axis);
1564             sorted.push_back(n);
1565             //dest[axis] = pNode->pos[axis];
1566             //sp_node_moveto(pNode, dest);
1567         }
1568     }
1569     std::sort(sorted.begin(), sorted.end());
1570     unsigned int len = sorted.size();
1571     //overall bboxes span
1572     float dist = (sorted.back()._coord -
1573                   sorted.front()._coord);
1574     //new distance between each bbox
1575     float step = (dist) / (len - 1);
1576     float pos = sorted.front()._coord;
1577     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1578           it < sorted.end();
1579           it ++ )
1580     {
1581         NR::Point dest((*it)._node->pos);
1582         dest[axis] = pos;
1583         sp_node_moveto((*it)._node, dest);
1584         pos += step;
1585     }
1587     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1591 /**
1592  * Call sp_nodepath_line_add_node() for all selected segments.
1593  */
1594 void
1595 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1597     if (!nodepath) {
1598         return;
1599     }
1601     GList *nl = NULL;
1603     int n_added = 0;
1605     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1606        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1607         g_assert(t->selected);
1608         if (t->p.other && t->p.other->selected) {
1609             nl = g_list_prepend(nl, t);
1610         }
1611     }
1613     while (nl) {
1614        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1615        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1616        sp_nodepath_node_select(n, TRUE, FALSE);
1617        n_added ++;
1618        nl = g_list_remove(nl, t);
1619     }
1621     /** \todo fixme: adjust ? */
1622     sp_nodepath_update_handles(nodepath);
1624     if (n_added > 1) {
1625         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1626     } else if (n_added > 0) {
1627         sp_nodepath_update_repr(nodepath, _("Add node"));
1628     }
1630     sp_nodepath_update_statusbar(nodepath);
1633 /**
1634  * Select segment nearest to point
1635  */
1636 void
1637 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1639     if (!nodepath) {
1640         return;
1641     }
1643     sp_nodepath_ensure_livarot_path(nodepath);
1644     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1645     if (!maybe_position) {
1646         return;
1647     }
1648     Path::cut_position position = *maybe_position;
1650     //find segment to segment
1651     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1653     //fixme: this can return NULL, so check before proceeding.
1654     g_return_if_fail(e != NULL);
1655     
1656     gboolean force = FALSE;
1657     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1658         force = TRUE;
1659     }
1660     sp_nodepath_node_select(e, (gboolean) toggle, force);
1661     if (e->p.other)
1662         sp_nodepath_node_select(e->p.other, TRUE, force);
1664     sp_nodepath_update_handles(nodepath);
1666     sp_nodepath_update_statusbar(nodepath);
1669 /**
1670  * Add a node nearest to point
1671  */
1672 void
1673 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1675     if (!nodepath) {
1676         return;
1677     }
1679     sp_nodepath_ensure_livarot_path(nodepath);
1680     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1681     if (!maybe_position) {
1682         return;
1683     }
1684     Path::cut_position position = *maybe_position;
1686     //find segment to split
1687     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1689     //don't know why but t seems to flip for lines
1690     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1691         position.t = 1.0 - position.t;
1692     }
1693     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1694     sp_nodepath_node_select(n, FALSE, TRUE);
1696     /* fixme: adjust ? */
1697     sp_nodepath_update_handles(nodepath);
1699     sp_nodepath_update_repr(nodepath, _("Add node"));
1701     sp_nodepath_update_statusbar(nodepath);
1704 /*
1705  * Adjusts a segment so that t moves by a certain delta for dragging
1706  * converts lines to curves
1707  *
1708  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1709  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1710  */
1711 void
1712 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1714     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1716     //fixme: e and e->p can be NULL, so check for those before proceeding
1717     g_return_if_fail(e != NULL);
1718     g_return_if_fail(&e->p != NULL);
1719     
1720     /* feel good is an arbitrary parameter that distributes the delta between handles
1721      * if t of the drag point is less than 1/6 distance form the endpoint only
1722      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1723      */
1724     double feel_good;
1725     if (t <= 1.0 / 6.0)
1726         feel_good = 0;
1727     else if (t <= 0.5)
1728         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1729     else if (t <= 5.0 / 6.0)
1730         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1731     else
1732         feel_good = 1;
1734     //if we're dragging a line convert it to a curve
1735     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1736         sp_nodepath_set_line_type(e, NR_CURVETO);
1737     }
1739     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1740     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1741     e->p.other->n.pos += offsetcoord0;
1742     e->p.pos += offsetcoord1;
1744     // adjust handles of adjacent nodes where necessary
1745     sp_node_adjust_handle(e,1);
1746     sp_node_adjust_handle(e->p.other,-1);
1748     sp_nodepath_update_handles(e->subpath->nodepath);
1750     update_object(e->subpath->nodepath);
1752     sp_nodepath_update_statusbar(e->subpath->nodepath);
1756 /**
1757  * Call sp_nodepath_break() for all selected segments.
1758  */
1759 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1761     if (!nodepath) return;
1763     GList *temp = NULL;
1764     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1765        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1766        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1767         if (nn == NULL) continue; // no break, no new node
1768         temp = g_list_prepend(temp, nn);
1769     }
1771     if (temp) {
1772         sp_nodepath_deselect(nodepath);
1773     }
1774     for (GList *l = temp; l != NULL; l = l->next) {
1775         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1776     }
1778     sp_nodepath_update_handles(nodepath);
1780     sp_nodepath_update_repr(nodepath, _("Break path"));
1783 /**
1784  * Duplicate the selected node(s).
1785  */
1786 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1788     if (!nodepath) {
1789         return;
1790     }
1792     GList *temp = NULL;
1793     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1794        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1795        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1796         if (nn == NULL) continue; // could not duplicate
1797         temp = g_list_prepend(temp, nn);
1798     }
1800     if (temp) {
1801         sp_nodepath_deselect(nodepath);
1802     }
1803     for (GList *l = temp; l != NULL; l = l->next) {
1804         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1805     }
1807     sp_nodepath_update_handles(nodepath);
1809     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1812 /**
1813  *  Join two nodes by merging them into one.
1814  */
1815 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
1817     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1819     if (g_list_length(nodepath->selected) != 2) {
1820         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1821         return;
1822     }
1824    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1825    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1827     g_assert(a != b);
1828     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1829         // someone tried to join an orphan node (i.e. a single-node subpath).
1830         // this is not worth an error message, just fail silently.
1831         return;
1832     }
1834     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1835         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1836         return;
1837     }
1839     /* a and b are endpoints */
1841     NR::Point c;
1842     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1843         c = a->pos;
1844     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1845         c = b->pos;
1846     } else {
1847         c = (a->pos + b->pos) / 2;
1848     }
1850     if (a->subpath == b->subpath) {
1851        Inkscape::NodePath::SubPath *sp = a->subpath;
1852         sp_nodepath_subpath_close(sp);
1853         sp_node_moveto (sp->first, c);
1855         sp_nodepath_update_handles(sp->nodepath);
1856         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1857         return;
1858     }
1860     /* a and b are separate subpaths */
1861    Inkscape::NodePath::SubPath *sa = a->subpath;
1862    Inkscape::NodePath::SubPath *sb = b->subpath;
1863     NR::Point p;
1864    Inkscape::NodePath::Node *n;
1865     NRPathcode code;
1866     if (a == sa->first) {
1867         p = sa->first->n.pos;
1868         code = (NRPathcode)sa->first->n.other->code;
1869        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1870         n = sa->last;
1871         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1872         n = n->p.other;
1873         while (n) {
1874             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1875             n = n->p.other;
1876             if (n == sa->first) n = NULL;
1877         }
1878         sp_nodepath_subpath_destroy(sa);
1879         sa = t;
1880     } else if (a == sa->last) {
1881         p = sa->last->p.pos;
1882         code = (NRPathcode)sa->last->code;
1883         sp_nodepath_node_destroy(sa->last);
1884     } else {
1885         code = NR_END;
1886         g_assert_not_reached();
1887     }
1889     if (b == sb->first) {
1890         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1891         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1892             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1893         }
1894     } else if (b == sb->last) {
1895         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1896         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1897             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1898         }
1899     } else {
1900         g_assert_not_reached();
1901     }
1902     /* and now destroy sb */
1904     sp_nodepath_subpath_destroy(sb);
1906     sp_nodepath_update_handles(sa->nodepath);
1908     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1910     sp_nodepath_update_statusbar(nodepath);
1913 /**
1914  *  Join two nodes by adding a segment between them.
1915  */
1916 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
1918     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1920     if (g_list_length(nodepath->selected) != 2) {
1921         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1922         return;
1923     }
1925    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1926    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1928     g_assert(a != b);
1929     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1930         // someone tried to join an orphan node (i.e. a single-node subpath).
1931         // this is not worth an error message, just fail silently.
1932         return;
1933     }
1935     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1936         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1937         return;
1938     }
1940     if (a->subpath == b->subpath) {
1941        Inkscape::NodePath::SubPath *sp = a->subpath;
1943         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1944         sp->closed = TRUE;
1946         sp->first->p.other = sp->last;
1947         sp->last->n.other  = sp->first;
1949         sp_node_handle_mirror_p_to_n(sp->last);
1950         sp_node_handle_mirror_n_to_p(sp->first);
1952         sp->first->code = sp->last->code;
1953         sp->first       = sp->last;
1955         sp_nodepath_update_handles(sp->nodepath);
1957         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1959         return;
1960     }
1962     /* a and b are separate subpaths */
1963    Inkscape::NodePath::SubPath *sa = a->subpath;
1964    Inkscape::NodePath::SubPath *sb = b->subpath;
1966    Inkscape::NodePath::Node *n;
1967     NR::Point p;
1968     NRPathcode code;
1969     if (a == sa->first) {
1970         code = (NRPathcode) sa->first->n.other->code;
1971        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1972         n = sa->last;
1973         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1974         for (n = n->p.other; n != NULL; n = n->p.other) {
1975             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1976         }
1977         sp_nodepath_subpath_destroy(sa);
1978         sa = t;
1979     } else if (a == sa->last) {
1980         code = (NRPathcode)sa->last->code;
1981     } else {
1982         code = NR_END;
1983         g_assert_not_reached();
1984     }
1986     if (b == sb->first) {
1987         n = sb->first;
1988         sp_node_handle_mirror_p_to_n(sa->last);
1989         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1990         sp_node_handle_mirror_n_to_p(sa->last);
1991         for (n = n->n.other; n != NULL; n = n->n.other) {
1992             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1993         }
1994     } else if (b == sb->last) {
1995         n = sb->last;
1996         sp_node_handle_mirror_p_to_n(sa->last);
1997         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1998         sp_node_handle_mirror_n_to_p(sa->last);
1999         for (n = n->p.other; n != NULL; n = n->p.other) {
2000             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2001         }
2002     } else {
2003         g_assert_not_reached();
2004     }
2005     /* and now destroy sb */
2007     sp_nodepath_subpath_destroy(sb);
2009     sp_nodepath_update_handles(sa->nodepath);
2011     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2014 /**
2015  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2016  */
2017 void sp_node_delete_preserve(GList *nodes_to_delete)
2019     GSList *nodepaths = NULL;
2020     
2021     while (nodes_to_delete) {
2022         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2023         Inkscape::NodePath::SubPath *sp = node->subpath;
2024         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2025         Inkscape::NodePath::Node *sample_cursor = NULL;
2026         Inkscape::NodePath::Node *sample_end = NULL;
2027         Inkscape::NodePath::Node *delete_cursor = node;
2028         bool just_delete = false;
2029         
2030         //find the start of this contiguous selection
2031         //move left to the first node that is not selected
2032         //or the start of the non-closed path
2033         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2034             delete_cursor = curr;
2035         }
2037         //just delete at the beginning of an open path
2038         if (!delete_cursor->p.other) {
2039             sample_cursor = delete_cursor;
2040             just_delete = true;
2041         } else {
2042             sample_cursor = delete_cursor->p.other;
2043         }
2044         
2045         //calculate points for each segment
2046         int rate = 5;
2047         float period = 1.0 / rate;
2048         std::vector<NR::Point> data;
2049         if (!just_delete) {
2050             data.push_back(sample_cursor->pos);
2051             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2052                 //just delete at the end of an open path
2053                 if (!sp->closed && curr == sp->last) {
2054                     just_delete = true;
2055                     break;
2056                 }
2057                 
2058                 //sample points on the contiguous selected segment
2059                 NR::Point *bez;
2060                 bez = new NR::Point [4];
2061                 bez[0] = curr->pos;
2062                 bez[1] = curr->n.pos;
2063                 bez[2] = curr->n.other->p.pos;
2064                 bez[3] = curr->n.other->pos;
2065                 for (int i=1; i<rate; i++) {
2066                     gdouble t = i * period;
2067                     NR::Point p = bezier_pt(3, bez, t);
2068                     data.push_back(p);
2069                 }
2070                 data.push_back(curr->n.other->pos);
2072                 sample_end = curr->n.other;
2073                 //break if we've come full circle or hit the end of the selection
2074                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2075                     break;
2076                 }
2077             }
2078         }
2080         if (!just_delete) {
2081             //calculate the best fitting single segment and adjust the endpoints
2082             NR::Point *adata;
2083             adata = new NR::Point [data.size()];
2084             copy(data.begin(), data.end(), adata);
2085             
2086             NR::Point *bez;
2087             bez = new NR::Point [4];
2088             //would decreasing error create a better fitting approximation?
2089             gdouble error = 1.0;
2090             gint ret;
2091             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2093             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2094             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2095             //the resulting nodes behave as expected.
2096             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2097             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2098             
2099             //adjust endpoints
2100             sample_cursor->n.pos = bez[1];
2101             sample_end->p.pos = bez[2];
2102         }
2103        
2104         //destroy this contiguous selection
2105         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2106             Inkscape::NodePath::Node *temp = delete_cursor;
2107             if (delete_cursor->n.other == delete_cursor) {
2108                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2109                 delete_cursor = NULL; 
2110             } else {
2111                 delete_cursor = delete_cursor->n.other;
2112             }
2113             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2114             sp_nodepath_node_destroy(temp);
2115         }
2117         sp_nodepath_update_handles(nodepath);
2119         if (!g_slist_find(nodepaths, nodepath))
2120             nodepaths = g_slist_prepend (nodepaths, nodepath);
2121     }
2123     for (GSList *i = nodepaths; i; i = i->next) {
2124         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2125         // different nodepaths will give us one undo event per nodepath
2126         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2128         // if the entire nodepath is removed, delete the selected object.
2129         if (nodepath->subpaths == NULL ||
2130             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2131             //at least 2
2132             sp_nodepath_get_node_count(nodepath) < 2) {
2133             SPDocument *document = sp_desktop_document (nodepath->desktop);
2134             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2135             //delete this nodepath's object, not the entire selection! (though at this time, this
2136             //does not matter)
2137             sp_selection_delete();
2138             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2139                               _("Delete nodes"));
2140         } else {
2141             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2142             sp_nodepath_update_statusbar(nodepath);
2143         }
2144     }
2146     g_slist_free (nodepaths);
2149 /**
2150  * Delete one or more selected nodes.
2151  */
2152 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2154     if (!nodepath) return;
2155     if (!nodepath->selected) return;
2157     /** \todo fixme: do it the right way */
2158     while (nodepath->selected) {
2159        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2160         sp_nodepath_node_destroy(node);
2161     }
2164     //clean up the nodepath (such as for trivial subpaths)
2165     sp_nodepath_cleanup(nodepath);
2167     sp_nodepath_update_handles(nodepath);
2169     // if the entire nodepath is removed, delete the selected object.
2170     if (nodepath->subpaths == NULL ||
2171         sp_nodepath_get_node_count(nodepath) < 2) {
2172         SPDocument *document = sp_desktop_document (nodepath->desktop);
2173         sp_selection_delete();
2174         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2175                           _("Delete nodes"));
2176         return;
2177     }
2179     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2181     sp_nodepath_update_statusbar(nodepath);
2184 /**
2185  * Delete one or more segments between two selected nodes.
2186  * This is the code for 'split'.
2187  */
2188 void
2189 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2191    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2192    Inkscape::NodePath::Node *curr, *next;     //Iterators
2194     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2196     if (g_list_length(nodepath->selected) != 2) {
2197         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2198                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2199         return;
2200     }
2202     //Selected nodes, not inclusive
2203    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2204    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2206     if ( ( a==b)                       ||  //same node
2207          (a->subpath  != b->subpath )  ||  //not the same path
2208          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2209          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2210     {
2211         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2212                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2213         return;
2214     }
2216     //###########################################
2217     //# BEGIN EDITS
2218     //###########################################
2219     //##################################
2220     //# CLOSED PATH
2221     //##################################
2222     if (a->subpath->closed) {
2225         gboolean reversed = FALSE;
2227         //Since we can go in a circle, we need to find the shorter distance.
2228         //  a->b or b->a
2229         start = end = NULL;
2230         int distance    = 0;
2231         int minDistance = 0;
2232         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2233             if (curr==b) {
2234                 //printf("a to b:%d\n", distance);
2235                 start = a;//go from a to b
2236                 end   = b;
2237                 minDistance = distance;
2238                 //printf("A to B :\n");
2239                 break;
2240             }
2241             distance++;
2242         }
2244         //try again, the other direction
2245         distance = 0;
2246         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2247             if (curr==a) {
2248                 //printf("b to a:%d\n", distance);
2249                 if (distance < minDistance) {
2250                     start    = b;  //we go from b to a
2251                     end      = a;
2252                     reversed = TRUE;
2253                     //printf("B to A\n");
2254                 }
2255                 break;
2256             }
2257             distance++;
2258         }
2261         //Copy everything from 'end' to 'start' to a new subpath
2262        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2263         for (curr=end ; curr ; curr=curr->n.other) {
2264             NRPathcode code = (NRPathcode) curr->code;
2265             if (curr == end)
2266                 code = NR_MOVETO;
2267             sp_nodepath_node_new(t, NULL,
2268                                  (Inkscape::NodePath::NodeType)curr->type, code,
2269                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2270             if (curr == start)
2271                 break;
2272         }
2273         sp_nodepath_subpath_destroy(a->subpath);
2276     }
2280     //##################################
2281     //# OPEN PATH
2282     //##################################
2283     else {
2285         //We need to get the direction of the list between A and B
2286         //Can we walk from a to b?
2287         start = end = NULL;
2288         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2289             if (curr==b) {
2290                 start = a;  //did it!  we go from a to b
2291                 end   = b;
2292                 //printf("A to B\n");
2293                 break;
2294             }
2295         }
2296         if (!start) {//didn't work?  let's try the other direction
2297             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2298                 if (curr==a) {
2299                     start = b;  //did it!  we go from b to a
2300                     end   = a;
2301                     //printf("B to A\n");
2302                     break;
2303                 }
2304             }
2305         }
2306         if (!start) {
2307             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2308                                                      _("Cannot find path between nodes."));
2309             return;
2310         }
2314         //Copy everything after 'end' to a new subpath
2315        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2316         for (curr=end ; curr ; curr=curr->n.other) {
2317             NRPathcode code = (NRPathcode) curr->code;
2318             if (curr == end)
2319                 code = NR_MOVETO;
2320             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2321                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2322         }
2324         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2325         for (curr = start->n.other ; curr  ; curr=next) {
2326             next = curr->n.other;
2327             sp_nodepath_node_destroy(curr);
2328         }
2330     }
2331     //###########################################
2332     //# END EDITS
2333     //###########################################
2335     //clean up the nodepath (such as for trivial subpaths)
2336     sp_nodepath_cleanup(nodepath);
2338     sp_nodepath_update_handles(nodepath);
2340     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2342     sp_nodepath_update_statusbar(nodepath);
2345 /**
2346  * Call sp_nodepath_set_line() for all selected segments.
2347  */
2348 void
2349 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2351     if (nodepath == NULL) return;
2353     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2354        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2355         g_assert(n->selected);
2356         if (n->p.other && n->p.other->selected) {
2357             sp_nodepath_set_line_type(n, code);
2358         }
2359     }
2361     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2364 /**
2365  * Call sp_nodepath_convert_node_type() for all selected nodes.
2366  */
2367 void
2368 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2370     if (nodepath == NULL) return;
2372     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2373         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2374     }
2376     sp_nodepath_update_repr(nodepath, _("Change node type"));
2379 /**
2380  * Change select status of node, update its own and neighbour handles.
2381  */
2382 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2384     node->selected = selected;
2386     if (selected) {
2387         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2388         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2389         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2390         sp_knot_update_ctrl(node->knot);
2391     } else {
2392         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2393         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2394         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2395         sp_knot_update_ctrl(node->knot);
2396     }
2398     sp_node_update_handles(node);
2399     if (node->n.other) sp_node_update_handles(node->n.other);
2400     if (node->p.other) sp_node_update_handles(node->p.other);
2403 /**
2404 \brief Select a node
2405 \param node     The node to select
2406 \param incremental   If true, add to selection, otherwise deselect others
2407 \param override   If true, always select this node, otherwise toggle selected status
2408 */
2409 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2411     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2413     if (incremental) {
2414         if (override) {
2415             if (!g_list_find(nodepath->selected, node)) {
2416                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2417             }
2418             sp_node_set_selected(node, TRUE);
2419         } else { // toggle
2420             if (node->selected) {
2421                 g_assert(g_list_find(nodepath->selected, node));
2422                 nodepath->selected = g_list_remove(nodepath->selected, node);
2423             } else {
2424                 g_assert(!g_list_find(nodepath->selected, node));
2425                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2426             }
2427             sp_node_set_selected(node, !node->selected);
2428         }
2429     } else {
2430         sp_nodepath_deselect(nodepath);
2431         nodepath->selected = g_list_prepend(nodepath->selected, node);
2432         sp_node_set_selected(node, TRUE);
2433     }
2435     sp_nodepath_update_statusbar(nodepath);
2439 /**
2440 \brief Deselect all nodes in the nodepath
2441 */
2442 void
2443 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2445     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2447     while (nodepath->selected) {
2448         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2449         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2450     }
2451     sp_nodepath_update_statusbar(nodepath);
2454 /**
2455 \brief Select or invert selection of all nodes in the nodepath
2456 */
2457 void
2458 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2460     if (!nodepath) return;
2462     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2463        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2464         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2465            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2466            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2467         }
2468     }
2471 /**
2472  * If nothing selected, does the same as sp_nodepath_select_all();
2473  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2474  * (i.e., similar to "select all in layer", with the "selected" subpaths
2475  * being treated as "layers" in the path).
2476  */
2477 void
2478 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2480     if (!nodepath) return;
2482     if (g_list_length (nodepath->selected) == 0) {
2483         sp_nodepath_select_all (nodepath, invert);
2484         return;
2485     }
2487     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2488     GSList *subpaths = NULL;
2490     for (GList *l = copy; l != NULL; l = l->next) {
2491         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2492         Inkscape::NodePath::SubPath *subpath = n->subpath;
2493         if (!g_slist_find (subpaths, subpath))
2494             subpaths = g_slist_prepend (subpaths, subpath);
2495     }
2497     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2498         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2499         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2500             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2501             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2502         }
2503     }
2505     g_slist_free (subpaths);
2506     g_list_free (copy);
2509 /**
2510  * \brief Select the node after the last selected; if none is selected,
2511  * select the first within path.
2512  */
2513 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2515     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2517    Inkscape::NodePath::Node *last = NULL;
2518     if (nodepath->selected) {
2519         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2520            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2521             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2522             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2523                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2524                 if (node->selected) {
2525                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2526                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2527                             if (spl->next) { // there's a next subpath
2528                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2529                                 last = subpath_next->first;
2530                             } else if (spl->prev) { // there's a previous subpath
2531                                 last = NULL; // to be set later to the first node of first subpath
2532                             } else {
2533                                 last = node->n.other;
2534                             }
2535                         } else {
2536                             last = node->n.other;
2537                         }
2538                     } else {
2539                         if (node->n.other) {
2540                             last = node->n.other;
2541                         } else {
2542                             if (spl->next) { // there's a next subpath
2543                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2544                                 last = subpath_next->first;
2545                             } else if (spl->prev) { // there's a previous subpath
2546                                 last = NULL; // to be set later to the first node of first subpath
2547                             } else {
2548                                 last = (Inkscape::NodePath::Node *) subpath->first;
2549                             }
2550                         }
2551                     }
2552                 }
2553             }
2554         }
2555         sp_nodepath_deselect(nodepath);
2556     }
2558     if (last) { // there's at least one more node after selected
2559         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2560     } else { // no more nodes, select the first one in first subpath
2561        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2562         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2563     }
2566 /**
2567  * \brief Select the node before the first selected; if none is selected,
2568  * select the last within path
2569  */
2570 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2572     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2574    Inkscape::NodePath::Node *last = NULL;
2575     if (nodepath->selected) {
2576         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2577            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2578             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2579                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2580                 if (node->selected) {
2581                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2582                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2583                             if (spl->prev) { // there's a prev subpath
2584                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2585                                 last = subpath_prev->last;
2586                             } else if (spl->next) { // there's a next subpath
2587                                 last = NULL; // to be set later to the last node of last subpath
2588                             } else {
2589                                 last = node->p.other;
2590                             }
2591                         } else {
2592                             last = node->p.other;
2593                         }
2594                     } else {
2595                         if (node->p.other) {
2596                             last = node->p.other;
2597                         } else {
2598                             if (spl->prev) { // there's a prev subpath
2599                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2600                                 last = subpath_prev->last;
2601                             } else if (spl->next) { // there's a next subpath
2602                                 last = NULL; // to be set later to the last node of last subpath
2603                             } else {
2604                                 last = (Inkscape::NodePath::Node *) subpath->last;
2605                             }
2606                         }
2607                     }
2608                 }
2609             }
2610         }
2611         sp_nodepath_deselect(nodepath);
2612     }
2614     if (last) { // there's at least one more node before selected
2615         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2616     } else { // no more nodes, select the last one in last subpath
2617         GList *spl = g_list_last(nodepath->subpaths);
2618        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2619         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2620     }
2623 /**
2624  * \brief Select all nodes that are within the rectangle.
2625  */
2626 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2628     if (!incremental) {
2629         sp_nodepath_deselect(nodepath);
2630     }
2632     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2633        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2634         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2635            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2637             if (b.contains(node->pos)) {
2638                 sp_nodepath_node_select(node, TRUE, TRUE);
2639             }
2640         }
2641     }
2645 void
2646 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2648     g_assert (n);
2649     g_assert (nodepath);
2650     g_assert (n->subpath->nodepath == nodepath);
2652     if (g_list_length (nodepath->selected) == 0) {
2653         if (grow > 0) {
2654             sp_nodepath_node_select(n, TRUE, TRUE);
2655         }
2656         return;
2657     }
2659     if (g_list_length (nodepath->selected) == 1) {
2660         if (grow < 0) {
2661             sp_nodepath_deselect (nodepath);
2662             return;
2663         }
2664     }
2666         double n_sel_range = 0, p_sel_range = 0;
2667             Inkscape::NodePath::Node *farthest_n_node = n;
2668             Inkscape::NodePath::Node *farthest_p_node = n;
2670         // Calculate ranges
2671         {
2672             double n_range = 0, p_range = 0;
2673             bool n_going = true, p_going = true;
2674             Inkscape::NodePath::Node *n_node = n;
2675             Inkscape::NodePath::Node *p_node = n;
2676             do {
2677                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2678                 if (n_node && n_going)
2679                     n_node = n_node->n.other;
2680                 if (n_node == NULL) {
2681                     n_going = false;
2682                 } else {
2683                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2684                     if (n_node->selected) {
2685                         n_sel_range = n_range;
2686                         farthest_n_node = n_node;
2687                     }
2688                     if (n_node == p_node) {
2689                         n_going = false;
2690                         p_going = false;
2691                     }
2692                 }
2693                 if (p_node && p_going)
2694                     p_node = p_node->p.other;
2695                 if (p_node == NULL) {
2696                     p_going = false;
2697                 } else {
2698                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2699                     if (p_node->selected) {
2700                         p_sel_range = p_range;
2701                         farthest_p_node = p_node;
2702                     }
2703                     if (p_node == n_node) {
2704                         n_going = false;
2705                         p_going = false;
2706                     }
2707                 }
2708             } while (n_going || p_going);
2709         }
2711     if (grow > 0) {
2712         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2713                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2714         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2715                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2716         }
2717     } else {
2718         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2719                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2720         } else if (farthest_p_node && farthest_p_node->selected) {
2721                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2722         }
2723     }
2726 void
2727 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2729     g_assert (n);
2730     g_assert (nodepath);
2731     g_assert (n->subpath->nodepath == nodepath);
2733     if (g_list_length (nodepath->selected) == 0) {
2734         if (grow > 0) {
2735             sp_nodepath_node_select(n, TRUE, TRUE);
2736         }
2737         return;
2738     }
2740     if (g_list_length (nodepath->selected) == 1) {
2741         if (grow < 0) {
2742             sp_nodepath_deselect (nodepath);
2743             return;
2744         }
2745     }
2747     Inkscape::NodePath::Node *farthest_selected = NULL;
2748     double farthest_dist = 0;
2750     Inkscape::NodePath::Node *closest_unselected = NULL;
2751     double closest_dist = NR_HUGE;
2753     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2754        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2755         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2756            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2757            if (node == n)
2758                continue;
2759            if (node->selected) {
2760                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2761                    farthest_dist = NR::L2(node->pos - n->pos);
2762                    farthest_selected = node;
2763                }
2764            } else {
2765                if (NR::L2(node->pos - n->pos) < closest_dist) {
2766                    closest_dist = NR::L2(node->pos - n->pos);
2767                    closest_unselected = node;
2768                }
2769            }
2770         }
2771     }
2773     if (grow > 0) {
2774         if (closest_unselected) {
2775             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2776         }
2777     } else {
2778         if (farthest_selected) {
2779             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2780         }
2781     }
2785 /**
2786 \brief  Saves all nodes' and handles' current positions in their origin members
2787 */
2788 void
2789 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2791     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2792        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2793         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2794            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2795            n->origin = n->pos;
2796            n->p.origin = n->p.pos;
2797            n->n.origin = n->n.pos;
2798         }
2799     }
2800
2802 /**
2803 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2804 */
2805 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2807     if (!nodepath->selected) {
2808         return NULL;
2809     }
2811     GList *r = NULL;
2812     guint i = 0;
2813     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2814        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2815         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2816            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2817             i++;
2818             if (node->selected) {
2819                 r = g_list_append(r, GINT_TO_POINTER(i));
2820             }
2821         }
2822     }
2823     return r;
2826 /**
2827 \brief  Restores selection by selecting nodes whose positions are in the list
2828 */
2829 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2831     sp_nodepath_deselect(nodepath);
2833     guint i = 0;
2834     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2835        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2836         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2837            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2838             i++;
2839             if (g_list_find(r, GINT_TO_POINTER(i))) {
2840                 sp_nodepath_node_select(node, TRUE, TRUE);
2841             }
2842         }
2843     }
2847 /**
2848 \brief Adjusts handle according to node type and line code.
2849 */
2850 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2852     double len, otherlen, linelen;
2854     g_assert(node);
2856    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2857    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2859     /** \todo fixme: */
2860     if (me->other == NULL) return;
2861     if (other->other == NULL) return;
2863     /* I have line */
2865     NRPathcode mecode, ocode;
2866     if (which_adjust == 1) {
2867         mecode = (NRPathcode)me->other->code;
2868         ocode = (NRPathcode)node->code;
2869     } else {
2870         mecode = (NRPathcode)node->code;
2871         ocode = (NRPathcode)other->other->code;
2872     }
2874     if (mecode == NR_LINETO) return;
2876     /* I am curve */
2878     if (other->other == NULL) return;
2880     /* Other has line */
2882     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2884     NR::Point delta;
2885     if (ocode == NR_LINETO) {
2886         /* other is lineto, we are either smooth or symm */
2887        Inkscape::NodePath::Node *othernode = other->other;
2888         len = NR::L2(me->pos - node->pos);
2889         delta = node->pos - othernode->pos;
2890         linelen = NR::L2(delta);
2891         if (linelen < 1e-18) 
2892             return;
2893         me->pos = node->pos + (len / linelen)*delta;
2894         return;
2895     }
2897     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2899         me->pos = 2 * node->pos - other->pos;
2900         return;
2901     }
2903     /* We are smooth */
2905     len = NR::L2(me->pos - node->pos);
2906     delta = other->pos - node->pos;
2907     otherlen = NR::L2(delta);
2908     if (otherlen < 1e-18) return;
2910     me->pos = node->pos - (len / otherlen) * delta;
2913 /**
2914  \brief Adjusts both handles according to node type and line code
2915  */
2916 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2918     g_assert(node);
2920     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2922     /* we are either smooth or symm */
2924     if (node->p.other == NULL) return;
2926     if (node->n.other == NULL) return;
2928     if (node->code == NR_LINETO) {
2929         if (node->n.other->code == NR_LINETO) return;
2930         sp_node_adjust_handle(node, 1);
2931         return;
2932     }
2934     if (node->n.other->code == NR_LINETO) {
2935         if (node->code == NR_LINETO) return;
2936         sp_node_adjust_handle(node, -1);
2937         return;
2938     }
2940     /* both are curves */
2941     NR::Point const delta( node->n.pos - node->p.pos );
2943     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2944         node->p.pos = node->pos - delta / 2;
2945         node->n.pos = node->pos + delta / 2;
2946         return;
2947     }
2949     /* We are smooth */
2950     double plen = NR::L2(node->p.pos - node->pos);
2951     if (plen < 1e-18) return;
2952     double nlen = NR::L2(node->n.pos - node->pos);
2953     if (nlen < 1e-18) return;
2954     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2955     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2958 /**
2959  * Node event callback.
2960  */
2961 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2963     gboolean ret = FALSE;
2964     switch (event->type) {
2965         case GDK_ENTER_NOTIFY:
2966             Inkscape::NodePath::Path::active_node = n;
2967             break;
2968         case GDK_LEAVE_NOTIFY:
2969             Inkscape::NodePath::Path::active_node = NULL;
2970             break;
2971         case GDK_SCROLL:
2972             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
2973                 switch (event->scroll.direction) {
2974                     case GDK_SCROLL_UP:
2975                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2976                         break;
2977                     case GDK_SCROLL_DOWN:
2978                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2979                         break;
2980                     default:
2981                         break;
2982                 }
2983                 ret = TRUE;
2984             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
2985                 switch (event->scroll.direction) {
2986                     case GDK_SCROLL_UP:
2987                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2988                         break;
2989                     case GDK_SCROLL_DOWN:
2990                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2991                         break;
2992                     default:
2993                         break;
2994                 }
2995                 ret = TRUE;
2996             }
2997             break;
2998         case GDK_KEY_PRESS:
2999             switch (get_group0_keyval (&event->key)) {
3000                 case GDK_space:
3001                     if (event->key.state & GDK_BUTTON1_MASK) {
3002                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3003                         stamp_repr(nodepath);
3004                         ret = TRUE;
3005                     }
3006                     break;
3007                 case GDK_Page_Up:
3008                     if (event->key.state & GDK_CONTROL_MASK) {
3009                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3010                     } else {
3011                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3012                     }
3013                     break;
3014                 case GDK_Page_Down:
3015                     if (event->key.state & GDK_CONTROL_MASK) {
3016                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3017                     } else {
3018                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3019                     }
3020                     break;
3021                 default:
3022                     break;
3023             }
3024             break;
3025         default:
3026             break;
3027     }
3029     return ret;
3032 /**
3033  * Handle keypress on node; directly called.
3034  */
3035 gboolean node_key(GdkEvent *event)
3037     Inkscape::NodePath::Path *np;
3039     // there is no way to verify nodes so set active_node to nil when deleting!!
3040     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3042     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3043         gint ret = FALSE;
3044         switch (get_group0_keyval (&event->key)) {
3045             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3046             case GDK_BackSpace:
3047                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3048                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3049                 sp_nodepath_update_repr(np, _("Delete node"));
3050                 Inkscape::NodePath::Path::active_node = NULL;
3051                 ret = TRUE;
3052                 break;
3053             case GDK_c:
3054                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3055                 ret = TRUE;
3056                 break;
3057             case GDK_s:
3058                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3059                 ret = TRUE;
3060                 break;
3061             case GDK_y:
3062                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3063                 ret = TRUE;
3064                 break;
3065             case GDK_b:
3066                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3067                 ret = TRUE;
3068                 break;
3069         }
3070         return ret;
3071     }
3072     return FALSE;
3075 /**
3076  * Mouseclick on node callback.
3077  */
3078 static void node_clicked(SPKnot *knot, guint state, gpointer data)
3080    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3082     if (state & GDK_CONTROL_MASK) {
3083         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3085         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3086             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3087                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3088             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3089                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3090             } else {
3091                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3092             }
3093             sp_nodepath_update_repr(nodepath, _("Change node type"));
3094             sp_nodepath_update_statusbar(nodepath);
3096         } else { //ctrl+alt+click: delete node
3097             GList *node_to_delete = NULL;
3098             node_to_delete = g_list_append(node_to_delete, n);
3099             sp_node_delete_preserve(node_to_delete);
3100         }
3102     } else {
3103         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3104     }
3107 /**
3108  * Mouse grabbed node callback.
3109  */
3110 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3112    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3114     if (!n->selected) {
3115         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3116     }
3118     n->is_dragging = true;
3119     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3121     sp_nodepath_remember_origins (n->subpath->nodepath);
3124 /**
3125  * Mouse ungrabbed node callback.
3126  */
3127 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
3129    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3131    n->dragging_out = NULL;
3132    n->is_dragging = false;
3133    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3135    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3138 /**
3139  * The point on a line, given by its angle, closest to the given point.
3140  * \param p  A point.
3141  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3142  * \param closest  Pointer to the point struct where the result is stored.
3143  * \todo FIXME: use dot product perhaps?
3144  */
3145 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3147     if (a == HUGE_VAL) { // vertical
3148         *closest = NR::Point(0, (*p)[NR::Y]);
3149     } else {
3150         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3151         (*closest)[NR::Y] = a * (*closest)[NR::X];
3152     }
3155 /**
3156  * Distance from the point to a line given by its angle.
3157  * \param p  A point.
3158  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3159  */
3160 static double point_line_distance(NR::Point *p, double a)
3162     NR::Point c;
3163     point_line_closest(p, a, &c);
3164     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]));
3167 /**
3168  * Callback for node "request" signal.
3169  * \todo fixme: This goes to "moved" event? (lauris)
3170  */
3171 static gboolean
3172 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3174     double yn, xn, yp, xp;
3175     double an, ap, na, pa;
3176     double d_an, d_ap, d_na, d_pa;
3177     gboolean collinear = FALSE;
3178     NR::Point c;
3179     NR::Point pr;
3181    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3183    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3184    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3186        NR::Point mouse = (*p);
3188        if (!n->dragging_out) {
3189            // This is the first drag-out event; find out which handle to drag out
3190            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3191            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3193            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3194                return FALSE;
3196            Inkscape::NodePath::NodeSide *opposite;
3197            if (appr_p > appr_n) { // closer to p
3198                n->dragging_out = &n->p;
3199                opposite = &n->n;
3200                n->code = NR_CURVETO;
3201            } else if (appr_p < appr_n) { // closer to n
3202                n->dragging_out = &n->n;
3203                opposite = &n->p;
3204                n->n.other->code = NR_CURVETO;
3205            } else { // p and n nodes are the same
3206                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3207                    n->dragging_out = &n->p;
3208                    opposite = &n->n;
3209                    n->code = NR_CURVETO;
3210                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3211                    n->dragging_out = &n->n;
3212                    opposite = &n->p;
3213                    n->n.other->code = NR_CURVETO;
3214                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3215                    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);
3216                    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);
3217                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3218                        n->dragging_out = &n->n;
3219                        opposite = &n->p;
3220                        n->n.other->code = NR_CURVETO;
3221                    } else { // closer to other's n handle
3222                        n->dragging_out = &n->p;
3223                        opposite = &n->n;
3224                        n->code = NR_CURVETO;
3225                    }
3226                }
3227            }
3229            // if there's another handle, make sure the one we drag out starts parallel to it
3230            if (opposite->pos != n->pos) {
3231                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3232            }
3234            // knots might not be created yet!
3235            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3236            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3237        }
3239        // pass this on to the handle-moved callback
3240        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3241        sp_node_update_handles(n);
3242        return TRUE;
3243    }
3245     if (state & GDK_CONTROL_MASK) { // constrained motion
3247         // calculate relative distances of handles
3248         // n handle:
3249         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3250         xn = n->n.pos[NR::X] - n->pos[NR::X];
3251         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3252         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3253             if (n->n.other) { // if there is the next point
3254                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3255                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3256                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3257             }
3258         }
3259         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3260         if (yn < 0) { xn = -xn; yn = -yn; }
3262         // p handle:
3263         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3264         xp = n->p.pos[NR::X] - n->pos[NR::X];
3265         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3266         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3267             if (n->p.other) {
3268                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3269                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3270                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3271             }
3272         }
3273         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3274         if (yp < 0) { xp = -xp; yp = -yp; }
3276         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3277             // sliding on handles, only if at least one of the handles is non-vertical
3278             // (otherwise it's the same as ctrl+drag anyway)
3280             // calculate angles of the handles
3281             if (xn == 0) {
3282                 if (yn == 0) { // no handle, consider it the continuation of the other one
3283                     an = 0;
3284                     collinear = TRUE;
3285                 }
3286                 else an = 0; // vertical; set the angle to horizontal
3287             } else an = yn/xn;
3289             if (xp == 0) {
3290                 if (yp == 0) { // no handle, consider it the continuation of the other one
3291                     ap = an;
3292                 }
3293                 else ap = 0; // vertical; set the angle to horizontal
3294             } else  ap = yp/xp;
3296             if (collinear) an = ap;
3298             // angles of the perpendiculars; HUGE_VAL means vertical
3299             if (an == 0) na = HUGE_VAL; else na = -1/an;
3300             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3302             // mouse point relative to the node's original pos
3303             pr = (*p) - n->origin;
3305             // distances to the four lines (two handles and two perpendiculars)
3306             d_an = point_line_distance(&pr, an);
3307             d_na = point_line_distance(&pr, na);
3308             d_ap = point_line_distance(&pr, ap);
3309             d_pa = point_line_distance(&pr, pa);
3311             // find out which line is the closest, save its closest point in c
3312             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3313                 point_line_closest(&pr, an, &c);
3314             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3315                 point_line_closest(&pr, ap, &c);
3316             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3317                 point_line_closest(&pr, na, &c);
3318             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3319                 point_line_closest(&pr, pa, &c);
3320             }
3322             // move the node to the closest point
3323             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3324                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3325                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3327         } else {  // constraining to hor/vert
3329             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3330                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3331             } else { // snap to vert
3332                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3333             }
3334         }
3335     } else { // move freely
3336         if (n->is_dragging) {
3337             if (state & GDK_MOD1_MASK) { // sculpt
3338                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3339             } else {
3340                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3341                                             (*p)[NR::X] - n->pos[NR::X],
3342                                             (*p)[NR::Y] - n->pos[NR::Y],
3343                                             (state & GDK_SHIFT_MASK) == 0);
3344             }
3345         }
3346     }
3348     n->subpath->nodepath->desktop->scroll_to_point(p);
3350     return TRUE;
3353 /**
3354  * Node handle clicked callback.
3355  */
3356 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3358    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3360     if (state & GDK_CONTROL_MASK) { // "delete" handle
3361         if (n->p.knot == knot) {
3362             n->p.pos = n->pos;
3363         } else if (n->n.knot == knot) {
3364             n->n.pos = n->pos;
3365         }
3366         sp_node_update_handles(n);
3367         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3368         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3369         sp_nodepath_update_statusbar(nodepath);
3371     } else { // just select or add to selection, depending in Shift
3372         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3373     }
3376 /**
3377  * Node handle grabbed callback.
3378  */
3379 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3381    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3383     if (!n->selected) {
3384         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3385     }
3387     // remember the origin point of the handle
3388     if (n->p.knot == knot) {
3389         n->p.origin_radial = n->p.pos - n->pos;
3390     } else if (n->n.knot == knot) {
3391         n->n.origin_radial = n->n.pos - n->pos;
3392     } else {
3393         g_assert_not_reached();
3394     }
3396     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3399 /**
3400  * Node handle ungrabbed callback.
3401  */
3402 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3404    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3406     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3407     if (n->p.knot == knot) {
3408         n->p.origin_radial.a = 0;
3409         sp_knot_set_position(knot, &n->p.pos, state);
3410     } else if (n->n.knot == knot) {
3411         n->n.origin_radial.a = 0;
3412         sp_knot_set_position(knot, &n->n.pos, state);
3413     } else {
3414         g_assert_not_reached();
3415     }
3417     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3420 /**
3421  * Node handle "request" signal callback.
3422  */
3423 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3425     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3427     Inkscape::NodePath::NodeSide *me, *opposite;
3428     gint which;
3429     if (n->p.knot == knot) {
3430         me = &n->p;
3431         opposite = &n->n;
3432         which = -1;
3433     } else if (n->n.knot == knot) {
3434         me = &n->n;
3435         opposite = &n->p;
3436         which = 1;
3437     } else {
3438         me = opposite = NULL;
3439         which = 0;
3440         g_assert_not_reached();
3441     }
3443     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3445     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3447     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3448         /* We are smooth node adjacent with line */
3449         NR::Point const delta = *p - n->pos;
3450         NR::Coord const len = NR::L2(delta);
3451         Inkscape::NodePath::Node *othernode = opposite->other;
3452         NR::Point const ndelta = n->pos - othernode->pos;
3453         NR::Coord const linelen = NR::L2(ndelta);
3454         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3455             NR::Coord const scal = dot(delta, ndelta) / linelen;
3456             (*p) = n->pos + (scal / linelen) * ndelta;
3457         }
3458         *p = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), SP_ITEM(n->subpath->nodepath->object)).getPoint();
3459     } else {
3460         *p = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, SP_ITEM(n->subpath->nodepath->object)).getPoint();
3461     }
3463     sp_node_adjust_handle(n, -which);
3465     return FALSE;
3468 /**
3469  * Node handle moved callback.
3470  */
3471 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3473    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3475    Inkscape::NodePath::NodeSide *me;
3476    Inkscape::NodePath::NodeSide *other;
3477     if (n->p.knot == knot) {
3478         me = &n->p;
3479         other = &n->n;
3480     } else if (n->n.knot == knot) {
3481         me = &n->n;
3482         other = &n->p;
3483     } else {
3484         me = NULL;
3485         other = NULL;
3486         g_assert_not_reached();
3487     }
3489     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3490     Radial rme(me->pos - n->pos);
3491     Radial rother(other->pos - n->pos);
3492     Radial rnew(*p - n->pos);
3494     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3495         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3496         /* 0 interpreted as "no snapping". */
3498         // The closest PI/snaps angle, starting from zero.
3499         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3500         if (me->origin_radial.a == HUGE_VAL) {
3501             // ortho doesn't exist: original handle was zero length.
3502             rnew.a = a_snapped;
3503         } else {
3504             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3505              * its opposite and perpendiculars). */
3506             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3508             // Snap to the closest.
3509             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3510                        ? a_snapped
3511                        : a_ortho );
3512         }
3513     }
3515     if (state & GDK_MOD1_MASK) {
3516         // lock handle length
3517         rnew.r = me->origin_radial.r;
3518     }
3520     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3521         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3522         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3523         rother.a += rnew.a - rme.a;
3524         other->pos = NR::Point(rother) + n->pos;
3525         if (other->knot) {
3526             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3527             sp_knot_moveto(other->knot, &other->pos);
3528         }
3529     }
3531     me->pos = NR::Point(rnew) + n->pos;
3532     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3534     // move knot, but without emitting the signal:
3535     // we cannot emit a "moved" signal because we're now processing it
3536     sp_knot_moveto(me->knot, &(me->pos));
3538     update_object(n->subpath->nodepath);
3540     /* status text */
3541     SPDesktop *desktop = n->subpath->nodepath->desktop;
3542     if (!desktop) return;
3543     SPEventContext *ec = desktop->event_context;
3544     if (!ec) return;
3545     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3546     if (!mc) return;
3548     double degrees = 180 / M_PI * rnew.a;
3549     if (degrees > 180) degrees -= 360;
3550     if (degrees < -180) degrees += 360;
3551     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3552         degrees = angle_to_compass (degrees);
3554     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3556     mc->setF(Inkscape::NORMAL_MESSAGE,
3557          _("<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);
3559     g_string_free(length, TRUE);
3562 /**
3563  * Node handle event callback.
3564  */
3565 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3567     gboolean ret = FALSE;
3568     switch (event->type) {
3569         case GDK_KEY_PRESS:
3570             switch (get_group0_keyval (&event->key)) {
3571                 case GDK_space:
3572                     if (event->key.state & GDK_BUTTON1_MASK) {
3573                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3574                         stamp_repr(nodepath);
3575                         ret = TRUE;
3576                     }
3577                     break;
3578                 default:
3579                     break;
3580             }
3581             break;
3582         case GDK_ENTER_NOTIFY:
3583             // we use an experimentally determined threshold that seems to work fine
3584             if (NR::L2(n->pos - knot->pos) < 0.75)
3585                 Inkscape::NodePath::Path::active_node = n;
3586             break;
3587         case GDK_LEAVE_NOTIFY:
3588             // we use an experimentally determined threshold that seems to work fine
3589             if (NR::L2(n->pos - knot->pos) < 0.75)
3590                 Inkscape::NodePath::Path::active_node = NULL;
3591             break;
3592         default:
3593             break;
3594     }
3596     return ret;
3599 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3600                                  Radial &rme, Radial &rother, gboolean const both)
3602     rme.a += angle;
3603     if ( both
3604          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3605          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3606     {
3607         rother.a += angle;
3608     }
3611 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3612                                         Radial &rme, Radial &rother, gboolean const both)
3614     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3616     gdouble r;
3617     if ( both
3618          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3619          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3620     {
3621         r = MAX(rme.r, rother.r);
3622     } else {
3623         r = rme.r;
3624     }
3626     gdouble const weird_angle = atan2(norm_angle, r);
3627 /* Bulia says norm_angle is just the visible distance that the
3628  * object's end must travel on the screen.  Left as 'angle' for want of
3629  * a better name.*/
3631     rme.a += weird_angle;
3632     if ( both
3633          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3634          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3635     {
3636         rother.a += weird_angle;
3637     }
3640 /**
3641  * Rotate one node.
3642  */
3643 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3645     Inkscape::NodePath::NodeSide *me, *other;
3646     bool both = false;
3648     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3649     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3651     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3652         me = &(n->p);
3653         other = &(n->n);
3654     } else if (!n->p.other) {
3655         me = &(n->n);
3656         other = &(n->p);
3657     } else {
3658         if (which > 0) { // right handle
3659             if (xn > xp) {
3660                 me = &(n->n);
3661                 other = &(n->p);
3662             } else {
3663                 me = &(n->p);
3664                 other = &(n->n);
3665             }
3666         } else if (which < 0){ // left handle
3667             if (xn <= xp) {
3668                 me = &(n->n);
3669                 other = &(n->p);
3670             } else {
3671                 me = &(n->p);
3672                 other = &(n->n);
3673             }
3674         } else { // both handles
3675             me = &(n->n);
3676             other = &(n->p);
3677             both = true;
3678         }
3679     }
3681     Radial rme(me->pos - n->pos);
3682     Radial rother(other->pos - n->pos);
3684     if (screen) {
3685         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3686     } else {
3687         node_rotate_one_internal (*n, angle, rme, rother, both);
3688     }
3690     me->pos = n->pos + NR::Point(rme);
3692     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3693         other->pos =  n->pos + NR::Point(rother);
3694     }
3696     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3697     // so here we just move all the knots without emitting move signals, for speed
3698     sp_node_update_handles(n, false);
3701 /**
3702  * Rotate selected nodes.
3703  */
3704 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3706     if (!nodepath || !nodepath->selected) return;
3708     if (g_list_length(nodepath->selected) == 1) {
3709        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3710         node_rotate_one (n, angle, which, screen);
3711     } else {
3712        // rotate as an object:
3714         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3715         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3716         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3717             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3718             box.expandTo (n->pos); // contain all selected nodes
3719         }
3721         gdouble rot;
3722         if (screen) {
3723             gdouble const zoom = nodepath->desktop->current_zoom();
3724             gdouble const zmove = angle / zoom;
3725             gdouble const r = NR::L2(box.max() - box.midpoint());
3726             rot = atan2(zmove, r);
3727         } else {
3728             rot = angle;
3729         }
3731         NR::Point rot_center;
3732         if (Inkscape::NodePath::Path::active_node == NULL)
3733             rot_center = box.midpoint();
3734         else
3735             rot_center = Inkscape::NodePath::Path::active_node->pos;
3737         NR::Matrix t =
3738             NR::Matrix (NR::translate(-rot_center)) *
3739             NR::Matrix (NR::rotate(rot)) *
3740             NR::Matrix (NR::translate(rot_center));
3742         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3743             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3744             n->pos *= t;
3745             n->n.pos *= t;
3746             n->p.pos *= t;
3747             sp_node_update_handles(n, false);
3748         }
3749     }
3751     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3754 /**
3755  * Scale one node.
3756  */
3757 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3759     bool both = false;
3760     Inkscape::NodePath::NodeSide *me, *other;
3762     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3763     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3765     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3766         me = &(n->p);
3767         other = &(n->n);
3768         n->code = NR_CURVETO;
3769     } else if (!n->p.other) {
3770         me = &(n->n);
3771         other = &(n->p);
3772         if (n->n.other)
3773             n->n.other->code = NR_CURVETO;
3774     } else {
3775         if (which > 0) { // right handle
3776             if (xn > xp) {
3777                 me = &(n->n);
3778                 other = &(n->p);
3779                 if (n->n.other)
3780                     n->n.other->code = NR_CURVETO;
3781             } else {
3782                 me = &(n->p);
3783                 other = &(n->n);
3784                 n->code = NR_CURVETO;
3785             }
3786         } else if (which < 0){ // left handle
3787             if (xn <= xp) {
3788                 me = &(n->n);
3789                 other = &(n->p);
3790                 if (n->n.other)
3791                     n->n.other->code = NR_CURVETO;
3792             } else {
3793                 me = &(n->p);
3794                 other = &(n->n);
3795                 n->code = NR_CURVETO;
3796             }
3797         } else { // both handles
3798             me = &(n->n);
3799             other = &(n->p);
3800             both = true;
3801             n->code = NR_CURVETO;
3802             if (n->n.other)
3803                 n->n.other->code = NR_CURVETO;
3804         }
3805     }
3807     Radial rme(me->pos - n->pos);
3808     Radial rother(other->pos - n->pos);
3810     rme.r += grow;
3811     if (rme.r < 0) rme.r = 0;
3812     if (rme.a == HUGE_VAL) {
3813         if (me->other) { // if direction is unknown, initialize it towards the next node
3814             Radial rme_next(me->other->pos - n->pos);
3815             rme.a = rme_next.a;
3816         } else { // if there's no next, initialize to 0
3817             rme.a = 0;
3818         }
3819     }
3820     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3821         rother.r += grow;
3822         if (rother.r < 0) rother.r = 0;
3823         if (rother.a == HUGE_VAL) {
3824             rother.a = rme.a + M_PI;
3825         }
3826     }
3828     me->pos = n->pos + NR::Point(rme);
3830     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3831         other->pos = n->pos + NR::Point(rother);
3832     }
3834     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3835     // so here we just move all the knots without emitting move signals, for speed
3836     sp_node_update_handles(n, false);
3839 /**
3840  * Scale selected nodes.
3841  */
3842 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3844     if (!nodepath || !nodepath->selected) return;
3846     if (g_list_length(nodepath->selected) == 1) {
3847         // scale handles of the single selected node
3848         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3849         node_scale_one (n, grow, which);
3850     } else {
3851         // scale nodes as an "object":
3853         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3854         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3855         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3856             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3857             box.expandTo (n->pos); // contain all selected nodes
3858         }
3860         double scale = (box.maxExtent() + grow)/box.maxExtent();
3862         NR::Point scale_center;
3863         if (Inkscape::NodePath::Path::active_node == NULL)
3864             scale_center = box.midpoint();
3865         else
3866             scale_center = Inkscape::NodePath::Path::active_node->pos;
3868         NR::Matrix t =
3869             NR::Matrix (NR::translate(-scale_center)) *
3870             NR::Matrix (NR::scale(scale, scale)) *
3871             NR::Matrix (NR::translate(scale_center));
3873         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3874             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3875             n->pos *= t;
3876             n->n.pos *= t;
3877             n->p.pos *= t;
3878             sp_node_update_handles(n, false);
3879         }
3880     }
3882     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3885 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3887     if (!nodepath) return;
3888     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3891 /**
3892  * Flip selected nodes horizontally/vertically.
3893  */
3894 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
3896     if (!nodepath || !nodepath->selected) return;
3898     if (g_list_length(nodepath->selected) == 1 && !center) {
3899         // flip handles of the single selected node
3900         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3901         double temp = n->p.pos[axis];
3902         n->p.pos[axis] = n->n.pos[axis];
3903         n->n.pos[axis] = temp;
3904         sp_node_update_handles(n, false);
3905     } else {
3906         // scale nodes as an "object":
3908         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3909         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3910         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3911             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3912             box.expandTo (n->pos); // contain all selected nodes
3913         }
3915         if (!center) {
3916             center = box.midpoint();
3917         }
3918         NR::Matrix t =
3919             NR::Matrix (NR::translate(- *center)) *
3920             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3921             NR::Matrix (NR::translate(*center));
3923         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3924             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3925             n->pos *= t;
3926             n->n.pos *= t;
3927             n->p.pos *= t;
3928             sp_node_update_handles(n, false);
3929         }
3930     }
3932     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3935 //-----------------------------------------------
3936 /**
3937  * Return new subpath under given nodepath.
3938  */
3939 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3941     g_assert(nodepath);
3942     g_assert(nodepath->desktop);
3944    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3946     s->nodepath = nodepath;
3947     s->closed = FALSE;
3948     s->nodes = NULL;
3949     s->first = NULL;
3950     s->last = NULL;
3952     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3953     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3954     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3956     return s;
3959 /**
3960  * Destroy nodes in subpath, then subpath itself.
3961  */
3962 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3964     g_assert(subpath);
3965     g_assert(subpath->nodepath);
3966     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3968     while (subpath->nodes) {
3969         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3970     }
3972     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3974     g_free(subpath);
3977 /**
3978  * Link head to tail in subpath.
3979  */
3980 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3982     g_assert(!sp->closed);
3983     g_assert(sp->last != sp->first);
3984     g_assert(sp->first->code == NR_MOVETO);
3986     sp->closed = TRUE;
3988     //Link the head to the tail
3989     sp->first->p.other = sp->last;
3990     sp->last->n.other  = sp->first;
3991     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3992     sp->first          = sp->last;
3994     //Remove the extra end node
3995     sp_nodepath_node_destroy(sp->last->n.other);
3998 /**
3999  * Open closed (loopy) subpath at node.
4000  */
4001 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4003     g_assert(sp->closed);
4004     g_assert(n->subpath == sp);
4005     g_assert(sp->first == sp->last);
4007     /* We create new startpoint, current node will become last one */
4009    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4010                                                 &n->pos, &n->pos, &n->n.pos);
4013     sp->closed        = FALSE;
4015     //Unlink to make a head and tail
4016     sp->first         = new_path;
4017     sp->last          = n;
4018     n->n.other        = NULL;
4019     new_path->p.other = NULL;
4022 /**
4023  * Returns area in triangle given by points; may be negative.
4024  */
4025 inline double
4026 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
4028     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]);
4031 /**
4032  * Return new node in subpath with given properties.
4033  * \param pos Position of node.
4034  * \param ppos Handle position in previous direction
4035  * \param npos Handle position in previous direction
4036  */
4037 Inkscape::NodePath::Node *
4038 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)
4040     g_assert(sp);
4041     g_assert(sp->nodepath);
4042     g_assert(sp->nodepath->desktop);
4044     if (nodechunk == NULL)
4045         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4047     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4049     n->subpath  = sp;
4051     if (type != Inkscape::NodePath::NODE_NONE) {
4052         // use the type from sodipodi:nodetypes
4053         n->type = type;
4054     } else {
4055         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4056             // points are (almost) collinear
4057             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4058                 // endnode, or a node with a retracted handle
4059                 n->type = Inkscape::NodePath::NODE_CUSP;
4060             } else {
4061                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4062             }
4063         } else {
4064             n->type = Inkscape::NodePath::NODE_CUSP;
4065         }
4066     }
4068     n->code     = code;
4069     n->selected = FALSE;
4070     n->pos      = *pos;
4071     n->p.pos    = *ppos;
4072     n->n.pos    = *npos;
4074     n->dragging_out = NULL;
4076     Inkscape::NodePath::Node *prev;
4077     if (next) {
4078         //g_assert(g_list_find(sp->nodes, next));
4079         prev = next->p.other;
4080     } else {
4081         prev = sp->last;
4082     }
4084     if (prev)
4085         prev->n.other = n;
4086     else
4087         sp->first = n;
4089     if (next)
4090         next->p.other = n;
4091     else
4092         sp->last = n;
4094     n->p.other = prev;
4095     n->n.other = next;
4097     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"));
4098     sp_knot_set_position(n->knot, pos, 0);
4100     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4101     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4102     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4103     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4104     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4105     sp_knot_update_ctrl(n->knot);
4107     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4108     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4109     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4110     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4111     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4112     sp_knot_show(n->knot);
4114     // We only create handle knots and lines on demand
4115     n->p.knot = NULL;
4116     n->p.line = NULL;
4117     n->n.knot = NULL;
4118     n->n.line = NULL;
4120     sp->nodes = g_list_prepend(sp->nodes, n);
4122     return n;
4125 /**
4126  * Destroy node and its knots, link neighbors in subpath.
4127  */
4128 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4130     g_assert(node);
4131     g_assert(node->subpath);
4132     g_assert(SP_IS_KNOT(node->knot));
4134    Inkscape::NodePath::SubPath *sp = node->subpath;
4136     if (node->selected) { // first, deselect
4137         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4138         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4139     }
4141     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4143     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4144     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4145     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4146     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4147     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4148     g_object_unref(G_OBJECT(node->knot));
4150     if (node->p.knot) {
4151         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4152         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4153         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4154         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4155         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4156         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4157         g_object_unref(G_OBJECT(node->p.knot));
4158         node->p.knot = NULL;
4159     }
4161     if (node->n.knot) {
4162         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4163         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4164         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4165         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4166         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4167         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4168         g_object_unref(G_OBJECT(node->n.knot));
4169         node->n.knot = NULL;
4170     }
4172     if (node->p.line)
4173         gtk_object_destroy(GTK_OBJECT(node->p.line));
4174     if (node->n.line)
4175         gtk_object_destroy(GTK_OBJECT(node->n.line));
4177     if (sp->nodes) { // there are others nodes on the subpath
4178         if (sp->closed) {
4179             if (sp->first == node) {
4180                 g_assert(sp->last == node);
4181                 sp->first = node->n.other;
4182                 sp->last = sp->first;
4183             }
4184             node->p.other->n.other = node->n.other;
4185             node->n.other->p.other = node->p.other;
4186         } else {
4187             if (sp->first == node) {
4188                 sp->first = node->n.other;
4189                 sp->first->code = NR_MOVETO;
4190             }
4191             if (sp->last == node) sp->last = node->p.other;
4192             if (node->p.other) node->p.other->n.other = node->n.other;
4193             if (node->n.other) node->n.other->p.other = node->p.other;
4194         }
4195     } else { // this was the last node on subpath
4196         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4197     }
4199     g_mem_chunk_free(nodechunk, node);
4202 /**
4203  * Returns one of the node's two sides.
4204  * \param which Indicates which side.
4205  * \return Pointer to previous node side if which==-1, next if which==1.
4206  */
4207 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4209     g_assert(node);
4211     switch (which) {
4212         case -1:
4213             return &node->p;
4214         case 1:
4215             return &node->n;
4216         default:
4217             break;
4218     }
4220     g_assert_not_reached();
4222     return NULL;
4225 /**
4226  * Return the other side of the node, given one of its sides.
4227  */
4228 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4230     g_assert(node);
4232     if (me == &node->p) return &node->n;
4233     if (me == &node->n) return &node->p;
4235     g_assert_not_reached();
4237     return NULL;
4240 /**
4241  * Return NRPathcode on the given side of the node.
4242  */
4243 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4245     g_assert(node);
4247     if (me == &node->p) {
4248         if (node->p.other) return (NRPathcode)node->code;
4249         return NR_MOVETO;
4250     }
4252     if (me == &node->n) {
4253         if (node->n.other) return (NRPathcode)node->n.other->code;
4254         return NR_MOVETO;
4255     }
4257     g_assert_not_reached();
4259     return NR_END;
4262 /**
4263  * Return node with the given index
4264  */
4265 Inkscape::NodePath::Node *
4266 sp_nodepath_get_node_by_index(int index)
4268     Inkscape::NodePath::Node *e = NULL;
4270     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4271     if (!nodepath) {
4272         return e;
4273     }
4275     //find segment
4276     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4278         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4279         int n = g_list_length(sp->nodes);
4280         if (sp->closed) {
4281             n++;
4282         }
4284         //if the piece belongs to this subpath grab it
4285         //otherwise move onto the next subpath
4286         if (index < n) {
4287             e = sp->first;
4288             for (int i = 0; i < index; ++i) {
4289                 e = e->n.other;
4290             }
4291             break;
4292         } else {
4293             if (sp->closed) {
4294                 index -= (n+1);
4295             } else {
4296                 index -= n;
4297             }
4298         }
4299     }
4301     return e;
4304 /**
4305  * Returns plain text meaning of node type.
4306  */
4307 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4309     unsigned retracted = 0;
4310     bool endnode = false;
4312     for (int which = -1; which <= 1; which += 2) {
4313         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4314         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4315             retracted ++;
4316         if (!side->other)
4317             endnode = true;
4318     }
4320     if (retracted == 0) {
4321         if (endnode) {
4322                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4323                 return _("end node");
4324         } else {
4325             switch (node->type) {
4326                 case Inkscape::NodePath::NODE_CUSP:
4327                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4328                     return _("cusp");
4329                 case Inkscape::NodePath::NODE_SMOOTH:
4330                     // TRANSLATORS: "smooth" is an adjective here
4331                     return _("smooth");
4332                 case Inkscape::NodePath::NODE_SYMM:
4333                     return _("symmetric");
4334             }
4335         }
4336     } else if (retracted == 1) {
4337         if (endnode) {
4338             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4339             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4340         } else {
4341             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4342         }
4343     } else {
4344         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4345     }
4347     return NULL;
4350 /**
4351  * Handles content of statusbar as long as node tool is active.
4352  */
4353 void
4354 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4356     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");
4357     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4359     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4360     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4361     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4362     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4364     SPDesktop *desktop = NULL;
4365     if (nodepath) {
4366         desktop = nodepath->desktop;
4367     } else {
4368         desktop = SP_ACTIVE_DESKTOP;
4369     }
4371     SPEventContext *ec = desktop->event_context;
4372     if (!ec) return;
4373     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4374     if (!mc) return;
4376     if (selected_nodes == 0) {
4377         Inkscape::Selection *sel = desktop->selection;
4378         if (!sel || sel->isEmpty()) {
4379             mc->setF(Inkscape::NORMAL_MESSAGE,
4380                      _("Select a single object to edit its nodes or handles."));
4381         } else {
4382             if (nodepath) {
4383             mc->setF(Inkscape::NORMAL_MESSAGE,
4384                      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.",
4385                               "<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.",
4386                               total_nodes),
4387                      total_nodes);
4388             } else {
4389                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4390                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4391                 } else {
4392                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4393                 }
4394             }
4395         }
4396     } else if (nodepath && selected_nodes == 1) {
4397         mc->setF(Inkscape::NORMAL_MESSAGE,
4398                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4399                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4400                           total_nodes),
4401                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4402     } else {
4403         if (selected_subpaths > 1) {
4404             mc->setF(Inkscape::NORMAL_MESSAGE,
4405                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4406                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4407                               total_nodes),
4408                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4409         } else {
4410             mc->setF(Inkscape::NORMAL_MESSAGE,
4411                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4412                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4413                               total_nodes),
4414                      selected_nodes, total_nodes, when_selected);
4415         }
4416     }
4419 /*
4420  * returns a *copy* of the curve of that object.
4421  */
4422 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4423     if (!object)
4424         return NULL;
4426     SPCurve *curve = NULL;
4427     if (SP_IS_PATH(object)) {
4428         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4429         curve = sp_curve_copy(curve_new);
4430     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4431         const gchar *svgd = object->repr->attribute(key);
4432         if (svgd) {
4433             NArtBpath *bpath = sp_svg_read_path(svgd);
4434             SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
4435             if (curve_new) {
4436                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4437             } else {
4438                 g_free(bpath);
4439             }
4440         }
4441     }
4443     return curve;
4446 void sp_nodepath_object_set_curve (SPObject *object, SPCurve *curve) {
4447     if (!object || !curve)
4448         return;
4450     if (SP_IS_PATH(object)) {
4451         if (SP_SHAPE(object)->path_effect_href) {
4452             sp_path_set_original_curve(SP_PATH(object), curve, true, false);
4453         } else {
4454             sp_shape_set_curve(SP_SHAPE(object), curve, true);
4455         }
4456     } else if ( IS_LIVEPATHEFFECT(object) ) {
4457         g_warning("sp_nodepath_set_curve not implemented yet for lpeobjects");
4458     }
4461 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4462     np->show_helperpath = show;
4465 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4466     np->straight_path = true;
4467     np->show_handles = false;
4468     g_message("add code to make the path straight.");
4469     // do sp_nodepath_convert_node_type on all nodes?
4470     // search for this text !!!   "Make selected segments lines"
4474 /*
4475   Local Variables:
4476   mode:c++
4477   c-file-style:"stroustrup"
4478   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4479   indent-tabs-mode:nil
4480   fill-column:99
4481   End:
4482 */
4483 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :