Code

Applying fixes for gcc 4.3 build issues (closes LP: #169115)
[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 <cstring>
51 #include <string>
52 #include "live_effects/lpeobject.h"
53 #include "live_effects/parameter/parameter.h"
54 #include "util/mathfns.h"
56 class NR::Matrix;
58 /// \todo
59 /// evil evil evil. FIXME: conflict of two different Path classes!
60 /// There is a conflict in the namespace between two classes named Path.
61 /// #include "sp-flowtext.h"
62 /// #include "sp-flowregion.h"
64 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
65 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
66 GType sp_flowregion_get_type (void);
67 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
68 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
69 GType sp_flowtext_get_type (void);
70 // end evil workaround
72 #include "helper/stlport.h"
75 /// \todo fixme: Implement these via preferences */
77 #define NODE_FILL          0xbfbfbf00
78 #define NODE_STROKE        0x000000ff
79 #define NODE_FILL_HI       0xff000000
80 #define NODE_STROKE_HI     0x000000ff
81 #define NODE_FILL_SEL      0x0000ffff
82 #define NODE_STROKE_SEL    0x000000ff
83 #define NODE_FILL_SEL_HI   0xff000000
84 #define NODE_STROKE_SEL_HI 0x000000ff
85 #define KNOT_FILL          0xffffffff
86 #define KNOT_STROKE        0x000000ff
87 #define KNOT_FILL_HI       0xff000000
88 #define KNOT_STROKE_HI     0x000000ff
90 static GMemChunk *nodechunk = NULL;
92 /* Creation from object */
94 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
95 static gchar *parse_nodetypes(gchar const *types, gint length);
97 /* Object updating */
99 static void stamp_repr(Inkscape::NodePath::Path *np);
100 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
101 static gchar *create_typestr(Inkscape::NodePath::Path *np);
103 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
105 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
107 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
109 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
111 /* Adjust handle placement, if the node or the other handle is moved */
112 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
113 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
115 /* Node event callbacks */
116 static void node_clicked(SPKnot *knot, guint state, gpointer data);
117 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
118 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
119 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
121 /* Handle event callbacks */
122 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
123 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
124 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
125 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
126 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
127 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
129 /* Constructors and destructors */
131 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
132 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
133 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
134 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
135 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
136                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
137 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
139 /* Helpers */
141 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
142 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
143 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
145 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
146 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
148 // active_node indicates mouseover node
149 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
151 /**
152  * \brief Creates new nodepath from item
153  */
154 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
156     Inkscape::XML::Node *repr = object->repr;
158     /** \todo
159      * FIXME: remove this. We don't want to edit paths inside flowtext.
160      * Instead we will build our flowtext with cloned paths, so that the
161      * real paths are outside the flowtext and thus editable as usual.
162      */
163     if (SP_IS_FLOWTEXT(object)) {
164         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
165             if SP_IS_FLOWREGION(child) {
166                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
167                 if (grandchild && SP_IS_PATH(grandchild)) {
168                     object = SP_ITEM(grandchild);
169                     break;
170                 }
171             }
172         }
173     }
175     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
177     if (curve == NULL)
178         return NULL;
180     NArtBpath *bpath = sp_curve_first_bpath(curve);
181     gint length = curve->end;
182     if (length == 0) {
183         sp_curve_unref(curve);
184         return NULL; // prevent crash for one-node paths
185     }
187     //Create new nodepath
188     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
189     if (!np) {
190         sp_curve_unref(curve);
191         return NULL;
192     }
194     // Set defaults
195     np->desktop     = desktop;
196     np->object      = object;
197     np->subpaths    = NULL;
198     np->selected    = NULL;
199     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
200     np->livarot_path = NULL;
201     np->local_change = 0;
202     np->show_handles = show_handles;
203     np->helper_path = NULL;
204     np->curve = sp_curve_copy(curve);
205     np->show_helperpath = false;
206     np->straight_path = false;
207     if (IS_LIVEPATHEFFECT(object) && item) {
208         np->item = item;
209     } else {
210         np->item = SP_ITEM(object);
211     }
213     // we need to update item's transform from the repr here,
214     // because they may be out of sync when we respond
215     // to a change in repr by regenerating nodepath     --bb
216     sp_object_read_attr(SP_OBJECT(np->item), "transform");
218     np->i2d  = sp_item_i2d_affine(np->item);
219     np->d2i  = np->i2d.inverse();
221     np->repr = repr;
222     if (repr_key_in) { // apparantly the object is an LPEObject
223         np->repr_key = g_strdup(repr_key_in);
224         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
225         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
226         if (lpeparam) {
227             lpeparam->param_setup_nodepath(np);
228         }
229     } else {
230         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
231         if ( SP_SHAPE(np->object)->path_effect_href ) {
232             np->repr_key = g_strdup("inkscape:original-d");
234             LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(SP_SHAPE(np->object));
235             if (lpeobj && lpeobj->lpe) {
236                 lpeobj->lpe->setup_nodepath(np);
237             }
238         } else {
239             np->repr_key = g_strdup("d");
240         }
241     }
243     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
244     gchar *typestr = parse_nodetypes(nodetypes, length);
246     // create the subpath(s) from the bpath
247     NArtBpath *b = bpath;
248     while (b->code != NR_END) {
249         b = subpath_from_bpath(np, b, typestr + (b - bpath));
250     }
252     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
253     np->subpaths = g_list_reverse(np->subpaths);
255     g_free(typestr);
256     sp_curve_unref(curve);
258     // create the livarot representation from the same item
259     sp_nodepath_ensure_livarot_path(np);
261     // Draw helper curve
262     if (np->show_helperpath) {
263         SPCurve *helper_curve = sp_curve_copy(np->curve);
264         sp_curve_transform(helper_curve, np->i2d );
265         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
266         sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(np->helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
267         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
268         sp_canvas_item_show(np->helper_path);
269         sp_curve_unref(helper_curve);
270     }
272     return np;
275 /**
276  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
277  */
278 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
280     if (!np)  //soft fail, like delete
281         return;
283     while (np->subpaths) {
284         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
285     }
287     //Inform the ShapeEditor that made me, if any, that I am gone.
288     if (np->shape_editor)
289         np->shape_editor->nodepath_destroyed();
291     g_assert(!np->selected);
293     if (np->livarot_path) {
294         delete np->livarot_path;
295         np->livarot_path = NULL;
296     }
298     if (np->helper_path) {
299         GtkObject *temp = np->helper_path;
300         np->helper_path = NULL;
301         gtk_object_destroy(temp);
302     }
303     if (np->curve) {
304         sp_curve_unref(np->curve);
305         np->curve = NULL;
306     }
308     if (np->repr_key) {
309         g_free(np->repr_key);
310         np->repr_key = NULL;
311     }
312     if (np->repr_nodetypes_key) {
313         g_free(np->repr_nodetypes_key);
314         np->repr_nodetypes_key = NULL;
315     }
317     np->desktop = NULL;
319     g_free(np);
323 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
325     if (np && np->livarot_path == NULL) {
326         SPCurve *curve = create_curve(np);
327         NArtBpath *bpath = SP_CURVE_BPATH(curve);
328         np->livarot_path = bpath_to_Path(bpath);
330         if (np->livarot_path)
331             np->livarot_path->ConvertWithBackData(0.01);
333         sp_curve_unref(curve);
334     }
338 /**
339  *  Return the node count of a given NodeSubPath.
340  */
341 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
343     if (!subpath)
344         return 0;
345     gint nodeCount = g_list_length(subpath->nodes);
346     return nodeCount;
349 /**
350  *  Return the node count of a given NodePath.
351  */
352 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
354     if (!np)
355         return 0;
356     gint nodeCount = 0;
357     for (GList *item = np->subpaths ; item ; item=item->next) {
358        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
359         nodeCount += g_list_length(subpath->nodes);
360     }
361     return nodeCount;
364 /**
365  *  Return the subpath count of a given NodePath.
366  */
367 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
369     if (!np)
370         return 0;
371     return g_list_length (np->subpaths);
374 /**
375  *  Return the selected node count of a given NodePath.
376  */
377 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
379     if (!np)
380         return 0;
381     return g_list_length (np->selected);
384 /**
385  *  Return the number of subpaths where nodes are selected in a given NodePath.
386  */
387 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
389     if (!np)
390         return 0;
391     if (!np->selected)
392         return 0;
393     if (!np->selected->next)
394         return 1;
395     gint count = 0;
396     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
397         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
398         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
399             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
400             if (node->selected) {
401                 count ++;
402                 break;
403             }
404         }
405     }
406     return count;
409 /**
410  * Clean up a nodepath after editing.
411  *
412  * Currently we are deleting trivial subpaths.
413  */
414 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
416     GList *badSubPaths = NULL;
418     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
419     for (GList *l = nodepath->subpaths; l ; l=l->next) {
420        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
421        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
422             badSubPaths = g_list_append(badSubPaths, sp);
423     }
425     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
426     //also removes the subpath from nodepath->subpaths
427     for (GList *l = badSubPaths; l ; l=l->next) {
428        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
429         sp_nodepath_subpath_destroy(sp);
430     }
432     g_list_free(badSubPaths);
435 /**
436  * Create new nodepath from b, make it subpath of np.
437  * \param t The node type.
438  * \todo Fixme: t should be a proper type, rather than gchar
439  */
440 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
442     NR::Point ppos, pos, npos;
444     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
446     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
447     bool const closed = (b->code == NR_MOVETO);
449     pos = NR::Point(b->x3, b->y3) * np->i2d;
450     if (b[1].code == NR_CURVETO) {
451         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
452     } else {
453         npos = pos;
454     }
455     Inkscape::NodePath::Node *n;
456     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
457     g_assert(sp->first == n);
458     g_assert(sp->last  == n);
460     b++;
461     t++;
462     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
463         pos = NR::Point(b->x3, b->y3) * np->i2d;
464         if (b->code == NR_CURVETO) {
465             ppos = NR::Point(b->x2, b->y2) * np->i2d;
466         } else {
467             ppos = pos;
468         }
469         if (b[1].code == NR_CURVETO) {
470             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
471         } else {
472             npos = pos;
473         }
474         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
475         b++;
476         t++;
477     }
479     if (closed) sp_nodepath_subpath_close(sp);
481     return b;
484 /**
485  * Convert from sodipodi:nodetypes to new style type string.
486  */
487 static gchar *parse_nodetypes(gchar const *types, gint length)
489     g_assert(length > 0);
491     gchar *typestr = g_new(gchar, length + 1);
493     gint pos = 0;
495     if (types) {
496         for (gint i = 0; types[i] && ( i < length ); i++) {
497             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
498             if (types[i] != '\0') {
499                 switch (types[i]) {
500                     case 's':
501                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
502                         break;
503                     case 'z':
504                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
505                         break;
506                     case 'c':
507                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
508                         break;
509                     default:
510                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
511                         break;
512                 }
513             }
514         }
515     }
517     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
519     return typestr;
522 /**
523  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
524  * updated but repr is not (for speed). Used during curve and node drag.
525  */
526 static void update_object(Inkscape::NodePath::Path *np)
528     g_assert(np);
530     sp_curve_unref(np->curve);
531     np->curve = create_curve(np);
533     sp_nodepath_set_curve(np, np->curve);
535     if (np->show_helperpath) {
536         SPCurve * helper_curve = sp_curve_copy(np->curve);
537         sp_curve_transform(helper_curve, np->i2d );
538         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
539         sp_curve_unref(helper_curve);
540     }
543 /**
544  * Update XML path node with data from path object.
545  */
546 static void update_repr_internal(Inkscape::NodePath::Path *np)
548     g_assert(np);
550     Inkscape::XML::Node *repr = np->object->repr;
552     sp_curve_unref(np->curve);
553     np->curve = create_curve(np);
555     gchar *typestr = create_typestr(np);
556     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
558     // determine if path has an effect applied and write to correct "d" attribute.
559     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
560         np->local_change++;
561         repr->setAttribute(np->repr_key, svgpath);
562     }
564     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
565         np->local_change++;
566         repr->setAttribute(np->repr_nodetypes_key, typestr);
567     }
569     g_free(svgpath);
570     g_free(typestr);
572     if (np->show_helperpath) {
573         SPCurve * helper_curve = sp_curve_copy(np->curve);
574         sp_curve_transform(helper_curve, np->i2d );
575         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
576         sp_curve_unref(helper_curve);
577     }
578  }
580 /**
581  * Update XML path node with data from path object, commit changes forever.
582  */
583 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
585     //fixme: np can be NULL, so check before proceeding
586     g_return_if_fail(np != NULL);
588     if (np->livarot_path) {
589         delete np->livarot_path;
590         np->livarot_path = NULL;
591     }
593     update_repr_internal(np);
594     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
596     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
597                      annotation);
600 /**
601  * Update XML path node with data from path object, commit changes with undo.
602  */
603 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
605     if (np->livarot_path) {
606         delete np->livarot_path;
607         np->livarot_path = NULL;
608     }
610     update_repr_internal(np);
611     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
612                            annotation);
615 /**
616  * Make duplicate of path, replace corresponding XML node in tree, commit.
617  */
618 static void stamp_repr(Inkscape::NodePath::Path *np)
620     g_assert(np);
622     Inkscape::XML::Node *old_repr = np->object->repr;
623     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
625     // remember the position of the item
626     gint pos = old_repr->position();
627     // remember parent
628     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
630     SPCurve *curve = create_curve(np);
631     gchar *typestr = create_typestr(np);
633     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
635     new_repr->setAttribute(np->repr_key, svgpath);
636     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
638     // add the new repr to the parent
639     parent->appendChild(new_repr);
640     // move to the saved position
641     new_repr->setPosition(pos > 0 ? pos : 0);
643     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
644                      _("Stamp"));
646     Inkscape::GC::release(new_repr);
647     g_free(svgpath);
648     g_free(typestr);
649     sp_curve_unref(curve);
652 /**
653  * Create curve from path.
654  */
655 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
657     SPCurve *curve = sp_curve_new();
659     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
660        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
661         sp_curve_moveto(curve,
662                         sp->first->pos * np->d2i);
663        Inkscape::NodePath::Node *n = sp->first->n.other;
664         while (n) {
665             NR::Point const end_pt = n->pos * np->d2i;
666             switch (n->code) {
667                 case NR_LINETO:
668                     sp_curve_lineto(curve, end_pt);
669                     break;
670                 case NR_CURVETO:
671                     sp_curve_curveto(curve,
672                                      n->p.other->n.pos * np->d2i,
673                                      n->p.pos * np->d2i,
674                                      end_pt);
675                     break;
676                 default:
677                     g_assert_not_reached();
678                     break;
679             }
680             if (n != sp->last) {
681                 n = n->n.other;
682             } else {
683                 n = NULL;
684             }
685         }
686         if (sp->closed) {
687             sp_curve_closepath(curve);
688         }
689     }
691     return curve;
694 /**
695  * Convert path type string to sodipodi:nodetypes style.
696  */
697 static gchar *create_typestr(Inkscape::NodePath::Path *np)
699     gchar *typestr = g_new(gchar, 32);
700     gint len = 32;
701     gint pos = 0;
703     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
704        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
706         if (pos >= len) {
707             typestr = g_renew(gchar, typestr, len + 32);
708             len += 32;
709         }
711         typestr[pos++] = 'c';
713        Inkscape::NodePath::Node *n;
714         n = sp->first->n.other;
715         while (n) {
716             gchar code;
718             switch (n->type) {
719                 case Inkscape::NodePath::NODE_CUSP:
720                     code = 'c';
721                     break;
722                 case Inkscape::NodePath::NODE_SMOOTH:
723                     code = 's';
724                     break;
725                 case Inkscape::NodePath::NODE_SYMM:
726                     code = 'z';
727                     break;
728                 default:
729                     g_assert_not_reached();
730                     code = '\0';
731                     break;
732             }
734             if (pos >= len) {
735                 typestr = g_renew(gchar, typestr, len + 32);
736                 len += 32;
737             }
739             typestr[pos++] = code;
741             if (n != sp->last) {
742                 n = n->n.other;
743             } else {
744                 n = NULL;
745             }
746         }
747     }
749     if (pos >= len) {
750         typestr = g_renew(gchar, typestr, len + 1);
751         len += 1;
752     }
754     typestr[pos++] = '\0';
756     return typestr;
759 /**
760  * Returns current path in context. // later eliminate this function at all!
761  */
762 static Inkscape::NodePath::Path *sp_nodepath_current()
764     if (!SP_ACTIVE_DESKTOP) {
765         return NULL;
766     }
768     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
770     if (!SP_IS_NODE_CONTEXT(event_context)) {
771         return NULL;
772     }
774     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
779 /**
780  \brief Fills node and handle positions for three nodes, splitting line
781   marked by end at distance t.
782  */
783 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
785     g_assert(new_path != NULL);
786     g_assert(end      != NULL);
788     g_assert(end->p.other == new_path);
789    Inkscape::NodePath::Node *start = new_path->p.other;
790     g_assert(start);
792     if (end->code == NR_LINETO) {
793         new_path->type =Inkscape::NodePath::NODE_CUSP;
794         new_path->code = NR_LINETO;
795         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
796     } else {
797         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
798         new_path->code = NR_CURVETO;
799         gdouble s      = 1 - t;
800         for (int dim = 0; dim < 2; dim++) {
801             NR::Coord const f000 = start->pos[dim];
802             NR::Coord const f001 = start->n.pos[dim];
803             NR::Coord const f011 = end->p.pos[dim];
804             NR::Coord const f111 = end->pos[dim];
805             NR::Coord const f00t = s * f000 + t * f001;
806             NR::Coord const f01t = s * f001 + t * f011;
807             NR::Coord const f11t = s * f011 + t * f111;
808             NR::Coord const f0tt = s * f00t + t * f01t;
809             NR::Coord const f1tt = s * f01t + t * f11t;
810             NR::Coord const fttt = s * f0tt + t * f1tt;
811             start->n.pos[dim]    = f00t;
812             new_path->p.pos[dim] = f0tt;
813             new_path->pos[dim]   = fttt;
814             new_path->n.pos[dim] = f1tt;
815             end->p.pos[dim]      = f11t;
816         }
817     }
820 /**
821  * Adds new node on direct line between two nodes, activates handles of all
822  * three nodes.
823  */
824 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
826     g_assert(end);
827     g_assert(end->subpath);
828     g_assert(g_list_find(end->subpath->nodes, end));
830    Inkscape::NodePath::Node *start = end->p.other;
831     g_assert( start->n.other == end );
832    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
833                                                end,
834                                                (NRPathcode)end->code == NR_LINETO?
835                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
836                                                (NRPathcode)end->code,
837                                                &start->pos, &start->pos, &start->n.pos);
838     sp_nodepath_line_midpoint(newnode, end, t);
840     sp_node_adjust_handles(start);
841     sp_node_update_handles(start);
842     sp_node_update_handles(newnode);
843     sp_node_adjust_handles(end);
844     sp_node_update_handles(end);
846     return newnode;
849 /**
850 \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
851 */
852 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
854     g_assert(node);
855     g_assert(node->subpath);
856     g_assert(g_list_find(node->subpath->nodes, node));
858    Inkscape::NodePath::SubPath *sp = node->subpath;
859     Inkscape::NodePath::Path *np    = sp->nodepath;
861     if (sp->closed) {
862         sp_nodepath_subpath_open(sp, node);
863         return sp->first;
864     } else {
865         // no break for end nodes
866         if (node == sp->first) return NULL;
867         if (node == sp->last ) return NULL;
869         // create a new subpath
870        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
872         // duplicate the break node as start of the new subpath
873        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
875         while (node->n.other) { // copy the remaining nodes into the new subpath
876            Inkscape::NodePath::Node *n  = node->n.other;
877            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);
878             if (n->selected) {
879                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
880             }
881             sp_nodepath_node_destroy(n); // remove the point on the original subpath
882         }
884         return newnode;
885     }
888 /**
889  * Duplicate node and connect to neighbours.
890  */
891 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
893     g_assert(node);
894     g_assert(node->subpath);
895     g_assert(g_list_find(node->subpath->nodes, node));
897    Inkscape::NodePath::SubPath *sp = node->subpath;
899     NRPathcode code = (NRPathcode) node->code;
900     if (code == NR_MOVETO) { // if node is the endnode,
901         node->code = NR_LINETO; // new one is inserted before it, so change that to line
902     }
904     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
906     if (!node->n.other || !node->p.other) // if node is an endnode, select it
907         return node;
908     else
909         return newnode; // otherwise select the newly created node
912 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
914     node->p.pos = (node->pos + (node->pos - node->n.pos));
917 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
919     node->n.pos = (node->pos + (node->pos - node->p.pos));
922 /**
923  * Change line type at node, with side effects on neighbours.
924  */
925 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
927     g_assert(end);
928     g_assert(end->subpath);
929     g_assert(end->p.other);
931     if (end->code == static_cast< guint > ( code ) )
932         return;
934    Inkscape::NodePath::Node *start = end->p.other;
936     end->code = code;
938     if (code == NR_LINETO) {
939         if (start->code == NR_LINETO) {
940             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
941         }
942         if (end->n.other) {
943             if (end->n.other->code == NR_LINETO) {
944                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
945             }
946         }
947     } else {
948         NR::Point delta = end->pos - start->pos;
949         start->n.pos = start->pos + delta / 3;
950         end->p.pos = end->pos - delta / 3;
951         sp_node_adjust_handle(start, 1);
952         sp_node_adjust_handle(end, -1);
953     }
955     sp_node_update_handles(start);
956     sp_node_update_handles(end);
959 /**
960  * Change node type, and its handles accordingly.
961  */
962 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
964     g_assert(node);
965     g_assert(node->subpath);
967     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
968         return node;
970     if ((node->p.other != NULL) && (node->n.other != NULL)) {
971         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
972             type =Inkscape::NodePath::NODE_CUSP;
973         }
974     }
976     node->type = type;
978     if (node->type == Inkscape::NodePath::NODE_CUSP) {
979         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
980         node->knot->setSize (node->selected? 11 : 9);
981         sp_knot_update_ctrl(node->knot);
982     } else {
983         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
984         node->knot->setSize (node->selected? 9 : 7);
985         sp_knot_update_ctrl(node->knot);
986     }
988     // if one of handles is mouseovered, preserve its position
989     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
990         sp_node_adjust_handle(node, 1);
991     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
992         sp_node_adjust_handle(node, -1);
993     } else {
994         sp_node_adjust_handles(node);
995     }
997     sp_node_update_handles(node);
999     sp_nodepath_update_statusbar(node->subpath->nodepath);
1001     return node;
1004 /**
1005  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
1006  * adjacent segments from lines to curves.
1007 */
1008 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1010     bool p_line = (node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos);
1011     bool n_line = (node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos);
1013     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1014         if (p_line && n_line) {
1015             // only if both adjacent segments are lines,
1016             // convert both to curves:
1018             node->code = NR_CURVETO;
1019             node->n.other->code = NR_CURVETO;
1021             NR::Point leg_prev = node->pos - node->p.other->pos;
1022             NR::Point leg_next = node->pos - node->n.other->pos;
1024             double norm_leg_prev = L2(leg_prev);
1025             double norm_leg_next = L2(leg_next);
1027             // delta has length 1 and is orthogonal to bisecting line
1028             NR::Point delta;
1029             if (norm_leg_next > 0.0) {
1030                 delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1031                 (&delta)->normalize();
1032             }
1034             if (type == Inkscape::NodePath::NODE_SYMM) {
1035                 double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1036                 node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1037                 node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1038             } else {
1039                 // length of handle is proportional to distance to adjacent node
1040                 node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1041                 node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1042             }
1044             sp_node_update_handles(node);
1045         }
1046     }
1048     sp_nodepath_set_node_type (node, type);
1051 /**
1052  * Move node to point, and adjust its and neighbouring handles.
1053  */
1054 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1056     NR::Point delta = p - node->pos;
1057     node->pos = p;
1059     node->p.pos += delta;
1060     node->n.pos += delta;
1062     Inkscape::NodePath::Node *node_p = NULL;
1063     Inkscape::NodePath::Node *node_n = NULL;
1065     if (node->p.other) {
1066         if (node->code == NR_LINETO) {
1067             sp_node_adjust_handle(node, 1);
1068             sp_node_adjust_handle(node->p.other, -1);
1069             node_p = node->p.other;
1070         }
1071     }
1072     if (node->n.other) {
1073         if (node->n.other->code == NR_LINETO) {
1074             sp_node_adjust_handle(node, -1);
1075             sp_node_adjust_handle(node->n.other, 1);
1076             node_n = node->n.other;
1077         }
1078     }
1080     // this function is only called from batch movers that will update display at the end
1081     // themselves, so here we just move all the knots without emitting move signals, for speed
1082     sp_node_update_handles(node, false);
1083     if (node_n) {
1084         sp_node_update_handles(node_n, false);
1085     }
1086     if (node_p) {
1087         sp_node_update_handles(node_p, false);
1088     }
1091 /**
1092  * Call sp_node_moveto() for node selection and handle possible snapping.
1093  */
1094 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1095                                             bool const snap = true)
1097     NR::Coord best = NR_HUGE;
1098     NR::Point delta(dx, dy);
1099     NR::Point best_pt = delta;
1101     if (snap) {
1102         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
1104         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1105             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1106             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, SP_PATH(n->subpath->nodepath->item));
1107             if (s.getDistance() < best) {
1108                 best = s.getDistance();
1109                 best_pt = s.getPoint() - n->pos;
1110             }
1111         }
1112     }
1114     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1115         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1116         sp_node_moveto(n, n->pos + best_pt);
1117     }
1119     // do not update repr here so that node dragging is acceptably fast
1120     update_object(nodepath);
1123 /**
1124 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1125 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1126 near x = 0.
1127  */
1128 double
1129 sculpt_profile (double x, double alpha, guint profile)
1131     if (x >= 1)
1132         return 0;
1133     if (x <= 0)
1134         return 1;
1136     switch (profile) {
1137         case SCULPT_PROFILE_LINEAR:
1138         return 1 - x;
1139         case SCULPT_PROFILE_BELL:
1140         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1141         case SCULPT_PROFILE_ELLIPTIC:
1142         return sqrt(1 - x*x);
1143     }
1145     return 1;
1148 double
1149 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1151     // extremely primitive for now, don't have time to look for the real one
1152     double lower = NR::L2(b - a);
1153     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1154     return (lower + upper)/2;
1157 void
1158 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1160     n->pos = n->origin + delta;
1161     n->n.pos = n->n.origin + delta_n;
1162     n->p.pos = n->p.origin + delta_p;
1163     sp_node_adjust_handles(n);
1164     sp_node_update_handles(n, false);
1167 /**
1168  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1169  * on how far they are from the dragged node n.
1170  */
1171 static void
1172 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1174     g_assert (n);
1175     g_assert (nodepath);
1176     g_assert (n->subpath->nodepath == nodepath);
1178     double pressure = n->knot->pressure;
1179     if (pressure == 0)
1180         pressure = 0.5; // default
1181     pressure = CLAMP (pressure, 0.2, 0.8);
1183     // map pressure to alpha = 1/5 ... 5
1184     double alpha = 1 - 2 * fabs(pressure - 0.5);
1185     if (pressure > 0.5)
1186         alpha = 1/alpha;
1188     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1190     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1191         // Only one subpath has selected nodes:
1192         // use linear mode, where the distance from n to node being dragged is calculated along the path
1194         double n_sel_range = 0, p_sel_range = 0;
1195         guint n_nodes = 0, p_nodes = 0;
1196         guint n_sel_nodes = 0, p_sel_nodes = 0;
1198         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1199         {
1200             double n_range = 0, p_range = 0;
1201             bool n_going = true, p_going = true;
1202             Inkscape::NodePath::Node *n_node = n;
1203             Inkscape::NodePath::Node *p_node = n;
1204             do {
1205                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1206                 if (n_node && n_going)
1207                     n_node = n_node->n.other;
1208                 if (n_node == NULL) {
1209                     n_going = false;
1210                 } else {
1211                     n_nodes ++;
1212                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1213                     if (n_node->selected) {
1214                         n_sel_nodes ++;
1215                         n_sel_range = n_range;
1216                     }
1217                     if (n_node == p_node) {
1218                         n_going = false;
1219                         p_going = false;
1220                     }
1221                 }
1222                 if (p_node && p_going)
1223                     p_node = p_node->p.other;
1224                 if (p_node == NULL) {
1225                     p_going = false;
1226                 } else {
1227                     p_nodes ++;
1228                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1229                     if (p_node->selected) {
1230                         p_sel_nodes ++;
1231                         p_sel_range = p_range;
1232                     }
1233                     if (p_node == n_node) {
1234                         n_going = false;
1235                         p_going = false;
1236                     }
1237                 }
1238             } while (n_going || p_going);
1239         }
1241         // Second pass: actually move nodes in this subpath
1242         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1243         {
1244             double n_range = 0, p_range = 0;
1245             bool n_going = true, p_going = true;
1246             Inkscape::NodePath::Node *n_node = n;
1247             Inkscape::NodePath::Node *p_node = n;
1248             do {
1249                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1250                 if (n_node && n_going)
1251                     n_node = n_node->n.other;
1252                 if (n_node == NULL) {
1253                     n_going = false;
1254                 } else {
1255                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1256                     if (n_node->selected) {
1257                         sp_nodepath_move_node_and_handles (n_node,
1258                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1259                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1260                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1261                     }
1262                     if (n_node == p_node) {
1263                         n_going = false;
1264                         p_going = false;
1265                     }
1266                 }
1267                 if (p_node && p_going)
1268                     p_node = p_node->p.other;
1269                 if (p_node == NULL) {
1270                     p_going = false;
1271                 } else {
1272                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1273                     if (p_node->selected) {
1274                         sp_nodepath_move_node_and_handles (p_node,
1275                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1276                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1277                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1278                     }
1279                     if (p_node == n_node) {
1280                         n_going = false;
1281                         p_going = false;
1282                     }
1283                 }
1284             } while (n_going || p_going);
1285         }
1287     } else {
1288         // Multiple subpaths have selected nodes:
1289         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1290         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1291         // fix the pear-like shape when sculpting e.g. a ring
1293         // First pass: calculate range
1294         gdouble direct_range = 0;
1295         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1296             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1297             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1298                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1299                 if (node->selected) {
1300                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1301                 }
1302             }
1303         }
1305         // Second pass: actually move nodes
1306         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1307             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1308             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1309                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1310                 if (node->selected) {
1311                     if (direct_range > 1e-6) {
1312                         sp_nodepath_move_node_and_handles (node,
1313                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1314                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1315                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1316                     } else {
1317                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1318                     }
1320                 }
1321             }
1322         }
1323     }
1325     // do not update repr here so that node dragging is acceptably fast
1326     update_object(nodepath);
1330 /**
1331  * Move node selection to point, adjust its and neighbouring handles,
1332  * handle possible snapping, and commit the change with possible undo.
1333  */
1334 void
1335 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1337     if (!nodepath) return;
1339     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1341     if (dx == 0) {
1342         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1343     } else if (dy == 0) {
1344         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1345     } else {
1346         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1347     }
1350 /**
1351  * Move node selection off screen and commit the change.
1352  */
1353 void
1354 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1356     // borrowed from sp_selection_move_screen in selection-chemistry.c
1357     // we find out the current zoom factor and divide deltas by it
1358     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1360     gdouble zoom = desktop->current_zoom();
1361     gdouble zdx = dx / zoom;
1362     gdouble zdy = dy / zoom;
1364     if (!nodepath) return;
1366     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1368     if (dx == 0) {
1369         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1370     } else if (dy == 0) {
1371         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1372     } else {
1373         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1374     }
1377 /**
1378  * Move selected nodes to the absolute position given
1379  */
1380 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1382     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1383         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1384         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1385         sp_node_moveto(n, npos);
1386     }
1388     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1391 /**
1392  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1393  */
1394 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1396     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1397     g_return_val_if_fail(nodepath->selected, no_coord);
1399     // determine coordinate of first selected node
1400     GList *nsel = nodepath->selected;
1401     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1402     NR::Coord coord = n->pos[axis];
1403     bool coincide = true;
1405     // compare it to the coordinates of all the other selected nodes
1406     for (GList *l = nsel->next; l != NULL; l = l->next) {
1407         n = (Inkscape::NodePath::Node *) l->data;
1408         if (n->pos[axis] != coord) {
1409             coincide = false;
1410         }
1411     }
1412     if (coincide) {
1413         return coord;
1414     } else {
1415         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1416         // currently we return the coordinate of the bounding box midpoint because I don't know how
1417         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1418         return bbox.midpoint()[axis];
1419     }
1422 /** If they don't yet exist, creates knot and line for the given side of the node */
1423 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1425     if (!side->knot) {
1426         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"));
1428         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1429         side->knot->setSize (7);
1430         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1431         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1432         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1433         sp_knot_update_ctrl(side->knot);
1435         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1436         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1437         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1438         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1439         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1440         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1441     }
1443     if (!side->line) {
1444         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1445                                         SP_TYPE_CTRLLINE, NULL);
1446     }
1449 /**
1450  * Ensure the given handle of the node is visible/invisible, update its screen position
1451  */
1452 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1454     g_assert(node != NULL);
1456    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1457     NRPathcode code = sp_node_path_code_from_side(node, side);
1459     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1461     if (show_handle) {
1462         if (!side->knot) { // No handle knot at all
1463             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1464             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1465             side->knot->pos = side->pos;
1466             if (side->knot->item)
1467                 SP_CTRL(side->knot->item)->moveto(side->pos);
1468             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1469             sp_knot_show(side->knot);
1470         } else {
1471             if (side->knot->pos != side->pos) { // only if it's really moved
1472                 if (fire_move_signals) {
1473                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1474                 } else {
1475                     sp_knot_moveto(side->knot, &side->pos);
1476                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1477                 }
1478             }
1479             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1480                 sp_knot_show(side->knot);
1481             }
1482         }
1483         sp_canvas_item_show(side->line);
1484     } else {
1485         if (side->knot) {
1486             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1487                 sp_knot_hide(side->knot);
1488             }
1489         }
1490         if (side->line) {
1491             sp_canvas_item_hide(side->line);
1492         }
1493     }
1496 /**
1497  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1498  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1499  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1500  * updated; otherwise, just move the knots silently (used in batch moves).
1501  */
1502 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1504     g_assert(node != NULL);
1506     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1507         sp_knot_show(node->knot);
1508     }
1510     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1511         if (fire_move_signals)
1512             sp_knot_set_position(node->knot, &node->pos, 0);
1513         else
1514             sp_knot_moveto(node->knot, &node->pos);
1515     }
1517     gboolean show_handles = node->selected;
1518     if (node->p.other != NULL) {
1519         if (node->p.other->selected) show_handles = TRUE;
1520     }
1521     if (node->n.other != NULL) {
1522         if (node->n.other->selected) show_handles = TRUE;
1523     }
1525     if (node->subpath->nodepath->show_handles == false)
1526         show_handles = FALSE;
1528     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1529     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1532 /**
1533  * Call sp_node_update_handles() for all nodes on subpath.
1534  */
1535 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1537     g_assert(subpath != NULL);
1539     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1540         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1541     }
1544 /**
1545  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1546  */
1547 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1549     g_assert(nodepath != NULL);
1551     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1552         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1553     }
1556 void
1557 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1559     if (nodepath == NULL) return;
1561     nodepath->show_handles = show;
1562     sp_nodepath_update_handles(nodepath);
1565 /**
1566  * Adds all selected nodes in nodepath to list.
1567  */
1568 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1570     StlConv<Node *>::list(l, selected);
1571 /// \todo this adds a copying, rework when the selection becomes a stl list
1574 /**
1575  * Align selected nodes on the specified axis.
1576  */
1577 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1579     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1580         return;
1581     }
1583     if ( !nodepath->selected->next ) { // only one node selected
1584         return;
1585     }
1586    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1587     NR::Point dest(pNode->pos);
1588     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1589         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1590         if (pNode) {
1591             dest[axis] = pNode->pos[axis];
1592             sp_node_moveto(pNode, dest);
1593         }
1594     }
1596     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1599 /// Helper struct.
1600 struct NodeSort
1602    Inkscape::NodePath::Node *_node;
1603     NR::Coord _coord;
1604     /// \todo use vectorof pointers instead of calling copy ctor
1605     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1606         _node(node), _coord(node->pos[axis])
1607     {}
1609 };
1611 static bool operator<(NodeSort const &a, NodeSort const &b)
1613     return (a._coord < b._coord);
1616 /**
1617  * Distribute selected nodes on the specified axis.
1618  */
1619 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1621     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1622         return;
1623     }
1625     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1626         return;
1627     }
1629    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1630     std::vector<NodeSort> sorted;
1631     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1632         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1633         if (pNode) {
1634             NodeSort n(pNode, axis);
1635             sorted.push_back(n);
1636             //dest[axis] = pNode->pos[axis];
1637             //sp_node_moveto(pNode, dest);
1638         }
1639     }
1640     std::sort(sorted.begin(), sorted.end());
1641     unsigned int len = sorted.size();
1642     //overall bboxes span
1643     float dist = (sorted.back()._coord -
1644                   sorted.front()._coord);
1645     //new distance between each bbox
1646     float step = (dist) / (len - 1);
1647     float pos = sorted.front()._coord;
1648     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1649           it < sorted.end();
1650           it ++ )
1651     {
1652         NR::Point dest((*it)._node->pos);
1653         dest[axis] = pos;
1654         sp_node_moveto((*it)._node, dest);
1655         pos += step;
1656     }
1658     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1662 /**
1663  * Call sp_nodepath_line_add_node() for all selected segments.
1664  */
1665 void
1666 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1668     if (!nodepath) {
1669         return;
1670     }
1672     GList *nl = NULL;
1674     int n_added = 0;
1676     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1677        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1678         g_assert(t->selected);
1679         if (t->p.other && t->p.other->selected) {
1680             nl = g_list_prepend(nl, t);
1681         }
1682     }
1684     while (nl) {
1685        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1686        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1687        sp_nodepath_node_select(n, TRUE, FALSE);
1688        n_added ++;
1689        nl = g_list_remove(nl, t);
1690     }
1692     /** \todo fixme: adjust ? */
1693     sp_nodepath_update_handles(nodepath);
1695     if (n_added > 1) {
1696         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1697     } else if (n_added > 0) {
1698         sp_nodepath_update_repr(nodepath, _("Add node"));
1699     }
1701     sp_nodepath_update_statusbar(nodepath);
1704 /**
1705  * Select segment nearest to point
1706  */
1707 void
1708 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1710     if (!nodepath) {
1711         return;
1712     }
1714     sp_nodepath_ensure_livarot_path(nodepath);
1715     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1716     if (!maybe_position) {
1717         return;
1718     }
1719     Path::cut_position position = *maybe_position;
1721     //find segment to segment
1722     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1724     //fixme: this can return NULL, so check before proceeding.
1725     g_return_if_fail(e != NULL);
1727     gboolean force = FALSE;
1728     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1729         force = TRUE;
1730     }
1731     sp_nodepath_node_select(e, (gboolean) toggle, force);
1732     if (e->p.other)
1733         sp_nodepath_node_select(e->p.other, TRUE, force);
1735     sp_nodepath_update_handles(nodepath);
1737     sp_nodepath_update_statusbar(nodepath);
1740 /**
1741  * Add a node nearest to point
1742  */
1743 void
1744 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1746     if (!nodepath) {
1747         return;
1748     }
1750     sp_nodepath_ensure_livarot_path(nodepath);
1751     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1752     if (!maybe_position) {
1753         return;
1754     }
1755     Path::cut_position position = *maybe_position;
1757     //find segment to split
1758     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1760     //don't know why but t seems to flip for lines
1761     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1762         position.t = 1.0 - position.t;
1763     }
1764     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1765     sp_nodepath_node_select(n, FALSE, TRUE);
1767     /* fixme: adjust ? */
1768     sp_nodepath_update_handles(nodepath);
1770     sp_nodepath_update_repr(nodepath, _("Add node"));
1772     sp_nodepath_update_statusbar(nodepath);
1775 /*
1776  * Adjusts a segment so that t moves by a certain delta for dragging
1777  * converts lines to curves
1778  *
1779  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1780  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1781  */
1782 void
1783 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1785     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1787     //fixme: e and e->p can be NULL, so check for those before proceeding
1788     g_return_if_fail(e != NULL);
1789     g_return_if_fail(&e->p != NULL);
1791     /* feel good is an arbitrary parameter that distributes the delta between handles
1792      * if t of the drag point is less than 1/6 distance form the endpoint only
1793      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1794      */
1795     double feel_good;
1796     if (t <= 1.0 / 6.0)
1797         feel_good = 0;
1798     else if (t <= 0.5)
1799         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1800     else if (t <= 5.0 / 6.0)
1801         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1802     else
1803         feel_good = 1;
1805     //if we're dragging a line convert it to a curve
1806     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1807         sp_nodepath_set_line_type(e, NR_CURVETO);
1808     }
1810     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1811     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1812     e->p.other->n.pos += offsetcoord0;
1813     e->p.pos += offsetcoord1;
1815     // adjust handles of adjacent nodes where necessary
1816     sp_node_adjust_handle(e,1);
1817     sp_node_adjust_handle(e->p.other,-1);
1819     sp_nodepath_update_handles(e->subpath->nodepath);
1821     update_object(e->subpath->nodepath);
1823     sp_nodepath_update_statusbar(e->subpath->nodepath);
1827 /**
1828  * Call sp_nodepath_break() for all selected segments.
1829  */
1830 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1832     if (!nodepath) return;
1834     GList *temp = NULL;
1835     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1836        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1837        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1838         if (nn == NULL) continue; // no break, no new node
1839         temp = g_list_prepend(temp, nn);
1840     }
1842     if (temp) {
1843         sp_nodepath_deselect(nodepath);
1844     }
1845     for (GList *l = temp; l != NULL; l = l->next) {
1846         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1847     }
1849     sp_nodepath_update_handles(nodepath);
1851     sp_nodepath_update_repr(nodepath, _("Break path"));
1854 /**
1855  * Duplicate the selected node(s).
1856  */
1857 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1859     if (!nodepath) {
1860         return;
1861     }
1863     GList *temp = NULL;
1864     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1865        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1866        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1867         if (nn == NULL) continue; // could not duplicate
1868         temp = g_list_prepend(temp, nn);
1869     }
1871     if (temp) {
1872         sp_nodepath_deselect(nodepath);
1873     }
1874     for (GList *l = temp; l != NULL; l = l->next) {
1875         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1876     }
1878     sp_nodepath_update_handles(nodepath);
1880     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1883 /**
1884  *  Join two nodes by merging them into one.
1885  */
1886 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
1888     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1890     if (g_list_length(nodepath->selected) != 2) {
1891         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1892         return;
1893     }
1895    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1896    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1898     g_assert(a != b);
1899     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
1900         // someone tried to join an orphan node (i.e. a single-node subpath).
1901         // this is not worth an error message, just fail silently.
1902         return;
1903     }
1905     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1906         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1907         return;
1908     }
1910     /* a and b are endpoints */
1912     NR::Point c;
1913     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1914         c = a->pos;
1915     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1916         c = b->pos;
1917     } else {
1918         c = (a->pos + b->pos) / 2;
1919     }
1921     if (a->subpath == b->subpath) {
1922        Inkscape::NodePath::SubPath *sp = a->subpath;
1923         sp_nodepath_subpath_close(sp);
1924         sp_node_moveto (sp->first, c);
1926         sp_nodepath_update_handles(sp->nodepath);
1927         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1928         return;
1929     }
1931     /* a and b are separate subpaths */
1932    Inkscape::NodePath::SubPath *sa = a->subpath;
1933    Inkscape::NodePath::SubPath *sb = b->subpath;
1934     NR::Point p;
1935    Inkscape::NodePath::Node *n;
1936     NRPathcode code;
1937     if (a == sa->first) {
1938         p = sa->first->n.pos;
1939         code = (NRPathcode)sa->first->n.other->code;
1940        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1941         n = sa->last;
1942         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1943         n = n->p.other;
1944         while (n) {
1945             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1946             n = n->p.other;
1947             if (n == sa->first) n = NULL;
1948         }
1949         sp_nodepath_subpath_destroy(sa);
1950         sa = t;
1951     } else if (a == sa->last) {
1952         p = sa->last->p.pos;
1953         code = (NRPathcode)sa->last->code;
1954         sp_nodepath_node_destroy(sa->last);
1955     } else {
1956         code = NR_END;
1957         g_assert_not_reached();
1958     }
1960     if (b == sb->first) {
1961         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1962         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1963             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1964         }
1965     } else if (b == sb->last) {
1966         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1967         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1968             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1969         }
1970     } else {
1971         g_assert_not_reached();
1972     }
1973     /* and now destroy sb */
1975     sp_nodepath_subpath_destroy(sb);
1977     sp_nodepath_update_handles(sa->nodepath);
1979     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1981     sp_nodepath_update_statusbar(nodepath);
1984 /**
1985  *  Join two nodes by adding a segment between them.
1986  */
1987 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
1989     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1991     if (g_list_length(nodepath->selected) != 2) {
1992         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1993         return;
1994     }
1996    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1997    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1999     g_assert(a != b);
2000     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2001         // someone tried to join an orphan node (i.e. a single-node subpath).
2002         // this is not worth an error message, just fail silently.
2003         return;
2004     }
2006     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2007         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2008         return;
2009     }
2011     if (a->subpath == b->subpath) {
2012        Inkscape::NodePath::SubPath *sp = a->subpath;
2014         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2015         sp->closed = TRUE;
2017         sp->first->p.other = sp->last;
2018         sp->last->n.other  = sp->first;
2020         sp_node_handle_mirror_p_to_n(sp->last);
2021         sp_node_handle_mirror_n_to_p(sp->first);
2023         sp->first->code = sp->last->code;
2024         sp->first       = sp->last;
2026         sp_nodepath_update_handles(sp->nodepath);
2028         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2030         return;
2031     }
2033     /* a and b are separate subpaths */
2034    Inkscape::NodePath::SubPath *sa = a->subpath;
2035    Inkscape::NodePath::SubPath *sb = b->subpath;
2037    Inkscape::NodePath::Node *n;
2038     NR::Point p;
2039     NRPathcode code;
2040     if (a == sa->first) {
2041         code = (NRPathcode) sa->first->n.other->code;
2042        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2043         n = sa->last;
2044         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2045         for (n = n->p.other; n != NULL; n = n->p.other) {
2046             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2047         }
2048         sp_nodepath_subpath_destroy(sa);
2049         sa = t;
2050     } else if (a == sa->last) {
2051         code = (NRPathcode)sa->last->code;
2052     } else {
2053         code = NR_END;
2054         g_assert_not_reached();
2055     }
2057     if (b == sb->first) {
2058         n = sb->first;
2059         sp_node_handle_mirror_p_to_n(sa->last);
2060         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2061         sp_node_handle_mirror_n_to_p(sa->last);
2062         for (n = n->n.other; n != NULL; n = n->n.other) {
2063             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2064         }
2065     } else if (b == sb->last) {
2066         n = sb->last;
2067         sp_node_handle_mirror_p_to_n(sa->last);
2068         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2069         sp_node_handle_mirror_n_to_p(sa->last);
2070         for (n = n->p.other; n != NULL; n = n->p.other) {
2071             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2072         }
2073     } else {
2074         g_assert_not_reached();
2075     }
2076     /* and now destroy sb */
2078     sp_nodepath_subpath_destroy(sb);
2080     sp_nodepath_update_handles(sa->nodepath);
2082     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2085 /**
2086  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2087  */
2088 void sp_node_delete_preserve(GList *nodes_to_delete)
2090     GSList *nodepaths = NULL;
2092     while (nodes_to_delete) {
2093         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2094         Inkscape::NodePath::SubPath *sp = node->subpath;
2095         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2096         Inkscape::NodePath::Node *sample_cursor = NULL;
2097         Inkscape::NodePath::Node *sample_end = NULL;
2098         Inkscape::NodePath::Node *delete_cursor = node;
2099         bool just_delete = false;
2101         //find the start of this contiguous selection
2102         //move left to the first node that is not selected
2103         //or the start of the non-closed path
2104         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2105             delete_cursor = curr;
2106         }
2108         //just delete at the beginning of an open path
2109         if (!delete_cursor->p.other) {
2110             sample_cursor = delete_cursor;
2111             just_delete = true;
2112         } else {
2113             sample_cursor = delete_cursor->p.other;
2114         }
2116         //calculate points for each segment
2117         int rate = 5;
2118         float period = 1.0 / rate;
2119         std::vector<NR::Point> data;
2120         if (!just_delete) {
2121             data.push_back(sample_cursor->pos);
2122             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2123                 //just delete at the end of an open path
2124                 if (!sp->closed && curr == sp->last) {
2125                     just_delete = true;
2126                     break;
2127                 }
2129                 //sample points on the contiguous selected segment
2130                 NR::Point *bez;
2131                 bez = new NR::Point [4];
2132                 bez[0] = curr->pos;
2133                 bez[1] = curr->n.pos;
2134                 bez[2] = curr->n.other->p.pos;
2135                 bez[3] = curr->n.other->pos;
2136                 for (int i=1; i<rate; i++) {
2137                     gdouble t = i * period;
2138                     NR::Point p = bezier_pt(3, bez, t);
2139                     data.push_back(p);
2140                 }
2141                 data.push_back(curr->n.other->pos);
2143                 sample_end = curr->n.other;
2144                 //break if we've come full circle or hit the end of the selection
2145                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2146                     break;
2147                 }
2148             }
2149         }
2151         if (!just_delete) {
2152             //calculate the best fitting single segment and adjust the endpoints
2153             NR::Point *adata;
2154             adata = new NR::Point [data.size()];
2155             copy(data.begin(), data.end(), adata);
2157             NR::Point *bez;
2158             bez = new NR::Point [4];
2159             //would decreasing error create a better fitting approximation?
2160             gdouble error = 1.0;
2161             gint ret;
2162             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2164             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2165             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2166             //the resulting nodes behave as expected.
2167             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2168             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2170             //adjust endpoints
2171             sample_cursor->n.pos = bez[1];
2172             sample_end->p.pos = bez[2];
2173         }
2175         //destroy this contiguous selection
2176         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2177             Inkscape::NodePath::Node *temp = delete_cursor;
2178             if (delete_cursor->n.other == delete_cursor) {
2179                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2180                 delete_cursor = NULL;
2181             } else {
2182                 delete_cursor = delete_cursor->n.other;
2183             }
2184             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2185             sp_nodepath_node_destroy(temp);
2186         }
2188         sp_nodepath_update_handles(nodepath);
2190         if (!g_slist_find(nodepaths, nodepath))
2191             nodepaths = g_slist_prepend (nodepaths, nodepath);
2192     }
2194     for (GSList *i = nodepaths; i; i = i->next) {
2195         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2196         // different nodepaths will give us one undo event per nodepath
2197         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2199         // if the entire nodepath is removed, delete the selected object.
2200         if (nodepath->subpaths == NULL ||
2201             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2202             //at least 2
2203             sp_nodepath_get_node_count(nodepath) < 2) {
2204             SPDocument *document = sp_desktop_document (nodepath->desktop);
2205             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2206             //delete this nodepath's object, not the entire selection! (though at this time, this
2207             //does not matter)
2208             sp_selection_delete();
2209             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2210                               _("Delete nodes"));
2211         } else {
2212             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2213             sp_nodepath_update_statusbar(nodepath);
2214         }
2215     }
2217     g_slist_free (nodepaths);
2220 /**
2221  * Delete one or more selected nodes.
2222  */
2223 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2225     if (!nodepath) return;
2226     if (!nodepath->selected) return;
2228     /** \todo fixme: do it the right way */
2229     while (nodepath->selected) {
2230        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2231         sp_nodepath_node_destroy(node);
2232     }
2235     //clean up the nodepath (such as for trivial subpaths)
2236     sp_nodepath_cleanup(nodepath);
2238     sp_nodepath_update_handles(nodepath);
2240     // if the entire nodepath is removed, delete the selected object.
2241     if (nodepath->subpaths == NULL ||
2242         sp_nodepath_get_node_count(nodepath) < 2) {
2243         SPDocument *document = sp_desktop_document (nodepath->desktop);
2244         sp_selection_delete();
2245         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2246                           _("Delete nodes"));
2247         return;
2248     }
2250     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2252     sp_nodepath_update_statusbar(nodepath);
2255 /**
2256  * Delete one or more segments between two selected nodes.
2257  * This is the code for 'split'.
2258  */
2259 void
2260 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2262    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2263    Inkscape::NodePath::Node *curr, *next;     //Iterators
2265     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2267     if (g_list_length(nodepath->selected) != 2) {
2268         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2269                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2270         return;
2271     }
2273     //Selected nodes, not inclusive
2274    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2275    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2277     if ( ( a==b)                       ||  //same node
2278          (a->subpath  != b->subpath )  ||  //not the same path
2279          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2280          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2281     {
2282         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2283                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2284         return;
2285     }
2287     //###########################################
2288     //# BEGIN EDITS
2289     //###########################################
2290     //##################################
2291     //# CLOSED PATH
2292     //##################################
2293     if (a->subpath->closed) {
2296         gboolean reversed = FALSE;
2298         //Since we can go in a circle, we need to find the shorter distance.
2299         //  a->b or b->a
2300         start = end = NULL;
2301         int distance    = 0;
2302         int minDistance = 0;
2303         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2304             if (curr==b) {
2305                 //printf("a to b:%d\n", distance);
2306                 start = a;//go from a to b
2307                 end   = b;
2308                 minDistance = distance;
2309                 //printf("A to B :\n");
2310                 break;
2311             }
2312             distance++;
2313         }
2315         //try again, the other direction
2316         distance = 0;
2317         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2318             if (curr==a) {
2319                 //printf("b to a:%d\n", distance);
2320                 if (distance < minDistance) {
2321                     start    = b;  //we go from b to a
2322                     end      = a;
2323                     reversed = TRUE;
2324                     //printf("B to A\n");
2325                 }
2326                 break;
2327             }
2328             distance++;
2329         }
2332         //Copy everything from 'end' to 'start' to a new subpath
2333        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2334         for (curr=end ; curr ; curr=curr->n.other) {
2335             NRPathcode code = (NRPathcode) curr->code;
2336             if (curr == end)
2337                 code = NR_MOVETO;
2338             sp_nodepath_node_new(t, NULL,
2339                                  (Inkscape::NodePath::NodeType)curr->type, code,
2340                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2341             if (curr == start)
2342                 break;
2343         }
2344         sp_nodepath_subpath_destroy(a->subpath);
2347     }
2351     //##################################
2352     //# OPEN PATH
2353     //##################################
2354     else {
2356         //We need to get the direction of the list between A and B
2357         //Can we walk from a to b?
2358         start = end = NULL;
2359         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2360             if (curr==b) {
2361                 start = a;  //did it!  we go from a to b
2362                 end   = b;
2363                 //printf("A to B\n");
2364                 break;
2365             }
2366         }
2367         if (!start) {//didn't work?  let's try the other direction
2368             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2369                 if (curr==a) {
2370                     start = b;  //did it!  we go from b to a
2371                     end   = a;
2372                     //printf("B to A\n");
2373                     break;
2374                 }
2375             }
2376         }
2377         if (!start) {
2378             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2379                                                      _("Cannot find path between nodes."));
2380             return;
2381         }
2385         //Copy everything after 'end' to a new subpath
2386        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2387         for (curr=end ; curr ; curr=curr->n.other) {
2388             NRPathcode code = (NRPathcode) curr->code;
2389             if (curr == end)
2390                 code = NR_MOVETO;
2391             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2392                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2393         }
2395         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2396         for (curr = start->n.other ; curr  ; curr=next) {
2397             next = curr->n.other;
2398             sp_nodepath_node_destroy(curr);
2399         }
2401     }
2402     //###########################################
2403     //# END EDITS
2404     //###########################################
2406     //clean up the nodepath (such as for trivial subpaths)
2407     sp_nodepath_cleanup(nodepath);
2409     sp_nodepath_update_handles(nodepath);
2411     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2413     sp_nodepath_update_statusbar(nodepath);
2416 /**
2417  * Call sp_nodepath_set_line() for all selected segments.
2418  */
2419 void
2420 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2422     if (nodepath == NULL) return;
2424     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2425        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2426         g_assert(n->selected);
2427         if (n->p.other && n->p.other->selected) {
2428             sp_nodepath_set_line_type(n, code);
2429         }
2430     }
2432     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2435 /**
2436  * Call sp_nodepath_convert_node_type() for all selected nodes.
2437  */
2438 void
2439 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2441     if (nodepath == NULL) return;
2443     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2445     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2446         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2447     }
2449     sp_nodepath_update_repr(nodepath, _("Change node type"));
2452 /**
2453  * Change select status of node, update its own and neighbour handles.
2454  */
2455 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2457     node->selected = selected;
2459     if (selected) {
2460         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2461         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2462         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2463         sp_knot_update_ctrl(node->knot);
2464     } else {
2465         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2466         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2467         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2468         sp_knot_update_ctrl(node->knot);
2469     }
2471     sp_node_update_handles(node);
2472     if (node->n.other) sp_node_update_handles(node->n.other);
2473     if (node->p.other) sp_node_update_handles(node->p.other);
2476 /**
2477 \brief Select a node
2478 \param node     The node to select
2479 \param incremental   If true, add to selection, otherwise deselect others
2480 \param override   If true, always select this node, otherwise toggle selected status
2481 */
2482 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2484     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2486     if (incremental) {
2487         if (override) {
2488             if (!g_list_find(nodepath->selected, node)) {
2489                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2490             }
2491             sp_node_set_selected(node, TRUE);
2492         } else { // toggle
2493             if (node->selected) {
2494                 g_assert(g_list_find(nodepath->selected, node));
2495                 nodepath->selected = g_list_remove(nodepath->selected, node);
2496             } else {
2497                 g_assert(!g_list_find(nodepath->selected, node));
2498                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2499             }
2500             sp_node_set_selected(node, !node->selected);
2501         }
2502     } else {
2503         sp_nodepath_deselect(nodepath);
2504         nodepath->selected = g_list_prepend(nodepath->selected, node);
2505         sp_node_set_selected(node, TRUE);
2506     }
2508     sp_nodepath_update_statusbar(nodepath);
2512 /**
2513 \brief Deselect all nodes in the nodepath
2514 */
2515 void
2516 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2518     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2520     while (nodepath->selected) {
2521         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2522         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2523     }
2524     sp_nodepath_update_statusbar(nodepath);
2527 /**
2528 \brief Select or invert selection of all nodes in the nodepath
2529 */
2530 void
2531 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2533     if (!nodepath) return;
2535     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2536        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2537         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2538            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2539            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2540         }
2541     }
2544 /**
2545  * If nothing selected, does the same as sp_nodepath_select_all();
2546  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2547  * (i.e., similar to "select all in layer", with the "selected" subpaths
2548  * being treated as "layers" in the path).
2549  */
2550 void
2551 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2553     if (!nodepath) return;
2555     if (g_list_length (nodepath->selected) == 0) {
2556         sp_nodepath_select_all (nodepath, invert);
2557         return;
2558     }
2560     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2561     GSList *subpaths = NULL;
2563     for (GList *l = copy; l != NULL; l = l->next) {
2564         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2565         Inkscape::NodePath::SubPath *subpath = n->subpath;
2566         if (!g_slist_find (subpaths, subpath))
2567             subpaths = g_slist_prepend (subpaths, subpath);
2568     }
2570     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2571         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2572         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2573             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2574             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2575         }
2576     }
2578     g_slist_free (subpaths);
2579     g_list_free (copy);
2582 /**
2583  * \brief Select the node after the last selected; if none is selected,
2584  * select the first within path.
2585  */
2586 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2588     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2590    Inkscape::NodePath::Node *last = NULL;
2591     if (nodepath->selected) {
2592         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2593            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2594             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2595             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2596                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2597                 if (node->selected) {
2598                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2599                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2600                             if (spl->next) { // there's a next subpath
2601                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2602                                 last = subpath_next->first;
2603                             } else if (spl->prev) { // there's a previous subpath
2604                                 last = NULL; // to be set later to the first node of first subpath
2605                             } else {
2606                                 last = node->n.other;
2607                             }
2608                         } else {
2609                             last = node->n.other;
2610                         }
2611                     } else {
2612                         if (node->n.other) {
2613                             last = node->n.other;
2614                         } else {
2615                             if (spl->next) { // there's a next subpath
2616                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2617                                 last = subpath_next->first;
2618                             } else if (spl->prev) { // there's a previous subpath
2619                                 last = NULL; // to be set later to the first node of first subpath
2620                             } else {
2621                                 last = (Inkscape::NodePath::Node *) subpath->first;
2622                             }
2623                         }
2624                     }
2625                 }
2626             }
2627         }
2628         sp_nodepath_deselect(nodepath);
2629     }
2631     if (last) { // there's at least one more node after selected
2632         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2633     } else { // no more nodes, select the first one in first subpath
2634        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2635         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2636     }
2639 /**
2640  * \brief Select the node before the first selected; if none is selected,
2641  * select the last within path
2642  */
2643 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2645     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2647    Inkscape::NodePath::Node *last = NULL;
2648     if (nodepath->selected) {
2649         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2650            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2651             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2652                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2653                 if (node->selected) {
2654                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2655                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2656                             if (spl->prev) { // there's a prev subpath
2657                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2658                                 last = subpath_prev->last;
2659                             } else if (spl->next) { // there's a next subpath
2660                                 last = NULL; // to be set later to the last node of last subpath
2661                             } else {
2662                                 last = node->p.other;
2663                             }
2664                         } else {
2665                             last = node->p.other;
2666                         }
2667                     } else {
2668                         if (node->p.other) {
2669                             last = node->p.other;
2670                         } else {
2671                             if (spl->prev) { // there's a prev subpath
2672                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2673                                 last = subpath_prev->last;
2674                             } else if (spl->next) { // there's a next subpath
2675                                 last = NULL; // to be set later to the last node of last subpath
2676                             } else {
2677                                 last = (Inkscape::NodePath::Node *) subpath->last;
2678                             }
2679                         }
2680                     }
2681                 }
2682             }
2683         }
2684         sp_nodepath_deselect(nodepath);
2685     }
2687     if (last) { // there's at least one more node before selected
2688         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2689     } else { // no more nodes, select the last one in last subpath
2690         GList *spl = g_list_last(nodepath->subpaths);
2691        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2692         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2693     }
2696 /**
2697  * \brief Select all nodes that are within the rectangle.
2698  */
2699 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2701     if (!incremental) {
2702         sp_nodepath_deselect(nodepath);
2703     }
2705     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2706        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2707         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2708            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2710             if (b.contains(node->pos)) {
2711                 sp_nodepath_node_select(node, TRUE, TRUE);
2712             }
2713         }
2714     }
2718 void
2719 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2721     g_assert (n);
2722     g_assert (nodepath);
2723     g_assert (n->subpath->nodepath == nodepath);
2725     if (g_list_length (nodepath->selected) == 0) {
2726         if (grow > 0) {
2727             sp_nodepath_node_select(n, TRUE, TRUE);
2728         }
2729         return;
2730     }
2732     if (g_list_length (nodepath->selected) == 1) {
2733         if (grow < 0) {
2734             sp_nodepath_deselect (nodepath);
2735             return;
2736         }
2737     }
2739         double n_sel_range = 0, p_sel_range = 0;
2740             Inkscape::NodePath::Node *farthest_n_node = n;
2741             Inkscape::NodePath::Node *farthest_p_node = n;
2743         // Calculate ranges
2744         {
2745             double n_range = 0, p_range = 0;
2746             bool n_going = true, p_going = true;
2747             Inkscape::NodePath::Node *n_node = n;
2748             Inkscape::NodePath::Node *p_node = n;
2749             do {
2750                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2751                 if (n_node && n_going)
2752                     n_node = n_node->n.other;
2753                 if (n_node == NULL) {
2754                     n_going = false;
2755                 } else {
2756                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2757                     if (n_node->selected) {
2758                         n_sel_range = n_range;
2759                         farthest_n_node = n_node;
2760                     }
2761                     if (n_node == p_node) {
2762                         n_going = false;
2763                         p_going = false;
2764                     }
2765                 }
2766                 if (p_node && p_going)
2767                     p_node = p_node->p.other;
2768                 if (p_node == NULL) {
2769                     p_going = false;
2770                 } else {
2771                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2772                     if (p_node->selected) {
2773                         p_sel_range = p_range;
2774                         farthest_p_node = p_node;
2775                     }
2776                     if (p_node == n_node) {
2777                         n_going = false;
2778                         p_going = false;
2779                     }
2780                 }
2781             } while (n_going || p_going);
2782         }
2784     if (grow > 0) {
2785         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2786                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2787         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2788                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2789         }
2790     } else {
2791         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2792                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2793         } else if (farthest_p_node && farthest_p_node->selected) {
2794                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2795         }
2796     }
2799 void
2800 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2802     g_assert (n);
2803     g_assert (nodepath);
2804     g_assert (n->subpath->nodepath == nodepath);
2806     if (g_list_length (nodepath->selected) == 0) {
2807         if (grow > 0) {
2808             sp_nodepath_node_select(n, TRUE, TRUE);
2809         }
2810         return;
2811     }
2813     if (g_list_length (nodepath->selected) == 1) {
2814         if (grow < 0) {
2815             sp_nodepath_deselect (nodepath);
2816             return;
2817         }
2818     }
2820     Inkscape::NodePath::Node *farthest_selected = NULL;
2821     double farthest_dist = 0;
2823     Inkscape::NodePath::Node *closest_unselected = NULL;
2824     double closest_dist = NR_HUGE;
2826     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2827        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2828         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2829            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2830            if (node == n)
2831                continue;
2832            if (node->selected) {
2833                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2834                    farthest_dist = NR::L2(node->pos - n->pos);
2835                    farthest_selected = node;
2836                }
2837            } else {
2838                if (NR::L2(node->pos - n->pos) < closest_dist) {
2839                    closest_dist = NR::L2(node->pos - n->pos);
2840                    closest_unselected = node;
2841                }
2842            }
2843         }
2844     }
2846     if (grow > 0) {
2847         if (closest_unselected) {
2848             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2849         }
2850     } else {
2851         if (farthest_selected) {
2852             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2853         }
2854     }
2858 /**
2859 \brief  Saves all nodes' and handles' current positions in their origin members
2860 */
2861 void
2862 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2864     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2865        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2866         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2867            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2868            n->origin = n->pos;
2869            n->p.origin = n->p.pos;
2870            n->n.origin = n->n.pos;
2871         }
2872     }
2875 /**
2876 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2877 */
2878 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2880     if (!nodepath->selected) {
2881         return NULL;
2882     }
2884     GList *r = NULL;
2885     guint i = 0;
2886     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2887        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2888         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2889            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2890             i++;
2891             if (node->selected) {
2892                 r = g_list_append(r, GINT_TO_POINTER(i));
2893             }
2894         }
2895     }
2896     return r;
2899 /**
2900 \brief  Restores selection by selecting nodes whose positions are in the list
2901 */
2902 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2904     sp_nodepath_deselect(nodepath);
2906     guint i = 0;
2907     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2908        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2909         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2910            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2911             i++;
2912             if (g_list_find(r, GINT_TO_POINTER(i))) {
2913                 sp_nodepath_node_select(node, TRUE, TRUE);
2914             }
2915         }
2916     }
2920 /**
2921 \brief Adjusts handle according to node type and line code.
2922 */
2923 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2925     double len, otherlen, linelen;
2927     g_assert(node);
2929    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2930    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2932     /** \todo fixme: */
2933     if (me->other == NULL) return;
2934     if (other->other == NULL) return;
2936     /* I have line */
2938     NRPathcode mecode, ocode;
2939     if (which_adjust == 1) {
2940         mecode = (NRPathcode)me->other->code;
2941         ocode = (NRPathcode)node->code;
2942     } else {
2943         mecode = (NRPathcode)node->code;
2944         ocode = (NRPathcode)other->other->code;
2945     }
2947     if (mecode == NR_LINETO) return;
2949     /* I am curve */
2951     if (other->other == NULL) return;
2953     /* Other has line */
2955     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2957     NR::Point delta;
2958     if (ocode == NR_LINETO) {
2959         /* other is lineto, we are either smooth or symm */
2960        Inkscape::NodePath::Node *othernode = other->other;
2961         len = NR::L2(me->pos - node->pos);
2962         delta = node->pos - othernode->pos;
2963         linelen = NR::L2(delta);
2964         if (linelen < 1e-18)
2965             return;
2966         me->pos = node->pos + (len / linelen)*delta;
2967         return;
2968     }
2970     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2972         me->pos = 2 * node->pos - other->pos;
2973         return;
2974     }
2976     /* We are smooth */
2978     len = NR::L2(me->pos - node->pos);
2979     delta = other->pos - node->pos;
2980     otherlen = NR::L2(delta);
2981     if (otherlen < 1e-18) return;
2983     me->pos = node->pos - (len / otherlen) * delta;
2986 /**
2987  \brief Adjusts both handles according to node type and line code
2988  */
2989 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2991     g_assert(node);
2993     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2995     /* we are either smooth or symm */
2997     if (node->p.other == NULL) return;
2999     if (node->n.other == NULL) return;
3001     if (node->code == NR_LINETO) {
3002         if (node->n.other->code == NR_LINETO) return;
3003         sp_node_adjust_handle(node, 1);
3004         return;
3005     }
3007     if (node->n.other->code == NR_LINETO) {
3008         if (node->code == NR_LINETO) return;
3009         sp_node_adjust_handle(node, -1);
3010         return;
3011     }
3013     /* both are curves */
3014     NR::Point const delta( node->n.pos - node->p.pos );
3016     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3017         node->p.pos = node->pos - delta / 2;
3018         node->n.pos = node->pos + delta / 2;
3019         return;
3020     }
3022     /* We are smooth */
3023     double plen = NR::L2(node->p.pos - node->pos);
3024     if (plen < 1e-18) return;
3025     double nlen = NR::L2(node->n.pos - node->pos);
3026     if (nlen < 1e-18) return;
3027     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3028     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3031 /**
3032  * Node event callback.
3033  */
3034 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3036     gboolean ret = FALSE;
3037     switch (event->type) {
3038         case GDK_ENTER_NOTIFY:
3039             Inkscape::NodePath::Path::active_node = n;
3040             break;
3041         case GDK_LEAVE_NOTIFY:
3042             Inkscape::NodePath::Path::active_node = NULL;
3043             break;
3044         case GDK_SCROLL:
3045             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3046                 switch (event->scroll.direction) {
3047                     case GDK_SCROLL_UP:
3048                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3049                         break;
3050                     case GDK_SCROLL_DOWN:
3051                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3052                         break;
3053                     default:
3054                         break;
3055                 }
3056                 ret = TRUE;
3057             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3058                 switch (event->scroll.direction) {
3059                     case GDK_SCROLL_UP:
3060                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3061                         break;
3062                     case GDK_SCROLL_DOWN:
3063                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3064                         break;
3065                     default:
3066                         break;
3067                 }
3068                 ret = TRUE;
3069             }
3070             break;
3071         case GDK_KEY_PRESS:
3072             switch (get_group0_keyval (&event->key)) {
3073                 case GDK_space:
3074                     if (event->key.state & GDK_BUTTON1_MASK) {
3075                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3076                         stamp_repr(nodepath);
3077                         ret = TRUE;
3078                     }
3079                     break;
3080                 case GDK_Page_Up:
3081                     if (event->key.state & GDK_CONTROL_MASK) {
3082                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3083                     } else {
3084                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3085                     }
3086                     break;
3087                 case GDK_Page_Down:
3088                     if (event->key.state & GDK_CONTROL_MASK) {
3089                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3090                     } else {
3091                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3092                     }
3093                     break;
3094                 default:
3095                     break;
3096             }
3097             break;
3098         default:
3099             break;
3100     }
3102     return ret;
3105 /**
3106  * Handle keypress on node; directly called.
3107  */
3108 gboolean node_key(GdkEvent *event)
3110     Inkscape::NodePath::Path *np;
3112     // there is no way to verify nodes so set active_node to nil when deleting!!
3113     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3115     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3116         gint ret = FALSE;
3117         switch (get_group0_keyval (&event->key)) {
3118             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3119             case GDK_BackSpace:
3120                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3121                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3122                 sp_nodepath_update_repr(np, _("Delete node"));
3123                 Inkscape::NodePath::Path::active_node = NULL;
3124                 ret = TRUE;
3125                 break;
3126             case GDK_c:
3127                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3128                 ret = TRUE;
3129                 break;
3130             case GDK_s:
3131                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3132                 ret = TRUE;
3133                 break;
3134             case GDK_y:
3135                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3136                 ret = TRUE;
3137                 break;
3138             case GDK_b:
3139                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3140                 ret = TRUE;
3141                 break;
3142         }
3143         return ret;
3144     }
3145     return FALSE;
3148 /**
3149  * Mouseclick on node callback.
3150  */
3151 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3153    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3155     if (state & GDK_CONTROL_MASK) {
3156         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3158         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3159             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3160                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3161             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3162                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3163             } else {
3164                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3165             }
3166             sp_nodepath_update_repr(nodepath, _("Change node type"));
3167             sp_nodepath_update_statusbar(nodepath);
3169         } else { //ctrl+alt+click: delete node
3170             GList *node_to_delete = NULL;
3171             node_to_delete = g_list_append(node_to_delete, n);
3172             sp_node_delete_preserve(node_to_delete);
3173         }
3175     } else {
3176         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3177     }
3180 /**
3181  * Mouse grabbed node callback.
3182  */
3183 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3185    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3187     if (!n->selected) {
3188         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3189     }
3191     n->is_dragging = true;
3192     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3194     sp_nodepath_remember_origins (n->subpath->nodepath);
3197 /**
3198  * Mouse ungrabbed node callback.
3199  */
3200 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3202    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3204    n->dragging_out = NULL;
3205    n->is_dragging = false;
3206    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3208    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3211 /**
3212  * The point on a line, given by its angle, closest to the given point.
3213  * \param p  A point.
3214  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3215  * \param closest  Pointer to the point struct where the result is stored.
3216  * \todo FIXME: use dot product perhaps?
3217  */
3218 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3220     if (a == HUGE_VAL) { // vertical
3221         *closest = NR::Point(0, (*p)[NR::Y]);
3222     } else {
3223         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3224         (*closest)[NR::Y] = a * (*closest)[NR::X];
3225     }
3228 /**
3229  * Distance from the point to a line given by its angle.
3230  * \param p  A point.
3231  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3232  */
3233 static double point_line_distance(NR::Point *p, double a)
3235     NR::Point c;
3236     point_line_closest(p, a, &c);
3237     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]));
3240 /**
3241  * Callback for node "request" signal.
3242  * \todo fixme: This goes to "moved" event? (lauris)
3243  */
3244 static gboolean
3245 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3247     double yn, xn, yp, xp;
3248     double an, ap, na, pa;
3249     double d_an, d_ap, d_na, d_pa;
3250     gboolean collinear = FALSE;
3251     NR::Point c;
3252     NR::Point pr;
3254    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3256    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3257     if ( (!n->subpath->nodepath->straight_path) &&
3258          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3259            || n->dragging_out ) )
3260     {
3261        NR::Point mouse = (*p);
3263        if (!n->dragging_out) {
3264            // This is the first drag-out event; find out which handle to drag out
3265            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3266            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3268            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3269                return FALSE;
3271            Inkscape::NodePath::NodeSide *opposite;
3272            if (appr_p > appr_n) { // closer to p
3273                n->dragging_out = &n->p;
3274                opposite = &n->n;
3275                n->code = NR_CURVETO;
3276            } else if (appr_p < appr_n) { // closer to n
3277                n->dragging_out = &n->n;
3278                opposite = &n->p;
3279                n->n.other->code = NR_CURVETO;
3280            } else { // p and n nodes are the same
3281                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3282                    n->dragging_out = &n->p;
3283                    opposite = &n->n;
3284                    n->code = NR_CURVETO;
3285                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3286                    n->dragging_out = &n->n;
3287                    opposite = &n->p;
3288                    n->n.other->code = NR_CURVETO;
3289                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3290                    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);
3291                    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);
3292                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3293                        n->dragging_out = &n->n;
3294                        opposite = &n->p;
3295                        n->n.other->code = NR_CURVETO;
3296                    } else { // closer to other's n handle
3297                        n->dragging_out = &n->p;
3298                        opposite = &n->n;
3299                        n->code = NR_CURVETO;
3300                    }
3301                }
3302            }
3304            // if there's another handle, make sure the one we drag out starts parallel to it
3305            if (opposite->pos != n->pos) {
3306                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3307            }
3309            // knots might not be created yet!
3310            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3311            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3312        }
3314        // pass this on to the handle-moved callback
3315        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3316        sp_node_update_handles(n);
3317        return TRUE;
3318    }
3320     if (state & GDK_CONTROL_MASK) { // constrained motion
3322         // calculate relative distances of handles
3323         // n handle:
3324         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3325         xn = n->n.pos[NR::X] - n->pos[NR::X];
3326         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3327         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3328             if (n->n.other) { // if there is the next point
3329                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3330                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3331                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3332             }
3333         }
3334         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3335         if (yn < 0) { xn = -xn; yn = -yn; }
3337         // p handle:
3338         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3339         xp = n->p.pos[NR::X] - n->pos[NR::X];
3340         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3341         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3342             if (n->p.other) {
3343                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3344                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3345                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3346             }
3347         }
3348         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3349         if (yp < 0) { xp = -xp; yp = -yp; }
3351         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3352             // sliding on handles, only if at least one of the handles is non-vertical
3353             // (otherwise it's the same as ctrl+drag anyway)
3355             // calculate angles of the handles
3356             if (xn == 0) {
3357                 if (yn == 0) { // no handle, consider it the continuation of the other one
3358                     an = 0;
3359                     collinear = TRUE;
3360                 }
3361                 else an = 0; // vertical; set the angle to horizontal
3362             } else an = yn/xn;
3364             if (xp == 0) {
3365                 if (yp == 0) { // no handle, consider it the continuation of the other one
3366                     ap = an;
3367                 }
3368                 else ap = 0; // vertical; set the angle to horizontal
3369             } else  ap = yp/xp;
3371             if (collinear) an = ap;
3373             // angles of the perpendiculars; HUGE_VAL means vertical
3374             if (an == 0) na = HUGE_VAL; else na = -1/an;
3375             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3377             // mouse point relative to the node's original pos
3378             pr = (*p) - n->origin;
3380             // distances to the four lines (two handles and two perpendiculars)
3381             d_an = point_line_distance(&pr, an);
3382             d_na = point_line_distance(&pr, na);
3383             d_ap = point_line_distance(&pr, ap);
3384             d_pa = point_line_distance(&pr, pa);
3386             // find out which line is the closest, save its closest point in c
3387             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3388                 point_line_closest(&pr, an, &c);
3389             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3390                 point_line_closest(&pr, ap, &c);
3391             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3392                 point_line_closest(&pr, na, &c);
3393             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3394                 point_line_closest(&pr, pa, &c);
3395             }
3397             // move the node to the closest point
3398             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3399                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3400                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3402         } else {  // constraining to hor/vert
3404             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3405                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3406             } else { // snap to vert
3407                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3408             }
3409         }
3410     } else { // move freely
3411         if (n->is_dragging) {
3412             if (state & GDK_MOD1_MASK) { // sculpt
3413                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3414             } else {
3415                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3416                                             (*p)[NR::X] - n->pos[NR::X],
3417                                             (*p)[NR::Y] - n->pos[NR::Y],
3418                                             (state & GDK_SHIFT_MASK) == 0);
3419             }
3420         }
3421     }
3423     n->subpath->nodepath->desktop->scroll_to_point(p);
3425     return TRUE;
3428 /**
3429  * Node handle clicked callback.
3430  */
3431 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3433    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3435     if (state & GDK_CONTROL_MASK) { // "delete" handle
3436         if (n->p.knot == knot) {
3437             n->p.pos = n->pos;
3438         } else if (n->n.knot == knot) {
3439             n->n.pos = n->pos;
3440         }
3441         sp_node_update_handles(n);
3442         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3443         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3444         sp_nodepath_update_statusbar(nodepath);
3446     } else { // just select or add to selection, depending in Shift
3447         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3448     }
3451 /**
3452  * Node handle grabbed callback.
3453  */
3454 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3456    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3458     if (!n->selected) {
3459         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3460     }
3462     // remember the origin point of the handle
3463     if (n->p.knot == knot) {
3464         n->p.origin_radial = n->p.pos - n->pos;
3465     } else if (n->n.knot == knot) {
3466         n->n.origin_radial = n->n.pos - n->pos;
3467     } else {
3468         g_assert_not_reached();
3469     }
3471     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3474 /**
3475  * Node handle ungrabbed callback.
3476  */
3477 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3479    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3481     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3482     if (n->p.knot == knot) {
3483         n->p.origin_radial.a = 0;
3484         sp_knot_set_position(knot, &n->p.pos, state);
3485     } else if (n->n.knot == knot) {
3486         n->n.origin_radial.a = 0;
3487         sp_knot_set_position(knot, &n->n.pos, state);
3488     } else {
3489         g_assert_not_reached();
3490     }
3492     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3495 /**
3496  * Node handle "request" signal callback.
3497  */
3498 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3500     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3502     Inkscape::NodePath::NodeSide *me, *opposite;
3503     gint which;
3504     if (n->p.knot == knot) {
3505         me = &n->p;
3506         opposite = &n->n;
3507         which = -1;
3508     } else if (n->n.knot == knot) {
3509         me = &n->n;
3510         opposite = &n->p;
3511         which = 1;
3512     } else {
3513         me = opposite = NULL;
3514         which = 0;
3515         g_assert_not_reached();
3516     }
3518     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3520     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3522     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3523         /* We are smooth node adjacent with line */
3524         NR::Point const delta = *p - n->pos;
3525         NR::Coord const len = NR::L2(delta);
3526         Inkscape::NodePath::Node *othernode = opposite->other;
3527         NR::Point const ndelta = n->pos - othernode->pos;
3528         NR::Coord const linelen = NR::L2(ndelta);
3529         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3530             NR::Coord const scal = dot(delta, ndelta) / linelen;
3531             (*p) = n->pos + (scal / linelen) * ndelta;
3532         }
3533         *p = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), n->subpath->nodepath->item).getPoint();
3534     } else {
3535         *p = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, n->subpath->nodepath->item).getPoint();
3536     }
3538     sp_node_adjust_handle(n, -which);
3540     return FALSE;
3543 /**
3544  * Node handle moved callback.
3545  */
3546 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3548    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3550    Inkscape::NodePath::NodeSide *me;
3551    Inkscape::NodePath::NodeSide *other;
3552     if (n->p.knot == knot) {
3553         me = &n->p;
3554         other = &n->n;
3555     } else if (n->n.knot == knot) {
3556         me = &n->n;
3557         other = &n->p;
3558     } else {
3559         me = NULL;
3560         other = NULL;
3561         g_assert_not_reached();
3562     }
3564     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3565     Radial rme(me->pos - n->pos);
3566     Radial rother(other->pos - n->pos);
3567     Radial rnew(*p - n->pos);
3569     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3570         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3571         /* 0 interpreted as "no snapping". */
3573         // The closest PI/snaps angle, starting from zero.
3574         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3575         if (me->origin_radial.a == HUGE_VAL) {
3576             // ortho doesn't exist: original handle was zero length.
3577             rnew.a = a_snapped;
3578         } else {
3579             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3580              * its opposite and perpendiculars). */
3581             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3583             // Snap to the closest.
3584             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3585                        ? a_snapped
3586                        : a_ortho );
3587         }
3588     }
3590     if (state & GDK_MOD1_MASK) {
3591         // lock handle length
3592         rnew.r = me->origin_radial.r;
3593     }
3595     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3596         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3597         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3598         rother.a += rnew.a - rme.a;
3599         other->pos = NR::Point(rother) + n->pos;
3600         if (other->knot) {
3601             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3602             sp_knot_moveto(other->knot, &other->pos);
3603         }
3604     }
3606     me->pos = NR::Point(rnew) + n->pos;
3607     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3609     // move knot, but without emitting the signal:
3610     // we cannot emit a "moved" signal because we're now processing it
3611     sp_knot_moveto(me->knot, &(me->pos));
3613     update_object(n->subpath->nodepath);
3615     /* status text */
3616     SPDesktop *desktop = n->subpath->nodepath->desktop;
3617     if (!desktop) return;
3618     SPEventContext *ec = desktop->event_context;
3619     if (!ec) return;
3620     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3621     if (!mc) return;
3623     double degrees = 180 / M_PI * rnew.a;
3624     if (degrees > 180) degrees -= 360;
3625     if (degrees < -180) degrees += 360;
3626     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3627         degrees = angle_to_compass (degrees);
3629     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3631     mc->setF(Inkscape::NORMAL_MESSAGE,
3632          _("<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);
3634     g_string_free(length, TRUE);
3637 /**
3638  * Node handle event callback.
3639  */
3640 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3642     gboolean ret = FALSE;
3643     switch (event->type) {
3644         case GDK_KEY_PRESS:
3645             switch (get_group0_keyval (&event->key)) {
3646                 case GDK_space:
3647                     if (event->key.state & GDK_BUTTON1_MASK) {
3648                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3649                         stamp_repr(nodepath);
3650                         ret = TRUE;
3651                     }
3652                     break;
3653                 default:
3654                     break;
3655             }
3656             break;
3657         case GDK_ENTER_NOTIFY:
3658             // we use an experimentally determined threshold that seems to work fine
3659             if (NR::L2(n->pos - knot->pos) < 0.75)
3660                 Inkscape::NodePath::Path::active_node = n;
3661             break;
3662         case GDK_LEAVE_NOTIFY:
3663             // we use an experimentally determined threshold that seems to work fine
3664             if (NR::L2(n->pos - knot->pos) < 0.75)
3665                 Inkscape::NodePath::Path::active_node = NULL;
3666             break;
3667         default:
3668             break;
3669     }
3671     return ret;
3674 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3675                                  Radial &rme, Radial &rother, gboolean const both)
3677     rme.a += angle;
3678     if ( both
3679          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3680          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3681     {
3682         rother.a += angle;
3683     }
3686 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3687                                         Radial &rme, Radial &rother, gboolean const both)
3689     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3691     gdouble r;
3692     if ( both
3693          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3694          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3695     {
3696         r = MAX(rme.r, rother.r);
3697     } else {
3698         r = rme.r;
3699     }
3701     gdouble const weird_angle = atan2(norm_angle, r);
3702 /* Bulia says norm_angle is just the visible distance that the
3703  * object's end must travel on the screen.  Left as 'angle' for want of
3704  * a better name.*/
3706     rme.a += weird_angle;
3707     if ( both
3708          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3709          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3710     {
3711         rother.a += weird_angle;
3712     }
3715 /**
3716  * Rotate one node.
3717  */
3718 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3720     Inkscape::NodePath::NodeSide *me, *other;
3721     bool both = false;
3723     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3724     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3726     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3727         me = &(n->p);
3728         other = &(n->n);
3729     } else if (!n->p.other) {
3730         me = &(n->n);
3731         other = &(n->p);
3732     } else {
3733         if (which > 0) { // right handle
3734             if (xn > xp) {
3735                 me = &(n->n);
3736                 other = &(n->p);
3737             } else {
3738                 me = &(n->p);
3739                 other = &(n->n);
3740             }
3741         } else if (which < 0){ // left handle
3742             if (xn <= xp) {
3743                 me = &(n->n);
3744                 other = &(n->p);
3745             } else {
3746                 me = &(n->p);
3747                 other = &(n->n);
3748             }
3749         } else { // both handles
3750             me = &(n->n);
3751             other = &(n->p);
3752             both = true;
3753         }
3754     }
3756     Radial rme(me->pos - n->pos);
3757     Radial rother(other->pos - n->pos);
3759     if (screen) {
3760         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3761     } else {
3762         node_rotate_one_internal (*n, angle, rme, rother, both);
3763     }
3765     me->pos = n->pos + NR::Point(rme);
3767     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3768         other->pos =  n->pos + NR::Point(rother);
3769     }
3771     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3772     // so here we just move all the knots without emitting move signals, for speed
3773     sp_node_update_handles(n, false);
3776 /**
3777  * Rotate selected nodes.
3778  */
3779 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3781     if (!nodepath || !nodepath->selected) return;
3783     if (g_list_length(nodepath->selected) == 1) {
3784        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3785         node_rotate_one (n, angle, which, screen);
3786     } else {
3787        // rotate as an object:
3789         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3790         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3791         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3792             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3793             box.expandTo (n->pos); // contain all selected nodes
3794         }
3796         gdouble rot;
3797         if (screen) {
3798             gdouble const zoom = nodepath->desktop->current_zoom();
3799             gdouble const zmove = angle / zoom;
3800             gdouble const r = NR::L2(box.max() - box.midpoint());
3801             rot = atan2(zmove, r);
3802         } else {
3803             rot = angle;
3804         }
3806         NR::Point rot_center;
3807         if (Inkscape::NodePath::Path::active_node == NULL)
3808             rot_center = box.midpoint();
3809         else
3810             rot_center = Inkscape::NodePath::Path::active_node->pos;
3812         NR::Matrix t =
3813             NR::Matrix (NR::translate(-rot_center)) *
3814             NR::Matrix (NR::rotate(rot)) *
3815             NR::Matrix (NR::translate(rot_center));
3817         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3818             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3819             n->pos *= t;
3820             n->n.pos *= t;
3821             n->p.pos *= t;
3822             sp_node_update_handles(n, false);
3823         }
3824     }
3826     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3829 /**
3830  * Scale one node.
3831  */
3832 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3834     bool both = false;
3835     Inkscape::NodePath::NodeSide *me, *other;
3837     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3838     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3840     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3841         me = &(n->p);
3842         other = &(n->n);
3843         n->code = NR_CURVETO;
3844     } else if (!n->p.other) {
3845         me = &(n->n);
3846         other = &(n->p);
3847         if (n->n.other)
3848             n->n.other->code = NR_CURVETO;
3849     } else {
3850         if (which > 0) { // right handle
3851             if (xn > xp) {
3852                 me = &(n->n);
3853                 other = &(n->p);
3854                 if (n->n.other)
3855                     n->n.other->code = NR_CURVETO;
3856             } else {
3857                 me = &(n->p);
3858                 other = &(n->n);
3859                 n->code = NR_CURVETO;
3860             }
3861         } else if (which < 0){ // left handle
3862             if (xn <= xp) {
3863                 me = &(n->n);
3864                 other = &(n->p);
3865                 if (n->n.other)
3866                     n->n.other->code = NR_CURVETO;
3867             } else {
3868                 me = &(n->p);
3869                 other = &(n->n);
3870                 n->code = NR_CURVETO;
3871             }
3872         } else { // both handles
3873             me = &(n->n);
3874             other = &(n->p);
3875             both = true;
3876             n->code = NR_CURVETO;
3877             if (n->n.other)
3878                 n->n.other->code = NR_CURVETO;
3879         }
3880     }
3882     Radial rme(me->pos - n->pos);
3883     Radial rother(other->pos - n->pos);
3885     rme.r += grow;
3886     if (rme.r < 0) rme.r = 0;
3887     if (rme.a == HUGE_VAL) {
3888         if (me->other) { // if direction is unknown, initialize it towards the next node
3889             Radial rme_next(me->other->pos - n->pos);
3890             rme.a = rme_next.a;
3891         } else { // if there's no next, initialize to 0
3892             rme.a = 0;
3893         }
3894     }
3895     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3896         rother.r += grow;
3897         if (rother.r < 0) rother.r = 0;
3898         if (rother.a == HUGE_VAL) {
3899             rother.a = rme.a + M_PI;
3900         }
3901     }
3903     me->pos = n->pos + NR::Point(rme);
3905     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3906         other->pos = n->pos + NR::Point(rother);
3907     }
3909     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3910     // so here we just move all the knots without emitting move signals, for speed
3911     sp_node_update_handles(n, false);
3914 /**
3915  * Scale selected nodes.
3916  */
3917 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3919     if (!nodepath || !nodepath->selected) return;
3921     if (g_list_length(nodepath->selected) == 1) {
3922         // scale handles of the single selected node
3923         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3924         node_scale_one (n, grow, which);
3925     } else {
3926         // scale nodes as an "object":
3928         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3929         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3930         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3931             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3932             box.expandTo (n->pos); // contain all selected nodes
3933         }
3935         double scale = (box.maxExtent() + grow)/box.maxExtent();
3937         NR::Point scale_center;
3938         if (Inkscape::NodePath::Path::active_node == NULL)
3939             scale_center = box.midpoint();
3940         else
3941             scale_center = Inkscape::NodePath::Path::active_node->pos;
3943         NR::Matrix t =
3944             NR::Matrix (NR::translate(-scale_center)) *
3945             NR::Matrix (NR::scale(scale, scale)) *
3946             NR::Matrix (NR::translate(scale_center));
3948         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3949             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3950             n->pos *= t;
3951             n->n.pos *= t;
3952             n->p.pos *= t;
3953             sp_node_update_handles(n, false);
3954         }
3955     }
3957     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3960 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3962     if (!nodepath) return;
3963     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3966 /**
3967  * Flip selected nodes horizontally/vertically.
3968  */
3969 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
3971     if (!nodepath || !nodepath->selected) return;
3973     if (g_list_length(nodepath->selected) == 1 && !center) {
3974         // flip handles of the single selected node
3975         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3976         double temp = n->p.pos[axis];
3977         n->p.pos[axis] = n->n.pos[axis];
3978         n->n.pos[axis] = temp;
3979         sp_node_update_handles(n, false);
3980     } else {
3981         // scale nodes as an "object":
3983         NR::Rect box = sp_node_selected_bbox (nodepath);
3984         if (!center) {
3985             center = box.midpoint();
3986         }
3987         NR::Matrix t =
3988             NR::Matrix (NR::translate(- *center)) *
3989             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3990             NR::Matrix (NR::translate(*center));
3992         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3993             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3994             n->pos *= t;
3995             n->n.pos *= t;
3996             n->p.pos *= t;
3997             sp_node_update_handles(n, false);
3998         }
3999     }
4001     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4004 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4006     g_assert (nodepath->selected);
4008     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4009     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4010     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4011         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4012         box.expandTo (n->pos); // contain all selected nodes
4013     }
4014     return box;
4017 //-----------------------------------------------
4018 /**
4019  * Return new subpath under given nodepath.
4020  */
4021 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4023     g_assert(nodepath);
4024     g_assert(nodepath->desktop);
4026    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4028     s->nodepath = nodepath;
4029     s->closed = FALSE;
4030     s->nodes = NULL;
4031     s->first = NULL;
4032     s->last = NULL;
4034     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4035     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4036     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4038     return s;
4041 /**
4042  * Destroy nodes in subpath, then subpath itself.
4043  */
4044 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4046     g_assert(subpath);
4047     g_assert(subpath->nodepath);
4048     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4050     while (subpath->nodes) {
4051         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4052     }
4054     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4056     g_free(subpath);
4059 /**
4060  * Link head to tail in subpath.
4061  */
4062 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4064     g_assert(!sp->closed);
4065     g_assert(sp->last != sp->first);
4066     g_assert(sp->first->code == NR_MOVETO);
4068     sp->closed = TRUE;
4070     //Link the head to the tail
4071     sp->first->p.other = sp->last;
4072     sp->last->n.other  = sp->first;
4073     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4074     sp->first          = sp->last;
4076     //Remove the extra end node
4077     sp_nodepath_node_destroy(sp->last->n.other);
4080 /**
4081  * Open closed (loopy) subpath at node.
4082  */
4083 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4085     g_assert(sp->closed);
4086     g_assert(n->subpath == sp);
4087     g_assert(sp->first == sp->last);
4089     /* We create new startpoint, current node will become last one */
4091    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4092                                                 &n->pos, &n->pos, &n->n.pos);
4095     sp->closed        = FALSE;
4097     //Unlink to make a head and tail
4098     sp->first         = new_path;
4099     sp->last          = n;
4100     n->n.other        = NULL;
4101     new_path->p.other = NULL;
4104 /**
4105  * Return new node in subpath with given properties.
4106  * \param pos Position of node.
4107  * \param ppos Handle position in previous direction
4108  * \param npos Handle position in previous direction
4109  */
4110 Inkscape::NodePath::Node *
4111 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)
4113     g_assert(sp);
4114     g_assert(sp->nodepath);
4115     g_assert(sp->nodepath->desktop);
4117     if (nodechunk == NULL)
4118         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4120     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4122     n->subpath  = sp;
4124     if (type != Inkscape::NodePath::NODE_NONE) {
4125         // use the type from sodipodi:nodetypes
4126         n->type = type;
4127     } else {
4128         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4129             // points are (almost) collinear
4130             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4131                 // endnode, or a node with a retracted handle
4132                 n->type = Inkscape::NodePath::NODE_CUSP;
4133             } else {
4134                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4135             }
4136         } else {
4137             n->type = Inkscape::NodePath::NODE_CUSP;
4138         }
4139     }
4141     n->code     = code;
4142     n->selected = FALSE;
4143     n->pos      = *pos;
4144     n->p.pos    = *ppos;
4145     n->n.pos    = *npos;
4147     n->dragging_out = NULL;
4149     Inkscape::NodePath::Node *prev;
4150     if (next) {
4151         //g_assert(g_list_find(sp->nodes, next));
4152         prev = next->p.other;
4153     } else {
4154         prev = sp->last;
4155     }
4157     if (prev)
4158         prev->n.other = n;
4159     else
4160         sp->first = n;
4162     if (next)
4163         next->p.other = n;
4164     else
4165         sp->last = n;
4167     n->p.other = prev;
4168     n->n.other = next;
4170     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"));
4171     sp_knot_set_position(n->knot, pos, 0);
4173     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4174     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4175     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4176     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4177     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4178     sp_knot_update_ctrl(n->knot);
4180     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4181     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4182     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4183     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4184     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4185     sp_knot_show(n->knot);
4187     // We only create handle knots and lines on demand
4188     n->p.knot = NULL;
4189     n->p.line = NULL;
4190     n->n.knot = NULL;
4191     n->n.line = NULL;
4193     sp->nodes = g_list_prepend(sp->nodes, n);
4195     return n;
4198 /**
4199  * Destroy node and its knots, link neighbors in subpath.
4200  */
4201 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4203     g_assert(node);
4204     g_assert(node->subpath);
4205     g_assert(SP_IS_KNOT(node->knot));
4207    Inkscape::NodePath::SubPath *sp = node->subpath;
4209     if (node->selected) { // first, deselect
4210         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4211         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4212     }
4214     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4216     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4217     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4218     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4219     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4220     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4221     g_object_unref(G_OBJECT(node->knot));
4223     if (node->p.knot) {
4224         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4225         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4226         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4227         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4228         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4229         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4230         g_object_unref(G_OBJECT(node->p.knot));
4231         node->p.knot = NULL;
4232     }
4234     if (node->n.knot) {
4235         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4236         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4237         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4238         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4239         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4240         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4241         g_object_unref(G_OBJECT(node->n.knot));
4242         node->n.knot = NULL;
4243     }
4245     if (node->p.line)
4246         gtk_object_destroy(GTK_OBJECT(node->p.line));
4247     if (node->n.line)
4248         gtk_object_destroy(GTK_OBJECT(node->n.line));
4250     if (sp->nodes) { // there are others nodes on the subpath
4251         if (sp->closed) {
4252             if (sp->first == node) {
4253                 g_assert(sp->last == node);
4254                 sp->first = node->n.other;
4255                 sp->last = sp->first;
4256             }
4257             node->p.other->n.other = node->n.other;
4258             node->n.other->p.other = node->p.other;
4259         } else {
4260             if (sp->first == node) {
4261                 sp->first = node->n.other;
4262                 sp->first->code = NR_MOVETO;
4263             }
4264             if (sp->last == node) sp->last = node->p.other;
4265             if (node->p.other) node->p.other->n.other = node->n.other;
4266             if (node->n.other) node->n.other->p.other = node->p.other;
4267         }
4268     } else { // this was the last node on subpath
4269         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4270     }
4272     g_mem_chunk_free(nodechunk, node);
4275 /**
4276  * Returns one of the node's two sides.
4277  * \param which Indicates which side.
4278  * \return Pointer to previous node side if which==-1, next if which==1.
4279  */
4280 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4282     g_assert(node);
4284     switch (which) {
4285         case -1:
4286             return &node->p;
4287         case 1:
4288             return &node->n;
4289         default:
4290             break;
4291     }
4293     g_assert_not_reached();
4295     return NULL;
4298 /**
4299  * Return the other side of the node, given one of its sides.
4300  */
4301 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4303     g_assert(node);
4305     if (me == &node->p) return &node->n;
4306     if (me == &node->n) return &node->p;
4308     g_assert_not_reached();
4310     return NULL;
4313 /**
4314  * Return NRPathcode on the given side of the node.
4315  */
4316 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4318     g_assert(node);
4320     if (me == &node->p) {
4321         if (node->p.other) return (NRPathcode)node->code;
4322         return NR_MOVETO;
4323     }
4325     if (me == &node->n) {
4326         if (node->n.other) return (NRPathcode)node->n.other->code;
4327         return NR_MOVETO;
4328     }
4330     g_assert_not_reached();
4332     return NR_END;
4335 /**
4336  * Return node with the given index
4337  */
4338 Inkscape::NodePath::Node *
4339 sp_nodepath_get_node_by_index(int index)
4341     Inkscape::NodePath::Node *e = NULL;
4343     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4344     if (!nodepath) {
4345         return e;
4346     }
4348     //find segment
4349     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4351         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4352         int n = g_list_length(sp->nodes);
4353         if (sp->closed) {
4354             n++;
4355         }
4357         //if the piece belongs to this subpath grab it
4358         //otherwise move onto the next subpath
4359         if (index < n) {
4360             e = sp->first;
4361             for (int i = 0; i < index; ++i) {
4362                 e = e->n.other;
4363             }
4364             break;
4365         } else {
4366             if (sp->closed) {
4367                 index -= (n+1);
4368             } else {
4369                 index -= n;
4370             }
4371         }
4372     }
4374     return e;
4377 /**
4378  * Returns plain text meaning of node type.
4379  */
4380 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4382     unsigned retracted = 0;
4383     bool endnode = false;
4385     for (int which = -1; which <= 1; which += 2) {
4386         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4387         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4388             retracted ++;
4389         if (!side->other)
4390             endnode = true;
4391     }
4393     if (retracted == 0) {
4394         if (endnode) {
4395                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4396                 return _("end node");
4397         } else {
4398             switch (node->type) {
4399                 case Inkscape::NodePath::NODE_CUSP:
4400                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4401                     return _("cusp");
4402                 case Inkscape::NodePath::NODE_SMOOTH:
4403                     // TRANSLATORS: "smooth" is an adjective here
4404                     return _("smooth");
4405                 case Inkscape::NodePath::NODE_SYMM:
4406                     return _("symmetric");
4407             }
4408         }
4409     } else if (retracted == 1) {
4410         if (endnode) {
4411             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4412             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4413         } else {
4414             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4415         }
4416     } else {
4417         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4418     }
4420     return NULL;
4423 /**
4424  * Handles content of statusbar as long as node tool is active.
4425  */
4426 void
4427 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4429     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");
4430     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4432     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4433     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4434     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4435     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4437     SPDesktop *desktop = NULL;
4438     if (nodepath) {
4439         desktop = nodepath->desktop;
4440     } else {
4441         desktop = SP_ACTIVE_DESKTOP;
4442     }
4444     SPEventContext *ec = desktop->event_context;
4445     if (!ec) return;
4446     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4447     if (!mc) return;
4449     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4451     if (selected_nodes == 0) {
4452         Inkscape::Selection *sel = desktop->selection;
4453         if (!sel || sel->isEmpty()) {
4454             mc->setF(Inkscape::NORMAL_MESSAGE,
4455                      _("Select a single object to edit its nodes or handles."));
4456         } else {
4457             if (nodepath) {
4458             mc->setF(Inkscape::NORMAL_MESSAGE,
4459                      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.",
4460                               "<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.",
4461                               total_nodes),
4462                      total_nodes);
4463             } else {
4464                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4465                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4466                 } else {
4467                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4468                 }
4469             }
4470         }
4471     } else if (nodepath && selected_nodes == 1) {
4472         mc->setF(Inkscape::NORMAL_MESSAGE,
4473                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4474                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4475                           total_nodes),
4476                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4477     } else {
4478         if (selected_subpaths > 1) {
4479             mc->setF(Inkscape::NORMAL_MESSAGE,
4480                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4481                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4482                               total_nodes),
4483                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4484         } else {
4485             mc->setF(Inkscape::NORMAL_MESSAGE,
4486                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4487                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4488                               total_nodes),
4489                      selected_nodes, total_nodes, when_selected);
4490         }
4491     }
4494 /*
4495  * returns a *copy* of the curve of that object.
4496  */
4497 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4498     if (!object)
4499         return NULL;
4501     SPCurve *curve = NULL;
4502     if (SP_IS_PATH(object)) {
4503         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4504         curve = sp_curve_copy(curve_new);
4505     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4506         const gchar *svgd = object->repr->attribute(key);
4507         if (svgd) {
4508             NArtBpath *bpath = sp_svg_read_path(svgd);
4509             SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
4510             if (curve_new) {
4511                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4512             } else {
4513                 g_free(bpath);
4514             }
4515         }
4516     }
4518     return curve;
4521 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4522     if (!np || !np->object || !curve)
4523         return;
4525     if (SP_IS_PATH(np->object)) {
4526         if (SP_SHAPE(np->object)->path_effect_href) {
4527             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4528         } else {
4529             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4530         }
4531     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4532         // FIXME: this writing to string and then reading from string is bound to be slow.
4533         // create a method to convert from curve directly to 2geom...
4534         gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
4535         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4536         g_free(svgpath);
4538         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4539     }
4542 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4543     np->show_helperpath = show;
4546 /* this function does not work yet */
4547 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4548     np->straight_path = true;
4549     np->show_handles = false;
4550     g_message("add code to make the path straight.");
4551     // do sp_nodepath_convert_node_type on all nodes?
4552     // search for this text !!!   "Make selected segments lines"
4556 /*
4557   Local Variables:
4558   mode:c++
4559   c-file-style:"stroustrup"
4560   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4561   indent-tabs-mode:nil
4562   fill-column:99
4563   End:
4564 */
4565 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :