Code

move helper path to the bottom so it doesnt get in the way with editing nodes
[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 "display/sp-canvas-util.h"
23 #include <glibmm/i18n.h>
24 #include "libnr/n-art-bpath.h"
25 #include "libnr/nr-path.h"
26 #include "helper/units.h"
27 #include "knot.h"
28 #include "inkscape.h"
29 #include "document.h"
30 #include "sp-namedview.h"
31 #include "desktop.h"
32 #include "desktop-handles.h"
33 #include "snap.h"
34 #include "message-stack.h"
35 #include "message-context.h"
36 #include "node-context.h"
37 #include "shape-editor.h"
38 #include "selection-chemistry.h"
39 #include "selection.h"
40 #include "xml/repr.h"
41 #include "prefs-utils.h"
42 #include "sp-metrics.h"
43 #include "sp-path.h"
44 #include "libnr/nr-matrix-ops.h"
45 #include "splivarot.h"
46 #include "svg/svg.h"
47 #include "verbs.h"
48 #include "display/bezier-utils.h"
49 #include <vector>
50 #include <algorithm>
51 #include <cstring>
52 #include <string>
53 #include "live_effects/lpeobject.h"
54 #include "live_effects/parameter/parameter.h"
55 #include "util/mathfns.h"
56 #include "display/snap-indicator.h"
58 class NR::Matrix;
60 /// \todo
61 /// evil evil evil. FIXME: conflict of two different Path classes!
62 /// There is a conflict in the namespace between two classes named Path.
63 /// #include "sp-flowtext.h"
64 /// #include "sp-flowregion.h"
66 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
67 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
68 GType sp_flowregion_get_type (void);
69 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
70 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
71 GType sp_flowtext_get_type (void);
72 // end evil workaround
74 #include "helper/stlport.h"
77 /// \todo fixme: Implement these via preferences */
79 #define NODE_FILL          0xbfbfbf00
80 #define NODE_STROKE        0x000000ff
81 #define NODE_FILL_HI       0xff000000
82 #define NODE_STROKE_HI     0x000000ff
83 #define NODE_FILL_SEL      0x0000ffff
84 #define NODE_STROKE_SEL    0x000000ff
85 #define NODE_FILL_SEL_HI   0xff000000
86 #define NODE_STROKE_SEL_HI 0x000000ff
87 #define KNOT_FILL          0xffffffff
88 #define KNOT_STROKE        0x000000ff
89 #define KNOT_FILL_HI       0xff000000
90 #define KNOT_STROKE_HI     0x000000ff
92 static GMemChunk *nodechunk = NULL;
94 /* Creation from object */
96 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
97 static gchar *parse_nodetypes(gchar const *types, gint length);
99 /* Object updating */
101 static void stamp_repr(Inkscape::NodePath::Path *np);
102 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
103 static gchar *create_typestr(Inkscape::NodePath::Path *np);
105 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
107 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
109 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
111 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
113 /* Adjust handle placement, if the node or the other handle is moved */
114 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
115 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
117 /* Node event callbacks */
118 static void node_clicked(SPKnot *knot, guint state, gpointer data);
119 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
120 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
121 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
123 /* Handle event callbacks */
124 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
125 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
126 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
127 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
128 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
129 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
131 /* Constructors and destructors */
133 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
134 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
135 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
136 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
137 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
138                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
139 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
141 /* Helpers */
143 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
144 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
145 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
147 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
148 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
150 // active_node indicates mouseover node
151 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
153 /**
154  * \brief Creates new nodepath from item
155  */
156 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
158     Inkscape::XML::Node *repr = object->repr;
160     /** \todo
161      * FIXME: remove this. We don't want to edit paths inside flowtext.
162      * Instead we will build our flowtext with cloned paths, so that the
163      * real paths are outside the flowtext and thus editable as usual.
164      */
165     if (SP_IS_FLOWTEXT(object)) {
166         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
167             if SP_IS_FLOWREGION(child) {
168                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
169                 if (grandchild && SP_IS_PATH(grandchild)) {
170                     object = SP_ITEM(grandchild);
171                     break;
172                 }
173             }
174         }
175     }
177     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
179     if (curve == NULL)
180         return NULL;
182     NArtBpath *bpath = sp_curve_first_bpath(curve);
183     gint length = curve->end;
184     if (length == 0) {
185         sp_curve_unref(curve);
186         return NULL; // prevent crash for one-node paths
187     }
189     //Create new nodepath
190     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
191     if (!np) {
192         sp_curve_unref(curve);
193         return NULL;
194     }
196     // Set defaults
197     np->desktop     = desktop;
198     np->object      = object;
199     np->subpaths    = NULL;
200     np->selected    = NULL;
201     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
202     np->livarot_path = NULL;
203     np->local_change = 0;
204     np->show_handles = show_handles;
205     np->helper_path = NULL;
206     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
207     np->helperpath_width = 1.0;
208     np->curve = sp_curve_copy(curve);
209     np->show_helperpath = prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1;
210     np->straight_path = false;
211     if (IS_LIVEPATHEFFECT(object) && item) {
212         np->item = item;
213     } else {
214         np->item = SP_ITEM(object);
215     }
217     // we need to update item's transform from the repr here,
218     // because they may be out of sync when we respond
219     // to a change in repr by regenerating nodepath     --bb
220     sp_object_read_attr(SP_OBJECT(np->item), "transform");
222     np->i2d  = sp_item_i2d_affine(np->item);
223     np->d2i  = np->i2d.inverse();
225     np->repr = repr;
226     if (repr_key_in) { // apparantly the object is an LPEObject
227         np->repr_key = g_strdup(repr_key_in);
228         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
229         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
230         if (lpeparam) {
231             lpeparam->param_setup_nodepath(np);
232         }
233     } else {
234         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
235         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
236             np->repr_key = g_strdup("inkscape:original-d");
238             LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(SP_LPE_ITEM(np->object));
239             if (lpeobj && lpeobj->lpe) {
240                 lpeobj->lpe->setup_nodepath(np);
241             }
242         } else {
243             np->repr_key = g_strdup("d");
244         }
245     }
247     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
248     gchar *typestr = parse_nodetypes(nodetypes, length);
250     // create the subpath(s) from the bpath
251     NArtBpath *b = bpath;
252     while (b->code != NR_END) {
253         b = subpath_from_bpath(np, b, typestr + (b - bpath));
254     }
256     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
257     np->subpaths = g_list_reverse(np->subpaths);
259     g_free(typestr);
260     sp_curve_unref(curve);
262     // create the livarot representation from the same item
263     sp_nodepath_ensure_livarot_path(np);
265     // Draw helper curve
266     if (np->show_helperpath) {
267         SPCurve *helper_curve = sp_curve_copy(np->curve);
268         sp_curve_transform(helper_curve, np->i2d );
269         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
270         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);
271         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
272         sp_canvas_item_move_to_z(np->helper_path, 0);
273         sp_canvas_item_show(np->helper_path);
274         sp_curve_unref(helper_curve);
275     }
277     return np;
280 /**
281  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
282  */
283 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
285     if (!np)  //soft fail, like delete
286         return;
288     while (np->subpaths) {
289         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
290     }
292     //Inform the ShapeEditor that made me, if any, that I am gone.
293     if (np->shape_editor)
294         np->shape_editor->nodepath_destroyed();
296     g_assert(!np->selected);
298     if (np->livarot_path) {
299         delete np->livarot_path;
300         np->livarot_path = NULL;
301     }
303     if (np->helper_path) {
304         GtkObject *temp = np->helper_path;
305         np->helper_path = NULL;
306         gtk_object_destroy(temp);
307     }
308     if (np->curve) {
309         sp_curve_unref(np->curve);
310         np->curve = NULL;
311     }
313     if (np->repr_key) {
314         g_free(np->repr_key);
315         np->repr_key = NULL;
316     }
317     if (np->repr_nodetypes_key) {
318         g_free(np->repr_nodetypes_key);
319         np->repr_nodetypes_key = NULL;
320     }
322     np->desktop = NULL;
324     g_free(np);
328 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
330     if (np && np->livarot_path == NULL) {
331         SPCurve *curve = create_curve(np);
332         NArtBpath *bpath = SP_CURVE_BPATH(curve);
333         np->livarot_path = bpath_to_Path(bpath);
335         if (np->livarot_path)
336             np->livarot_path->ConvertWithBackData(0.01);
338         sp_curve_unref(curve);
339     }
343 /**
344  *  Return the node count of a given NodeSubPath.
345  */
346 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
348     if (!subpath)
349         return 0;
350     gint nodeCount = g_list_length(subpath->nodes);
351     return nodeCount;
354 /**
355  *  Return the node count of a given NodePath.
356  */
357 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
359     if (!np)
360         return 0;
361     gint nodeCount = 0;
362     for (GList *item = np->subpaths ; item ; item=item->next) {
363        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
364         nodeCount += g_list_length(subpath->nodes);
365     }
366     return nodeCount;
369 /**
370  *  Return the subpath count of a given NodePath.
371  */
372 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
374     if (!np)
375         return 0;
376     return g_list_length (np->subpaths);
379 /**
380  *  Return the selected node count of a given NodePath.
381  */
382 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
384     if (!np)
385         return 0;
386     return g_list_length (np->selected);
389 /**
390  *  Return the number of subpaths where nodes are selected in a given NodePath.
391  */
392 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
394     if (!np)
395         return 0;
396     if (!np->selected)
397         return 0;
398     if (!np->selected->next)
399         return 1;
400     gint count = 0;
401     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
402         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
403         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
404             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
405             if (node->selected) {
406                 count ++;
407                 break;
408             }
409         }
410     }
411     return count;
414 /**
415  * Clean up a nodepath after editing.
416  *
417  * Currently we are deleting trivial subpaths.
418  */
419 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
421     GList *badSubPaths = NULL;
423     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
424     for (GList *l = nodepath->subpaths; l ; l=l->next) {
425        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
426        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
427             badSubPaths = g_list_append(badSubPaths, sp);
428     }
430     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
431     //also removes the subpath from nodepath->subpaths
432     for (GList *l = badSubPaths; l ; l=l->next) {
433        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
434         sp_nodepath_subpath_destroy(sp);
435     }
437     g_list_free(badSubPaths);
440 /**
441  * Create new nodepath from b, make it subpath of np.
442  * \param t The node type.
443  * \todo Fixme: t should be a proper type, rather than gchar
444  */
445 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
447     NR::Point ppos, pos, npos;
449     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
451     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
452     bool const closed = (b->code == NR_MOVETO);
454     pos = NR::Point(b->x3, b->y3) * np->i2d;
455     if (b[1].code == NR_CURVETO) {
456         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
457     } else {
458         npos = pos;
459     }
460     Inkscape::NodePath::Node *n;
461     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
462     g_assert(sp->first == n);
463     g_assert(sp->last  == n);
465     b++;
466     t++;
467     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
468         pos = NR::Point(b->x3, b->y3) * np->i2d;
469         if (b->code == NR_CURVETO) {
470             ppos = NR::Point(b->x2, b->y2) * np->i2d;
471         } else {
472             ppos = pos;
473         }
474         if (b[1].code == NR_CURVETO) {
475             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
476         } else {
477             npos = pos;
478         }
479         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
480         b++;
481         t++;
482     }
484     if (closed) sp_nodepath_subpath_close(sp);
486     return b;
489 /**
490  * Convert from sodipodi:nodetypes to new style type string.
491  */
492 static gchar *parse_nodetypes(gchar const *types, gint length)
494     g_assert(length > 0);
496     gchar *typestr = g_new(gchar, length + 1);
498     gint pos = 0;
500     if (types) {
501         for (gint i = 0; types[i] && ( i < length ); i++) {
502             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
503             if (types[i] != '\0') {
504                 switch (types[i]) {
505                     case 's':
506                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
507                         break;
508                     case 'z':
509                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
510                         break;
511                     case 'c':
512                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
513                         break;
514                     default:
515                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
516                         break;
517                 }
518             }
519         }
520     }
522     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
524     return typestr;
527 /**
528  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
529  * updated but repr is not (for speed). Used during curve and node drag.
530  */
531 static void update_object(Inkscape::NodePath::Path *np)
533     g_assert(np);
535     sp_curve_unref(np->curve);
536     np->curve = create_curve(np);
538     sp_nodepath_set_curve(np, np->curve);
540     if (np->show_helperpath) {
541         SPCurve * helper_curve = sp_curve_copy(np->curve);
542         sp_curve_transform(helper_curve, np->i2d );
543         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
544         sp_curve_unref(helper_curve);
545     }
548 /**
549  * Update XML path node with data from path object.
550  */
551 static void update_repr_internal(Inkscape::NodePath::Path *np)
553     g_assert(np);
555     Inkscape::XML::Node *repr = np->object->repr;
557     sp_curve_unref(np->curve);
558     np->curve = create_curve(np);
560     gchar *typestr = create_typestr(np);
561     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
563     // determine if path has an effect applied and write to correct "d" attribute.
564     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
565         np->local_change++;
566         repr->setAttribute(np->repr_key, svgpath);
567     }
569     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
570         np->local_change++;
571         repr->setAttribute(np->repr_nodetypes_key, typestr);
572     }
574     g_free(svgpath);
575     g_free(typestr);
577     if (np->show_helperpath) {
578         SPCurve * helper_curve = sp_curve_copy(np->curve);
579         sp_curve_transform(helper_curve, np->i2d );
580         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
581         sp_curve_unref(helper_curve);
582     }
583  }
585 /**
586  * Update XML path node with data from path object, commit changes forever.
587  */
588 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
590     //fixme: np can be NULL, so check before proceeding
591     g_return_if_fail(np != NULL);
593     if (np->livarot_path) {
594         delete np->livarot_path;
595         np->livarot_path = NULL;
596     }
598     update_repr_internal(np);
599     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
601     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
602                      annotation);
605 /**
606  * Update XML path node with data from path object, commit changes with undo.
607  */
608 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
610     if (np->livarot_path) {
611         delete np->livarot_path;
612         np->livarot_path = NULL;
613     }
615     update_repr_internal(np);
616     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
617                            annotation);
620 /**
621  * Make duplicate of path, replace corresponding XML node in tree, commit.
622  */
623 static void stamp_repr(Inkscape::NodePath::Path *np)
625     g_assert(np);
627     Inkscape::XML::Node *old_repr = np->object->repr;
628     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
630     // remember the position of the item
631     gint pos = old_repr->position();
632     // remember parent
633     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
635     SPCurve *curve = create_curve(np);
636     gchar *typestr = create_typestr(np);
638     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
640     new_repr->setAttribute(np->repr_key, svgpath);
641     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
643     // add the new repr to the parent
644     parent->appendChild(new_repr);
645     // move to the saved position
646     new_repr->setPosition(pos > 0 ? pos : 0);
648     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
649                      _("Stamp"));
651     Inkscape::GC::release(new_repr);
652     g_free(svgpath);
653     g_free(typestr);
654     sp_curve_unref(curve);
657 /**
658  * Create curve from path.
659  */
660 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
662     SPCurve *curve = sp_curve_new();
664     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
665        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
666         sp_curve_moveto(curve,
667                         sp->first->pos * np->d2i);
668        Inkscape::NodePath::Node *n = sp->first->n.other;
669         while (n) {
670             NR::Point const end_pt = n->pos * np->d2i;
671             switch (n->code) {
672                 case NR_LINETO:
673                     sp_curve_lineto(curve, end_pt);
674                     break;
675                 case NR_CURVETO:
676                     sp_curve_curveto(curve,
677                                      n->p.other->n.pos * np->d2i,
678                                      n->p.pos * np->d2i,
679                                      end_pt);
680                     break;
681                 default:
682                     g_assert_not_reached();
683                     break;
684             }
685             if (n != sp->last) {
686                 n = n->n.other;
687             } else {
688                 n = NULL;
689             }
690         }
691         if (sp->closed) {
692             sp_curve_closepath(curve);
693         }
694     }
696     return curve;
699 /**
700  * Convert path type string to sodipodi:nodetypes style.
701  */
702 static gchar *create_typestr(Inkscape::NodePath::Path *np)
704     gchar *typestr = g_new(gchar, 32);
705     gint len = 32;
706     gint pos = 0;
708     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
709        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
711         if (pos >= len) {
712             typestr = g_renew(gchar, typestr, len + 32);
713             len += 32;
714         }
716         typestr[pos++] = 'c';
718        Inkscape::NodePath::Node *n;
719         n = sp->first->n.other;
720         while (n) {
721             gchar code;
723             switch (n->type) {
724                 case Inkscape::NodePath::NODE_CUSP:
725                     code = 'c';
726                     break;
727                 case Inkscape::NodePath::NODE_SMOOTH:
728                     code = 's';
729                     break;
730                 case Inkscape::NodePath::NODE_SYMM:
731                     code = 'z';
732                     break;
733                 default:
734                     g_assert_not_reached();
735                     code = '\0';
736                     break;
737             }
739             if (pos >= len) {
740                 typestr = g_renew(gchar, typestr, len + 32);
741                 len += 32;
742             }
744             typestr[pos++] = code;
746             if (n != sp->last) {
747                 n = n->n.other;
748             } else {
749                 n = NULL;
750             }
751         }
752     }
754     if (pos >= len) {
755         typestr = g_renew(gchar, typestr, len + 1);
756         len += 1;
757     }
759     typestr[pos++] = '\0';
761     return typestr;
764 /**
765  * Returns current path in context. // later eliminate this function at all!
766  */
767 static Inkscape::NodePath::Path *sp_nodepath_current()
769     if (!SP_ACTIVE_DESKTOP) {
770         return NULL;
771     }
773     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
775     if (!SP_IS_NODE_CONTEXT(event_context)) {
776         return NULL;
777     }
779     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
784 /**
785  \brief Fills node and handle positions for three nodes, splitting line
786   marked by end at distance t.
787  */
788 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
790     g_assert(new_path != NULL);
791     g_assert(end      != NULL);
793     g_assert(end->p.other == new_path);
794    Inkscape::NodePath::Node *start = new_path->p.other;
795     g_assert(start);
797     if (end->code == NR_LINETO) {
798         new_path->type =Inkscape::NodePath::NODE_CUSP;
799         new_path->code = NR_LINETO;
800         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
801     } else {
802         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
803         new_path->code = NR_CURVETO;
804         gdouble s      = 1 - t;
805         for (int dim = 0; dim < 2; dim++) {
806             NR::Coord const f000 = start->pos[dim];
807             NR::Coord const f001 = start->n.pos[dim];
808             NR::Coord const f011 = end->p.pos[dim];
809             NR::Coord const f111 = end->pos[dim];
810             NR::Coord const f00t = s * f000 + t * f001;
811             NR::Coord const f01t = s * f001 + t * f011;
812             NR::Coord const f11t = s * f011 + t * f111;
813             NR::Coord const f0tt = s * f00t + t * f01t;
814             NR::Coord const f1tt = s * f01t + t * f11t;
815             NR::Coord const fttt = s * f0tt + t * f1tt;
816             start->n.pos[dim]    = f00t;
817             new_path->p.pos[dim] = f0tt;
818             new_path->pos[dim]   = fttt;
819             new_path->n.pos[dim] = f1tt;
820             end->p.pos[dim]      = f11t;
821         }
822     }
825 /**
826  * Adds new node on direct line between two nodes, activates handles of all
827  * three nodes.
828  */
829 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
831     g_assert(end);
832     g_assert(end->subpath);
833     g_assert(g_list_find(end->subpath->nodes, end));
835    Inkscape::NodePath::Node *start = end->p.other;
836     g_assert( start->n.other == end );
837    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
838                                                end,
839                                                (NRPathcode)end->code == NR_LINETO?
840                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
841                                                (NRPathcode)end->code,
842                                                &start->pos, &start->pos, &start->n.pos);
843     sp_nodepath_line_midpoint(newnode, end, t);
845     sp_node_adjust_handles(start);
846     sp_node_update_handles(start);
847     sp_node_update_handles(newnode);
848     sp_node_adjust_handles(end);
849     sp_node_update_handles(end);
851     return newnode;
854 /**
855 \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
856 */
857 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
859     g_assert(node);
860     g_assert(node->subpath);
861     g_assert(g_list_find(node->subpath->nodes, node));
863    Inkscape::NodePath::SubPath *sp = node->subpath;
864     Inkscape::NodePath::Path *np    = sp->nodepath;
866     if (sp->closed) {
867         sp_nodepath_subpath_open(sp, node);
868         return sp->first;
869     } else {
870         // no break for end nodes
871         if (node == sp->first) return NULL;
872         if (node == sp->last ) return NULL;
874         // create a new subpath
875        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
877         // duplicate the break node as start of the new subpath
878        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
880         while (node->n.other) { // copy the remaining nodes into the new subpath
881            Inkscape::NodePath::Node *n  = node->n.other;
882            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);
883             if (n->selected) {
884                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
885             }
886             sp_nodepath_node_destroy(n); // remove the point on the original subpath
887         }
889         return newnode;
890     }
893 /**
894  * Duplicate node and connect to neighbours.
895  */
896 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
898     g_assert(node);
899     g_assert(node->subpath);
900     g_assert(g_list_find(node->subpath->nodes, node));
902    Inkscape::NodePath::SubPath *sp = node->subpath;
904     NRPathcode code = (NRPathcode) node->code;
905     if (code == NR_MOVETO) { // if node is the endnode,
906         node->code = NR_LINETO; // new one is inserted before it, so change that to line
907     }
909     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
911     if (!node->n.other || !node->p.other) // if node is an endnode, select it
912         return node;
913     else
914         return newnode; // otherwise select the newly created node
917 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
919     node->p.pos = (node->pos + (node->pos - node->n.pos));
922 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
924     node->n.pos = (node->pos + (node->pos - node->p.pos));
927 /**
928  * Change line type at node, with side effects on neighbours.
929  */
930 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
932     g_assert(end);
933     g_assert(end->subpath);
934     g_assert(end->p.other);
936     if (end->code == static_cast< guint > ( code ) )
937         return;
939    Inkscape::NodePath::Node *start = end->p.other;
941     end->code = code;
943     if (code == NR_LINETO) {
944         if (start->code == NR_LINETO) {
945             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
946         }
947         if (end->n.other) {
948             if (end->n.other->code == NR_LINETO) {
949                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
950             }
951         }
952     } else {
953         NR::Point delta = end->pos - start->pos;
954         start->n.pos = start->pos + delta / 3;
955         end->p.pos = end->pos - delta / 3;
956         sp_node_adjust_handle(start, 1);
957         sp_node_adjust_handle(end, -1);
958     }
960     sp_node_update_handles(start);
961     sp_node_update_handles(end);
964 /**
965  * Change node type, and its handles accordingly.
966  */
967 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
969     g_assert(node);
970     g_assert(node->subpath);
972     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
973         return node;
975     if ((node->p.other != NULL) && (node->n.other != NULL)) {
976         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
977             type =Inkscape::NodePath::NODE_CUSP;
978         }
979     }
981     node->type = type;
983     if (node->type == Inkscape::NodePath::NODE_CUSP) {
984         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
985         node->knot->setSize (node->selected? 11 : 9);
986         sp_knot_update_ctrl(node->knot);
987     } else {
988         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
989         node->knot->setSize (node->selected? 9 : 7);
990         sp_knot_update_ctrl(node->knot);
991     }
993     // if one of handles is mouseovered, preserve its position
994     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
995         sp_node_adjust_handle(node, 1);
996     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
997         sp_node_adjust_handle(node, -1);
998     } else {
999         sp_node_adjust_handles(node);
1000     }
1002     sp_node_update_handles(node);
1004     sp_nodepath_update_statusbar(node->subpath->nodepath);
1006     return node;
1009 /**
1010  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
1011  * adjacent segments from lines to curves.
1012 */
1013 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1015     bool p_line = (node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos);
1016     bool n_line = (node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos);
1018     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1019         if (p_line && n_line) {
1020             // only if both adjacent segments are lines,
1021             // convert both to curves:
1023             node->code = NR_CURVETO;
1024             node->n.other->code = NR_CURVETO;
1026             NR::Point leg_prev = node->pos - node->p.other->pos;
1027             NR::Point leg_next = node->pos - node->n.other->pos;
1029             double norm_leg_prev = L2(leg_prev);
1030             double norm_leg_next = L2(leg_next);
1032             // delta has length 1 and is orthogonal to bisecting line
1033             NR::Point delta;
1034             if (norm_leg_next > 0.0) {
1035                 delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1036                 (&delta)->normalize();
1037             }
1039             if (type == Inkscape::NodePath::NODE_SYMM) {
1040                 double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1041                 node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1042                 node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1043             } else {
1044                 // length of handle is proportional to distance to adjacent node
1045                 node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1046                 node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1047             }
1049             sp_node_update_handles(node);
1050         }
1051     }
1053     sp_nodepath_set_node_type (node, type);
1056 /**
1057  * Move node to point, and adjust its and neighbouring handles.
1058  */
1059 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1061     NR::Point delta = p - node->pos;
1062     node->pos = p;
1064     node->p.pos += delta;
1065     node->n.pos += delta;
1067     Inkscape::NodePath::Node *node_p = NULL;
1068     Inkscape::NodePath::Node *node_n = NULL;
1070     if (node->p.other) {
1071         if (node->code == NR_LINETO) {
1072             sp_node_adjust_handle(node, 1);
1073             sp_node_adjust_handle(node->p.other, -1);
1074             node_p = node->p.other;
1075         }
1076     }
1077     if (node->n.other) {
1078         if (node->n.other->code == NR_LINETO) {
1079             sp_node_adjust_handle(node, -1);
1080             sp_node_adjust_handle(node->n.other, 1);
1081             node_n = node->n.other;
1082         }
1083     }
1085     // this function is only called from batch movers that will update display at the end
1086     // themselves, so here we just move all the knots without emitting move signals, for speed
1087     sp_node_update_handles(node, false);
1088     if (node_n) {
1089         sp_node_update_handles(node_n, false);
1090     }
1091     if (node_p) {
1092         sp_node_update_handles(node_p, false);
1093     }
1096 /**
1097  * Call sp_node_moveto() for node selection and handle possible snapping.
1098  */
1099 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1100                                             bool const snap = true)
1102     NR::Coord best = NR_HUGE;
1103     NR::Point delta(dx, dy);
1104     NR::Point best_pt = delta;
1105     NR::Point best_abs(NR_HUGE, NR_HUGE);
1107     
1108     if (snap) {    
1109         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1110          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1111          * must provide that information. */
1112           
1113         // Build a list of the unselected nodes to which the snapper should snap 
1114         std::vector<NR::Point> unselected_nodes;
1115         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1116             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1117             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1118                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1119                 if (!node->selected) {
1120                     unselected_nodes.push_back(node->pos);
1121                 }    
1122             }
1123         }        
1124         
1125         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1126         
1127         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1128             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1129             Inkscape::SnappedPoint s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);            
1130             if (s.getDistance() < best) {
1131                 best = s.getDistance();
1132                 best_abs = s.getPoint();
1133                 best_pt = best_abs - n->pos;
1134             }
1135         }
1136                         
1137         if (best_abs[NR::X] < NR_HUGE) {
1138             nodepath->desktop->snapindicator->set_new_snappoint(best_abs.to_2geom());
1139         }
1140     }
1142     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1143         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1144         sp_node_moveto(n, n->pos + best_pt);
1145     }
1147     // do not update repr here so that node dragging is acceptably fast
1148     update_object(nodepath);
1151 /**
1152 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1153 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1154 near x = 0.
1155  */
1156 double
1157 sculpt_profile (double x, double alpha, guint profile)
1159     if (x >= 1)
1160         return 0;
1161     if (x <= 0)
1162         return 1;
1164     switch (profile) {
1165         case SCULPT_PROFILE_LINEAR:
1166         return 1 - x;
1167         case SCULPT_PROFILE_BELL:
1168         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1169         case SCULPT_PROFILE_ELLIPTIC:
1170         return sqrt(1 - x*x);
1171     }
1173     return 1;
1176 double
1177 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1179     // extremely primitive for now, don't have time to look for the real one
1180     double lower = NR::L2(b - a);
1181     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1182     return (lower + upper)/2;
1185 void
1186 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1188     n->pos = n->origin + delta;
1189     n->n.pos = n->n.origin + delta_n;
1190     n->p.pos = n->p.origin + delta_p;
1191     sp_node_adjust_handles(n);
1192     sp_node_update_handles(n, false);
1195 /**
1196  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1197  * on how far they are from the dragged node n.
1198  */
1199 static void
1200 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1202     g_assert (n);
1203     g_assert (nodepath);
1204     g_assert (n->subpath->nodepath == nodepath);
1206     double pressure = n->knot->pressure;
1207     if (pressure == 0)
1208         pressure = 0.5; // default
1209     pressure = CLAMP (pressure, 0.2, 0.8);
1211     // map pressure to alpha = 1/5 ... 5
1212     double alpha = 1 - 2 * fabs(pressure - 0.5);
1213     if (pressure > 0.5)
1214         alpha = 1/alpha;
1216     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1218     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1219         // Only one subpath has selected nodes:
1220         // use linear mode, where the distance from n to node being dragged is calculated along the path
1222         double n_sel_range = 0, p_sel_range = 0;
1223         guint n_nodes = 0, p_nodes = 0;
1224         guint n_sel_nodes = 0, p_sel_nodes = 0;
1226         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1227         {
1228             double n_range = 0, p_range = 0;
1229             bool n_going = true, p_going = true;
1230             Inkscape::NodePath::Node *n_node = n;
1231             Inkscape::NodePath::Node *p_node = n;
1232             do {
1233                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1234                 if (n_node && n_going)
1235                     n_node = n_node->n.other;
1236                 if (n_node == NULL) {
1237                     n_going = false;
1238                 } else {
1239                     n_nodes ++;
1240                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1241                     if (n_node->selected) {
1242                         n_sel_nodes ++;
1243                         n_sel_range = n_range;
1244                     }
1245                     if (n_node == p_node) {
1246                         n_going = false;
1247                         p_going = false;
1248                     }
1249                 }
1250                 if (p_node && p_going)
1251                     p_node = p_node->p.other;
1252                 if (p_node == NULL) {
1253                     p_going = false;
1254                 } else {
1255                     p_nodes ++;
1256                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1257                     if (p_node->selected) {
1258                         p_sel_nodes ++;
1259                         p_sel_range = p_range;
1260                     }
1261                     if (p_node == n_node) {
1262                         n_going = false;
1263                         p_going = false;
1264                     }
1265                 }
1266             } while (n_going || p_going);
1267         }
1269         // Second pass: actually move nodes in this subpath
1270         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1271         {
1272             double n_range = 0, p_range = 0;
1273             bool n_going = true, p_going = true;
1274             Inkscape::NodePath::Node *n_node = n;
1275             Inkscape::NodePath::Node *p_node = n;
1276             do {
1277                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1278                 if (n_node && n_going)
1279                     n_node = n_node->n.other;
1280                 if (n_node == NULL) {
1281                     n_going = false;
1282                 } else {
1283                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1284                     if (n_node->selected) {
1285                         sp_nodepath_move_node_and_handles (n_node,
1286                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1287                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1288                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1289                     }
1290                     if (n_node == p_node) {
1291                         n_going = false;
1292                         p_going = false;
1293                     }
1294                 }
1295                 if (p_node && p_going)
1296                     p_node = p_node->p.other;
1297                 if (p_node == NULL) {
1298                     p_going = false;
1299                 } else {
1300                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1301                     if (p_node->selected) {
1302                         sp_nodepath_move_node_and_handles (p_node,
1303                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1304                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1305                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1306                     }
1307                     if (p_node == n_node) {
1308                         n_going = false;
1309                         p_going = false;
1310                     }
1311                 }
1312             } while (n_going || p_going);
1313         }
1315     } else {
1316         // Multiple subpaths have selected nodes:
1317         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1318         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1319         // fix the pear-like shape when sculpting e.g. a ring
1321         // First pass: calculate range
1322         gdouble direct_range = 0;
1323         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1324             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1325             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1326                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1327                 if (node->selected) {
1328                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1329                 }
1330             }
1331         }
1333         // Second pass: actually move nodes
1334         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1335             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1336             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1337                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1338                 if (node->selected) {
1339                     if (direct_range > 1e-6) {
1340                         sp_nodepath_move_node_and_handles (node,
1341                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1342                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1343                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1344                     } else {
1345                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1346                     }
1348                 }
1349             }
1350         }
1351     }
1353     // do not update repr here so that node dragging is acceptably fast
1354     update_object(nodepath);
1358 /**
1359  * Move node selection to point, adjust its and neighbouring handles,
1360  * handle possible snapping, and commit the change with possible undo.
1361  */
1362 void
1363 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1365     if (!nodepath) return;
1367     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1369     if (dx == 0) {
1370         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1371     } else if (dy == 0) {
1372         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1373     } else {
1374         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1375     }
1378 /**
1379  * Move node selection off screen and commit the change.
1380  */
1381 void
1382 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1384     // borrowed from sp_selection_move_screen in selection-chemistry.c
1385     // we find out the current zoom factor and divide deltas by it
1386     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1388     gdouble zoom = desktop->current_zoom();
1389     gdouble zdx = dx / zoom;
1390     gdouble zdy = dy / zoom;
1392     if (!nodepath) return;
1394     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1396     if (dx == 0) {
1397         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1398     } else if (dy == 0) {
1399         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1400     } else {
1401         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1402     }
1405 /**
1406  * Move selected nodes to the absolute position given
1407  */
1408 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1410     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1411         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1412         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1413         sp_node_moveto(n, npos);
1414     }
1416     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1419 /**
1420  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1421  */
1422 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1424     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1425     g_return_val_if_fail(nodepath->selected, no_coord);
1427     // determine coordinate of first selected node
1428     GList *nsel = nodepath->selected;
1429     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1430     NR::Coord coord = n->pos[axis];
1431     bool coincide = true;
1433     // compare it to the coordinates of all the other selected nodes
1434     for (GList *l = nsel->next; l != NULL; l = l->next) {
1435         n = (Inkscape::NodePath::Node *) l->data;
1436         if (n->pos[axis] != coord) {
1437             coincide = false;
1438         }
1439     }
1440     if (coincide) {
1441         return coord;
1442     } else {
1443         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1444         // currently we return the coordinate of the bounding box midpoint because I don't know how
1445         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1446         return bbox.midpoint()[axis];
1447     }
1450 /** If they don't yet exist, creates knot and line for the given side of the node */
1451 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1453     if (!side->knot) {
1454         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"));
1456         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1457         side->knot->setSize (7);
1458         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1459         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1460         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1461         sp_knot_update_ctrl(side->knot);
1463         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1464         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1465         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1466         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1467         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1468         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1469     }
1471     if (!side->line) {
1472         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1473                                         SP_TYPE_CTRLLINE, NULL);
1474     }
1477 /**
1478  * Ensure the given handle of the node is visible/invisible, update its screen position
1479  */
1480 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1482     g_assert(node != NULL);
1484    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1485     NRPathcode code = sp_node_path_code_from_side(node, side);
1487     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1489     if (show_handle) {
1490         if (!side->knot) { // No handle knot at all
1491             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1492             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1493             side->knot->pos = side->pos;
1494             if (side->knot->item)
1495                 SP_CTRL(side->knot->item)->moveto(side->pos);
1496             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1497             sp_knot_show(side->knot);
1498         } else {
1499             if (side->knot->pos != side->pos) { // only if it's really moved
1500                 if (fire_move_signals) {
1501                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1502                 } else {
1503                     sp_knot_moveto(side->knot, &side->pos);
1504                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1505                 }
1506             }
1507             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1508                 sp_knot_show(side->knot);
1509             }
1510         }
1511         sp_canvas_item_show(side->line);
1512     } else {
1513         if (side->knot) {
1514             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1515                 sp_knot_hide(side->knot);
1516             }
1517         }
1518         if (side->line) {
1519             sp_canvas_item_hide(side->line);
1520         }
1521     }
1524 /**
1525  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1526  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1527  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1528  * updated; otherwise, just move the knots silently (used in batch moves).
1529  */
1530 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1532     g_assert(node != NULL);
1534     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1535         sp_knot_show(node->knot);
1536     }
1538     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1539         if (fire_move_signals)
1540             sp_knot_set_position(node->knot, &node->pos, 0);
1541         else
1542             sp_knot_moveto(node->knot, &node->pos);
1543     }
1545     gboolean show_handles = node->selected;
1546     if (node->p.other != NULL) {
1547         if (node->p.other->selected) show_handles = TRUE;
1548     }
1549     if (node->n.other != NULL) {
1550         if (node->n.other->selected) show_handles = TRUE;
1551     }
1553     if (node->subpath->nodepath->show_handles == false)
1554         show_handles = FALSE;
1556     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1557     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1560 /**
1561  * Call sp_node_update_handles() for all nodes on subpath.
1562  */
1563 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1565     g_assert(subpath != NULL);
1567     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1568         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1569     }
1572 /**
1573  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1574  */
1575 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1577     g_assert(nodepath != NULL);
1579     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1580         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1581     }
1584 void
1585 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1587     if (nodepath == NULL) return;
1589     nodepath->show_handles = show;
1590     sp_nodepath_update_handles(nodepath);
1593 /**
1594  * Adds all selected nodes in nodepath to list.
1595  */
1596 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1598     StlConv<Node *>::list(l, selected);
1599 /// \todo this adds a copying, rework when the selection becomes a stl list
1602 /**
1603  * Align selected nodes on the specified axis.
1604  */
1605 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1607     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1608         return;
1609     }
1611     if ( !nodepath->selected->next ) { // only one node selected
1612         return;
1613     }
1614    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1615     NR::Point dest(pNode->pos);
1616     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1617         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1618         if (pNode) {
1619             dest[axis] = pNode->pos[axis];
1620             sp_node_moveto(pNode, dest);
1621         }
1622     }
1624     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1627 /// Helper struct.
1628 struct NodeSort
1630    Inkscape::NodePath::Node *_node;
1631     NR::Coord _coord;
1632     /// \todo use vectorof pointers instead of calling copy ctor
1633     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1634         _node(node), _coord(node->pos[axis])
1635     {}
1637 };
1639 static bool operator<(NodeSort const &a, NodeSort const &b)
1641     return (a._coord < b._coord);
1644 /**
1645  * Distribute selected nodes on the specified axis.
1646  */
1647 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1649     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1650         return;
1651     }
1653     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1654         return;
1655     }
1657    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1658     std::vector<NodeSort> sorted;
1659     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1660         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1661         if (pNode) {
1662             NodeSort n(pNode, axis);
1663             sorted.push_back(n);
1664             //dest[axis] = pNode->pos[axis];
1665             //sp_node_moveto(pNode, dest);
1666         }
1667     }
1668     std::sort(sorted.begin(), sorted.end());
1669     unsigned int len = sorted.size();
1670     //overall bboxes span
1671     float dist = (sorted.back()._coord -
1672                   sorted.front()._coord);
1673     //new distance between each bbox
1674     float step = (dist) / (len - 1);
1675     float pos = sorted.front()._coord;
1676     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1677           it < sorted.end();
1678           it ++ )
1679     {
1680         NR::Point dest((*it)._node->pos);
1681         dest[axis] = pos;
1682         sp_node_moveto((*it)._node, dest);
1683         pos += step;
1684     }
1686     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1690 /**
1691  * Call sp_nodepath_line_add_node() for all selected segments.
1692  */
1693 void
1694 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1696     if (!nodepath) {
1697         return;
1698     }
1700     GList *nl = NULL;
1702     int n_added = 0;
1704     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1705        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1706         g_assert(t->selected);
1707         if (t->p.other && t->p.other->selected) {
1708             nl = g_list_prepend(nl, t);
1709         }
1710     }
1712     while (nl) {
1713        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1714        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1715        sp_nodepath_node_select(n, TRUE, FALSE);
1716        n_added ++;
1717        nl = g_list_remove(nl, t);
1718     }
1720     /** \todo fixme: adjust ? */
1721     sp_nodepath_update_handles(nodepath);
1723     if (n_added > 1) {
1724         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1725     } else if (n_added > 0) {
1726         sp_nodepath_update_repr(nodepath, _("Add node"));
1727     }
1729     sp_nodepath_update_statusbar(nodepath);
1732 /**
1733  * Select segment nearest to point
1734  */
1735 void
1736 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1738     if (!nodepath) {
1739         return;
1740     }
1742     sp_nodepath_ensure_livarot_path(nodepath);
1743     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1744     if (!maybe_position) {
1745         return;
1746     }
1747     Path::cut_position position = *maybe_position;
1749     //find segment to segment
1750     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1752     //fixme: this can return NULL, so check before proceeding.
1753     g_return_if_fail(e != NULL);
1755     gboolean force = FALSE;
1756     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1757         force = TRUE;
1758     }
1759     sp_nodepath_node_select(e, (gboolean) toggle, force);
1760     if (e->p.other)
1761         sp_nodepath_node_select(e->p.other, TRUE, force);
1763     sp_nodepath_update_handles(nodepath);
1765     sp_nodepath_update_statusbar(nodepath);
1768 /**
1769  * Add a node nearest to point
1770  */
1771 void
1772 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1774     if (!nodepath) {
1775         return;
1776     }
1778     sp_nodepath_ensure_livarot_path(nodepath);
1779     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1780     if (!maybe_position) {
1781         return;
1782     }
1783     Path::cut_position position = *maybe_position;
1785     //find segment to split
1786     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1788     //don't know why but t seems to flip for lines
1789     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1790         position.t = 1.0 - position.t;
1791     }
1792     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1793     sp_nodepath_node_select(n, FALSE, TRUE);
1795     /* fixme: adjust ? */
1796     sp_nodepath_update_handles(nodepath);
1798     sp_nodepath_update_repr(nodepath, _("Add node"));
1800     sp_nodepath_update_statusbar(nodepath);
1803 /*
1804  * Adjusts a segment so that t moves by a certain delta for dragging
1805  * converts lines to curves
1806  *
1807  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1808  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1809  */
1810 void
1811 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1813     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1815     //fixme: e and e->p can be NULL, so check for those before proceeding
1816     g_return_if_fail(e != NULL);
1817     g_return_if_fail(&e->p != NULL);
1819     /* feel good is an arbitrary parameter that distributes the delta between handles
1820      * if t of the drag point is less than 1/6 distance form the endpoint only
1821      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1822      */
1823     double feel_good;
1824     if (t <= 1.0 / 6.0)
1825         feel_good = 0;
1826     else if (t <= 0.5)
1827         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1828     else if (t <= 5.0 / 6.0)
1829         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1830     else
1831         feel_good = 1;
1833     //if we're dragging a line convert it to a curve
1834     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1835         sp_nodepath_set_line_type(e, NR_CURVETO);
1836     }
1838     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1839     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1840     e->p.other->n.pos += offsetcoord0;
1841     e->p.pos += offsetcoord1;
1843     // adjust handles of adjacent nodes where necessary
1844     sp_node_adjust_handle(e,1);
1845     sp_node_adjust_handle(e->p.other,-1);
1847     sp_nodepath_update_handles(e->subpath->nodepath);
1849     update_object(e->subpath->nodepath);
1851     sp_nodepath_update_statusbar(e->subpath->nodepath);
1855 /**
1856  * Call sp_nodepath_break() for all selected segments.
1857  */
1858 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1860     if (!nodepath) return;
1862     GList *temp = NULL;
1863     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1864        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1865        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1866         if (nn == NULL) continue; // no break, no new node
1867         temp = g_list_prepend(temp, nn);
1868     }
1870     if (temp) {
1871         sp_nodepath_deselect(nodepath);
1872     }
1873     for (GList *l = temp; l != NULL; l = l->next) {
1874         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1875     }
1877     sp_nodepath_update_handles(nodepath);
1879     sp_nodepath_update_repr(nodepath, _("Break path"));
1882 /**
1883  * Duplicate the selected node(s).
1884  */
1885 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1887     if (!nodepath) {
1888         return;
1889     }
1891     GList *temp = NULL;
1892     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1893        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1894        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1895         if (nn == NULL) continue; // could not duplicate
1896         temp = g_list_prepend(temp, nn);
1897     }
1899     if (temp) {
1900         sp_nodepath_deselect(nodepath);
1901     }
1902     for (GList *l = temp; l != NULL; l = l->next) {
1903         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1904     }
1906     sp_nodepath_update_handles(nodepath);
1908     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1911 /**
1912  *  Internal function to join two nodes by merging them into one.
1913  */
1914 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
1916     /* a and b are endpoints */
1918     NR::Point c;
1919     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1920         c = a->pos;
1921     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1922         c = b->pos;
1923     } else {
1924         c = (a->pos + b->pos) / 2;
1925     }
1927     if (a->subpath == b->subpath) {
1928        Inkscape::NodePath::SubPath *sp = a->subpath;
1929         sp_nodepath_subpath_close(sp);
1930         sp_node_moveto (sp->first, c);
1932         sp_nodepath_update_handles(sp->nodepath);
1933         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1934         return;
1935     }
1937     /* a and b are separate subpaths */
1938     Inkscape::NodePath::SubPath *sa = a->subpath;
1939     Inkscape::NodePath::SubPath *sb = b->subpath;
1940     NR::Point p;
1941     Inkscape::NodePath::Node *n;
1942     NRPathcode code;
1943     if (a == sa->first) {
1944         p = sa->first->n.pos;
1945         code = (NRPathcode)sa->first->n.other->code;
1946        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1947         n = sa->last;
1948         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1949         n = n->p.other;
1950         while (n) {
1951             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1952             n = n->p.other;
1953             if (n == sa->first) n = NULL;
1954         }
1955         sp_nodepath_subpath_destroy(sa);
1956         sa = t;
1957     } else if (a == sa->last) {
1958         p = sa->last->p.pos;
1959         code = (NRPathcode)sa->last->code;
1960         sp_nodepath_node_destroy(sa->last);
1961     } else {
1962         code = NR_END;
1963         g_assert_not_reached();
1964     }
1966     if (b == sb->first) {
1967         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1968         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1969             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1970         }
1971     } else if (b == sb->last) {
1972         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1973         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1974             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1975         }
1976     } else {
1977         g_assert_not_reached();
1978     }
1979     /* and now destroy sb */
1981     sp_nodepath_subpath_destroy(sb);
1983     sp_nodepath_update_handles(sa->nodepath);
1985     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1987     sp_nodepath_update_statusbar(nodepath);
1990 /**
1991  *  Internal function to join two nodes by adding a segment between them.
1992  */
1993 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
1995     if (a->subpath == b->subpath) {
1996        Inkscape::NodePath::SubPath *sp = a->subpath;
1998         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1999         sp->closed = TRUE;
2001         sp->first->p.other = sp->last;
2002         sp->last->n.other  = sp->first;
2004         sp_node_handle_mirror_p_to_n(sp->last);
2005         sp_node_handle_mirror_n_to_p(sp->first);
2007         sp->first->code = sp->last->code;
2008         sp->first       = sp->last;
2010         sp_nodepath_update_handles(sp->nodepath);
2012         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2014         return;
2015     }
2017     /* a and b are separate subpaths */
2018     Inkscape::NodePath::SubPath *sa = a->subpath;
2019     Inkscape::NodePath::SubPath *sb = b->subpath;
2021     Inkscape::NodePath::Node *n;
2022     NR::Point p;
2023     NRPathcode code;
2024     if (a == sa->first) {
2025         code = (NRPathcode) sa->first->n.other->code;
2026        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2027         n = sa->last;
2028         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2029         for (n = n->p.other; n != NULL; n = n->p.other) {
2030             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2031         }
2032         sp_nodepath_subpath_destroy(sa);
2033         sa = t;
2034     } else if (a == sa->last) {
2035         code = (NRPathcode)sa->last->code;
2036     } else {
2037         code = NR_END;
2038         g_assert_not_reached();
2039     }
2041     if (b == sb->first) {
2042         n = sb->first;
2043         sp_node_handle_mirror_p_to_n(sa->last);
2044         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2045         sp_node_handle_mirror_n_to_p(sa->last);
2046         for (n = n->n.other; n != NULL; n = n->n.other) {
2047             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2048         }
2049     } else if (b == sb->last) {
2050         n = sb->last;
2051         sp_node_handle_mirror_p_to_n(sa->last);
2052         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2053         sp_node_handle_mirror_n_to_p(sa->last);
2054         for (n = n->p.other; n != NULL; n = n->p.other) {
2055             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2056         }
2057     } else {
2058         g_assert_not_reached();
2059     }
2060     /* and now destroy sb */
2062     sp_nodepath_subpath_destroy(sb);
2064     sp_nodepath_update_handles(sa->nodepath);
2066     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2069 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2071 /**
2072  * Internal function to handle joining two nodes.
2073  */
2074 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2076     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2078     if (g_list_length(nodepath->selected) != 2) {
2079         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2080         return;
2081     }
2083     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2084     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2086     g_assert(a != b);
2087     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2088         // someone tried to join an orphan node (i.e. a single-node subpath).
2089         // this is not worth an error message, just fail silently.
2090         return;
2091     }
2093     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2094         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2095         return;
2096     }
2098     switch(mode) {
2099         case NODE_JOIN_ENDPOINTS:
2100             do_node_selected_join(nodepath, a, b);
2101             break;
2102         case NODE_JOIN_SEGMENT:
2103             do_node_selected_join_segment(nodepath, a, b);
2104             break;
2105     }
2108 /**
2109  *  Join two nodes by merging them into one.
2110  */
2111 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2113     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2116 /**
2117  *  Join two nodes by adding a segment between them.
2118  */
2119 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2121     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2124 /**
2125  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2126  */
2127 void sp_node_delete_preserve(GList *nodes_to_delete)
2129     GSList *nodepaths = NULL;
2131     while (nodes_to_delete) {
2132         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2133         Inkscape::NodePath::SubPath *sp = node->subpath;
2134         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2135         Inkscape::NodePath::Node *sample_cursor = NULL;
2136         Inkscape::NodePath::Node *sample_end = NULL;
2137         Inkscape::NodePath::Node *delete_cursor = node;
2138         bool just_delete = false;
2140         //find the start of this contiguous selection
2141         //move left to the first node that is not selected
2142         //or the start of the non-closed path
2143         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2144             delete_cursor = curr;
2145         }
2147         //just delete at the beginning of an open path
2148         if (!delete_cursor->p.other) {
2149             sample_cursor = delete_cursor;
2150             just_delete = true;
2151         } else {
2152             sample_cursor = delete_cursor->p.other;
2153         }
2155         //calculate points for each segment
2156         int rate = 5;
2157         float period = 1.0 / rate;
2158         std::vector<NR::Point> data;
2159         if (!just_delete) {
2160             data.push_back(sample_cursor->pos);
2161             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2162                 //just delete at the end of an open path
2163                 if (!sp->closed && curr == sp->last) {
2164                     just_delete = true;
2165                     break;
2166                 }
2168                 //sample points on the contiguous selected segment
2169                 NR::Point *bez;
2170                 bez = new NR::Point [4];
2171                 bez[0] = curr->pos;
2172                 bez[1] = curr->n.pos;
2173                 bez[2] = curr->n.other->p.pos;
2174                 bez[3] = curr->n.other->pos;
2175                 for (int i=1; i<rate; i++) {
2176                     gdouble t = i * period;
2177                     NR::Point p = bezier_pt(3, bez, t);
2178                     data.push_back(p);
2179                 }
2180                 data.push_back(curr->n.other->pos);
2182                 sample_end = curr->n.other;
2183                 //break if we've come full circle or hit the end of the selection
2184                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2185                     break;
2186                 }
2187             }
2188         }
2190         if (!just_delete) {
2191             //calculate the best fitting single segment and adjust the endpoints
2192             NR::Point *adata;
2193             adata = new NR::Point [data.size()];
2194             copy(data.begin(), data.end(), adata);
2196             NR::Point *bez;
2197             bez = new NR::Point [4];
2198             //would decreasing error create a better fitting approximation?
2199             gdouble error = 1.0;
2200             gint ret;
2201             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2203             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2204             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2205             //the resulting nodes behave as expected.
2206             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2207             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2209             //adjust endpoints
2210             sample_cursor->n.pos = bez[1];
2211             sample_end->p.pos = bez[2];
2212         }
2214         //destroy this contiguous selection
2215         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2216             Inkscape::NodePath::Node *temp = delete_cursor;
2217             if (delete_cursor->n.other == delete_cursor) {
2218                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2219                 delete_cursor = NULL;
2220             } else {
2221                 delete_cursor = delete_cursor->n.other;
2222             }
2223             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2224             sp_nodepath_node_destroy(temp);
2225         }
2227         sp_nodepath_update_handles(nodepath);
2229         if (!g_slist_find(nodepaths, nodepath))
2230             nodepaths = g_slist_prepend (nodepaths, nodepath);
2231     }
2233     for (GSList *i = nodepaths; i; i = i->next) {
2234         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2235         // different nodepaths will give us one undo event per nodepath
2236         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2238         // if the entire nodepath is removed, delete the selected object.
2239         if (nodepath->subpaths == NULL ||
2240             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2241             //at least 2
2242             sp_nodepath_get_node_count(nodepath) < 2) {
2243             SPDocument *document = sp_desktop_document (nodepath->desktop);
2244             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2245             //delete this nodepath's object, not the entire selection! (though at this time, this
2246             //does not matter)
2247             sp_selection_delete();
2248             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2249                               _("Delete nodes"));
2250         } else {
2251             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2252             sp_nodepath_update_statusbar(nodepath);
2253         }
2254     }
2256     g_slist_free (nodepaths);
2259 /**
2260  * Delete one or more selected nodes.
2261  */
2262 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2264     if (!nodepath) return;
2265     if (!nodepath->selected) return;
2267     /** \todo fixme: do it the right way */
2268     while (nodepath->selected) {
2269        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2270         sp_nodepath_node_destroy(node);
2271     }
2274     //clean up the nodepath (such as for trivial subpaths)
2275     sp_nodepath_cleanup(nodepath);
2277     sp_nodepath_update_handles(nodepath);
2279     // if the entire nodepath is removed, delete the selected object.
2280     if (nodepath->subpaths == NULL ||
2281         sp_nodepath_get_node_count(nodepath) < 2) {
2282         SPDocument *document = sp_desktop_document (nodepath->desktop);
2283         sp_selection_delete();
2284         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2285                           _("Delete nodes"));
2286         return;
2287     }
2289     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2291     sp_nodepath_update_statusbar(nodepath);
2294 /**
2295  * Delete one or more segments between two selected nodes.
2296  * This is the code for 'split'.
2297  */
2298 void
2299 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2301    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2302    Inkscape::NodePath::Node *curr, *next;     //Iterators
2304     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2306     if (g_list_length(nodepath->selected) != 2) {
2307         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2308                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2309         return;
2310     }
2312     //Selected nodes, not inclusive
2313    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2314    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2316     if ( ( a==b)                       ||  //same node
2317          (a->subpath  != b->subpath )  ||  //not the same path
2318          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2319          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2320     {
2321         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2322                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2323         return;
2324     }
2326     //###########################################
2327     //# BEGIN EDITS
2328     //###########################################
2329     //##################################
2330     //# CLOSED PATH
2331     //##################################
2332     if (a->subpath->closed) {
2335         gboolean reversed = FALSE;
2337         //Since we can go in a circle, we need to find the shorter distance.
2338         //  a->b or b->a
2339         start = end = NULL;
2340         int distance    = 0;
2341         int minDistance = 0;
2342         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2343             if (curr==b) {
2344                 //printf("a to b:%d\n", distance);
2345                 start = a;//go from a to b
2346                 end   = b;
2347                 minDistance = distance;
2348                 //printf("A to B :\n");
2349                 break;
2350             }
2351             distance++;
2352         }
2354         //try again, the other direction
2355         distance = 0;
2356         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2357             if (curr==a) {
2358                 //printf("b to a:%d\n", distance);
2359                 if (distance < minDistance) {
2360                     start    = b;  //we go from b to a
2361                     end      = a;
2362                     reversed = TRUE;
2363                     //printf("B to A\n");
2364                 }
2365                 break;
2366             }
2367             distance++;
2368         }
2371         //Copy everything from 'end' to 'start' to a new subpath
2372        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2373         for (curr=end ; curr ; curr=curr->n.other) {
2374             NRPathcode code = (NRPathcode) curr->code;
2375             if (curr == end)
2376                 code = NR_MOVETO;
2377             sp_nodepath_node_new(t, NULL,
2378                                  (Inkscape::NodePath::NodeType)curr->type, code,
2379                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2380             if (curr == start)
2381                 break;
2382         }
2383         sp_nodepath_subpath_destroy(a->subpath);
2386     }
2390     //##################################
2391     //# OPEN PATH
2392     //##################################
2393     else {
2395         //We need to get the direction of the list between A and B
2396         //Can we walk from a to b?
2397         start = end = NULL;
2398         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2399             if (curr==b) {
2400                 start = a;  //did it!  we go from a to b
2401                 end   = b;
2402                 //printf("A to B\n");
2403                 break;
2404             }
2405         }
2406         if (!start) {//didn't work?  let's try the other direction
2407             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2408                 if (curr==a) {
2409                     start = b;  //did it!  we go from b to a
2410                     end   = a;
2411                     //printf("B to A\n");
2412                     break;
2413                 }
2414             }
2415         }
2416         if (!start) {
2417             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2418                                                      _("Cannot find path between nodes."));
2419             return;
2420         }
2424         //Copy everything after 'end' to a new subpath
2425        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2426         for (curr=end ; curr ; curr=curr->n.other) {
2427             NRPathcode code = (NRPathcode) curr->code;
2428             if (curr == end)
2429                 code = NR_MOVETO;
2430             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2431                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2432         }
2434         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2435         for (curr = start->n.other ; curr  ; curr=next) {
2436             next = curr->n.other;
2437             sp_nodepath_node_destroy(curr);
2438         }
2440     }
2441     //###########################################
2442     //# END EDITS
2443     //###########################################
2445     //clean up the nodepath (such as for trivial subpaths)
2446     sp_nodepath_cleanup(nodepath);
2448     sp_nodepath_update_handles(nodepath);
2450     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2452     sp_nodepath_update_statusbar(nodepath);
2455 /**
2456  * Call sp_nodepath_set_line() for all selected segments.
2457  */
2458 void
2459 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2461     if (nodepath == NULL) return;
2463     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2464        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2465         g_assert(n->selected);
2466         if (n->p.other && n->p.other->selected) {
2467             sp_nodepath_set_line_type(n, code);
2468         }
2469     }
2471     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2474 /**
2475  * Call sp_nodepath_convert_node_type() for all selected nodes.
2476  */
2477 void
2478 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2480     if (nodepath == NULL) return;
2482     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2484     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2485         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2486     }
2488     sp_nodepath_update_repr(nodepath, _("Change node type"));
2491 /**
2492  * Change select status of node, update its own and neighbour handles.
2493  */
2494 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2496     node->selected = selected;
2498     if (selected) {
2499         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2500         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2501         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2502         sp_knot_update_ctrl(node->knot);
2503     } else {
2504         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2505         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2506         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2507         sp_knot_update_ctrl(node->knot);
2508     }
2510     sp_node_update_handles(node);
2511     if (node->n.other) sp_node_update_handles(node->n.other);
2512     if (node->p.other) sp_node_update_handles(node->p.other);
2515 /**
2516 \brief Select a node
2517 \param node     The node to select
2518 \param incremental   If true, add to selection, otherwise deselect others
2519 \param override   If true, always select this node, otherwise toggle selected status
2520 */
2521 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2523     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2525     if (incremental) {
2526         if (override) {
2527             if (!g_list_find(nodepath->selected, node)) {
2528                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2529             }
2530             sp_node_set_selected(node, TRUE);
2531         } else { // toggle
2532             if (node->selected) {
2533                 g_assert(g_list_find(nodepath->selected, node));
2534                 nodepath->selected = g_list_remove(nodepath->selected, node);
2535             } else {
2536                 g_assert(!g_list_find(nodepath->selected, node));
2537                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2538             }
2539             sp_node_set_selected(node, !node->selected);
2540         }
2541     } else {
2542         sp_nodepath_deselect(nodepath);
2543         nodepath->selected = g_list_prepend(nodepath->selected, node);
2544         sp_node_set_selected(node, TRUE);
2545     }
2547     sp_nodepath_update_statusbar(nodepath);
2551 /**
2552 \brief Deselect all nodes in the nodepath
2553 */
2554 void
2555 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2557     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2559     while (nodepath->selected) {
2560         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2561         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2562     }
2563     sp_nodepath_update_statusbar(nodepath);
2566 /**
2567 \brief Select or invert selection of all nodes in the nodepath
2568 */
2569 void
2570 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2572     if (!nodepath) return;
2574     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2575        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2576         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2577            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2578            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2579         }
2580     }
2583 /**
2584  * If nothing selected, does the same as sp_nodepath_select_all();
2585  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2586  * (i.e., similar to "select all in layer", with the "selected" subpaths
2587  * being treated as "layers" in the path).
2588  */
2589 void
2590 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2592     if (!nodepath) return;
2594     if (g_list_length (nodepath->selected) == 0) {
2595         sp_nodepath_select_all (nodepath, invert);
2596         return;
2597     }
2599     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2600     GSList *subpaths = NULL;
2602     for (GList *l = copy; l != NULL; l = l->next) {
2603         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2604         Inkscape::NodePath::SubPath *subpath = n->subpath;
2605         if (!g_slist_find (subpaths, subpath))
2606             subpaths = g_slist_prepend (subpaths, subpath);
2607     }
2609     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2610         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2611         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2612             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2613             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2614         }
2615     }
2617     g_slist_free (subpaths);
2618     g_list_free (copy);
2621 /**
2622  * \brief Select the node after the last selected; if none is selected,
2623  * select the first within path.
2624  */
2625 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2627     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2629    Inkscape::NodePath::Node *last = NULL;
2630     if (nodepath->selected) {
2631         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2632            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2633             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2634             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2635                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2636                 if (node->selected) {
2637                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2638                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2639                             if (spl->next) { // there's a next subpath
2640                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2641                                 last = subpath_next->first;
2642                             } else if (spl->prev) { // there's a previous subpath
2643                                 last = NULL; // to be set later to the first node of first subpath
2644                             } else {
2645                                 last = node->n.other;
2646                             }
2647                         } else {
2648                             last = node->n.other;
2649                         }
2650                     } else {
2651                         if (node->n.other) {
2652                             last = node->n.other;
2653                         } else {
2654                             if (spl->next) { // there's a next subpath
2655                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2656                                 last = subpath_next->first;
2657                             } else if (spl->prev) { // there's a previous subpath
2658                                 last = NULL; // to be set later to the first node of first subpath
2659                             } else {
2660                                 last = (Inkscape::NodePath::Node *) subpath->first;
2661                             }
2662                         }
2663                     }
2664                 }
2665             }
2666         }
2667         sp_nodepath_deselect(nodepath);
2668     }
2670     if (last) { // there's at least one more node after selected
2671         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2672     } else { // no more nodes, select the first one in first subpath
2673        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2674         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2675     }
2678 /**
2679  * \brief Select the node before the first selected; if none is selected,
2680  * select the last within path
2681  */
2682 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2684     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2686    Inkscape::NodePath::Node *last = NULL;
2687     if (nodepath->selected) {
2688         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2689            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2690             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2691                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2692                 if (node->selected) {
2693                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2694                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2695                             if (spl->prev) { // there's a prev subpath
2696                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2697                                 last = subpath_prev->last;
2698                             } else if (spl->next) { // there's a next subpath
2699                                 last = NULL; // to be set later to the last node of last subpath
2700                             } else {
2701                                 last = node->p.other;
2702                             }
2703                         } else {
2704                             last = node->p.other;
2705                         }
2706                     } else {
2707                         if (node->p.other) {
2708                             last = node->p.other;
2709                         } else {
2710                             if (spl->prev) { // there's a prev subpath
2711                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2712                                 last = subpath_prev->last;
2713                             } else if (spl->next) { // there's a next subpath
2714                                 last = NULL; // to be set later to the last node of last subpath
2715                             } else {
2716                                 last = (Inkscape::NodePath::Node *) subpath->last;
2717                             }
2718                         }
2719                     }
2720                 }
2721             }
2722         }
2723         sp_nodepath_deselect(nodepath);
2724     }
2726     if (last) { // there's at least one more node before selected
2727         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2728     } else { // no more nodes, select the last one in last subpath
2729         GList *spl = g_list_last(nodepath->subpaths);
2730        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2731         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2732     }
2735 /**
2736  * \brief Select all nodes that are within the rectangle.
2737  */
2738 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2740     if (!incremental) {
2741         sp_nodepath_deselect(nodepath);
2742     }
2744     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2745        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2746         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2747            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2749             if (b.contains(node->pos)) {
2750                 sp_nodepath_node_select(node, TRUE, TRUE);
2751             }
2752         }
2753     }
2757 void
2758 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2760     g_assert (n);
2761     g_assert (nodepath);
2762     g_assert (n->subpath->nodepath == nodepath);
2764     if (g_list_length (nodepath->selected) == 0) {
2765         if (grow > 0) {
2766             sp_nodepath_node_select(n, TRUE, TRUE);
2767         }
2768         return;
2769     }
2771     if (g_list_length (nodepath->selected) == 1) {
2772         if (grow < 0) {
2773             sp_nodepath_deselect (nodepath);
2774             return;
2775         }
2776     }
2778         double n_sel_range = 0, p_sel_range = 0;
2779             Inkscape::NodePath::Node *farthest_n_node = n;
2780             Inkscape::NodePath::Node *farthest_p_node = n;
2782         // Calculate ranges
2783         {
2784             double n_range = 0, p_range = 0;
2785             bool n_going = true, p_going = true;
2786             Inkscape::NodePath::Node *n_node = n;
2787             Inkscape::NodePath::Node *p_node = n;
2788             do {
2789                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2790                 if (n_node && n_going)
2791                     n_node = n_node->n.other;
2792                 if (n_node == NULL) {
2793                     n_going = false;
2794                 } else {
2795                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2796                     if (n_node->selected) {
2797                         n_sel_range = n_range;
2798                         farthest_n_node = n_node;
2799                     }
2800                     if (n_node == p_node) {
2801                         n_going = false;
2802                         p_going = false;
2803                     }
2804                 }
2805                 if (p_node && p_going)
2806                     p_node = p_node->p.other;
2807                 if (p_node == NULL) {
2808                     p_going = false;
2809                 } else {
2810                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2811                     if (p_node->selected) {
2812                         p_sel_range = p_range;
2813                         farthest_p_node = p_node;
2814                     }
2815                     if (p_node == n_node) {
2816                         n_going = false;
2817                         p_going = false;
2818                     }
2819                 }
2820             } while (n_going || p_going);
2821         }
2823     if (grow > 0) {
2824         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2825                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2826         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2827                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2828         }
2829     } else {
2830         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2831                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2832         } else if (farthest_p_node && farthest_p_node->selected) {
2833                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2834         }
2835     }
2838 void
2839 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2841     g_assert (n);
2842     g_assert (nodepath);
2843     g_assert (n->subpath->nodepath == nodepath);
2845     if (g_list_length (nodepath->selected) == 0) {
2846         if (grow > 0) {
2847             sp_nodepath_node_select(n, TRUE, TRUE);
2848         }
2849         return;
2850     }
2852     if (g_list_length (nodepath->selected) == 1) {
2853         if (grow < 0) {
2854             sp_nodepath_deselect (nodepath);
2855             return;
2856         }
2857     }
2859     Inkscape::NodePath::Node *farthest_selected = NULL;
2860     double farthest_dist = 0;
2862     Inkscape::NodePath::Node *closest_unselected = NULL;
2863     double closest_dist = NR_HUGE;
2865     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2866        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2867         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2868            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2869            if (node == n)
2870                continue;
2871            if (node->selected) {
2872                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2873                    farthest_dist = NR::L2(node->pos - n->pos);
2874                    farthest_selected = node;
2875                }
2876            } else {
2877                if (NR::L2(node->pos - n->pos) < closest_dist) {
2878                    closest_dist = NR::L2(node->pos - n->pos);
2879                    closest_unselected = node;
2880                }
2881            }
2882         }
2883     }
2885     if (grow > 0) {
2886         if (closest_unselected) {
2887             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2888         }
2889     } else {
2890         if (farthest_selected) {
2891             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2892         }
2893     }
2897 /**
2898 \brief  Saves all nodes' and handles' current positions in their origin members
2899 */
2900 void
2901 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2903     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2904        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2905         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2906            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2907            n->origin = n->pos;
2908            n->p.origin = n->p.pos;
2909            n->n.origin = n->n.pos;
2910         }
2911     }
2914 /**
2915 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2916 */
2917 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2919     if (!nodepath->selected) {
2920         return NULL;
2921     }
2923     GList *r = NULL;
2924     guint i = 0;
2925     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2926        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2927         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2928            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2929             i++;
2930             if (node->selected) {
2931                 r = g_list_append(r, GINT_TO_POINTER(i));
2932             }
2933         }
2934     }
2935     return r;
2938 /**
2939 \brief  Restores selection by selecting nodes whose positions are in the list
2940 */
2941 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2943     sp_nodepath_deselect(nodepath);
2945     guint i = 0;
2946     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2947        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2948         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2949            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2950             i++;
2951             if (g_list_find(r, GINT_TO_POINTER(i))) {
2952                 sp_nodepath_node_select(node, TRUE, TRUE);
2953             }
2954         }
2955     }
2959 /**
2960 \brief Adjusts handle according to node type and line code.
2961 */
2962 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2964     double len, otherlen, linelen;
2966     g_assert(node);
2968    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2969    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2971     /** \todo fixme: */
2972     if (me->other == NULL) return;
2973     if (other->other == NULL) return;
2975     /* I have line */
2977     NRPathcode mecode, ocode;
2978     if (which_adjust == 1) {
2979         mecode = (NRPathcode)me->other->code;
2980         ocode = (NRPathcode)node->code;
2981     } else {
2982         mecode = (NRPathcode)node->code;
2983         ocode = (NRPathcode)other->other->code;
2984     }
2986     if (mecode == NR_LINETO) return;
2988     /* I am curve */
2990     if (other->other == NULL) return;
2992     /* Other has line */
2994     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2996     NR::Point delta;
2997     if (ocode == NR_LINETO) {
2998         /* other is lineto, we are either smooth or symm */
2999        Inkscape::NodePath::Node *othernode = other->other;
3000         len = NR::L2(me->pos - node->pos);
3001         delta = node->pos - othernode->pos;
3002         linelen = NR::L2(delta);
3003         if (linelen < 1e-18)
3004             return;
3005         me->pos = node->pos + (len / linelen)*delta;
3006         return;
3007     }
3009     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3011         me->pos = 2 * node->pos - other->pos;
3012         return;
3013     }
3015     /* We are smooth */
3017     len = NR::L2(me->pos - node->pos);
3018     delta = other->pos - node->pos;
3019     otherlen = NR::L2(delta);
3020     if (otherlen < 1e-18) return;
3022     me->pos = node->pos - (len / otherlen) * delta;
3025 /**
3026  \brief Adjusts both handles according to node type and line code
3027  */
3028 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3030     g_assert(node);
3032     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3034     /* we are either smooth or symm */
3036     if (node->p.other == NULL) return;
3038     if (node->n.other == NULL) return;
3040     if (node->code == NR_LINETO) {
3041         if (node->n.other->code == NR_LINETO) return;
3042         sp_node_adjust_handle(node, 1);
3043         return;
3044     }
3046     if (node->n.other->code == NR_LINETO) {
3047         if (node->code == NR_LINETO) return;
3048         sp_node_adjust_handle(node, -1);
3049         return;
3050     }
3052     /* both are curves */
3053     NR::Point const delta( node->n.pos - node->p.pos );
3055     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3056         node->p.pos = node->pos - delta / 2;
3057         node->n.pos = node->pos + delta / 2;
3058         return;
3059     }
3061     /* We are smooth */
3062     double plen = NR::L2(node->p.pos - node->pos);
3063     if (plen < 1e-18) return;
3064     double nlen = NR::L2(node->n.pos - node->pos);
3065     if (nlen < 1e-18) return;
3066     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3067     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3070 /**
3071  * Node event callback.
3072  */
3073 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3075     gboolean ret = FALSE;
3076     switch (event->type) {
3077         case GDK_ENTER_NOTIFY:
3078             Inkscape::NodePath::Path::active_node = n;
3079             break;
3080         case GDK_LEAVE_NOTIFY:
3081             Inkscape::NodePath::Path::active_node = NULL;
3082             break;
3083         case GDK_SCROLL:
3084             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3085                 switch (event->scroll.direction) {
3086                     case GDK_SCROLL_UP:
3087                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3088                         break;
3089                     case GDK_SCROLL_DOWN:
3090                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3091                         break;
3092                     default:
3093                         break;
3094                 }
3095                 ret = TRUE;
3096             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3097                 switch (event->scroll.direction) {
3098                     case GDK_SCROLL_UP:
3099                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3100                         break;
3101                     case GDK_SCROLL_DOWN:
3102                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3103                         break;
3104                     default:
3105                         break;
3106                 }
3107                 ret = TRUE;
3108             }
3109             break;
3110         case GDK_KEY_PRESS:
3111             switch (get_group0_keyval (&event->key)) {
3112                 case GDK_space:
3113                     if (event->key.state & GDK_BUTTON1_MASK) {
3114                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3115                         stamp_repr(nodepath);
3116                         ret = TRUE;
3117                     }
3118                     break;
3119                 case GDK_Page_Up:
3120                     if (event->key.state & GDK_CONTROL_MASK) {
3121                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3122                     } else {
3123                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3124                     }
3125                     break;
3126                 case GDK_Page_Down:
3127                     if (event->key.state & GDK_CONTROL_MASK) {
3128                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3129                     } else {
3130                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3131                     }
3132                     break;
3133                 default:
3134                     break;
3135             }
3136             break;
3137         default:
3138             break;
3139     }
3141     return ret;
3144 /**
3145  * Handle keypress on node; directly called.
3146  */
3147 gboolean node_key(GdkEvent *event)
3149     Inkscape::NodePath::Path *np;
3151     // there is no way to verify nodes so set active_node to nil when deleting!!
3152     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3154     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3155         gint ret = FALSE;
3156         switch (get_group0_keyval (&event->key)) {
3157             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3158             case GDK_BackSpace:
3159                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3160                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3161                 sp_nodepath_update_repr(np, _("Delete node"));
3162                 Inkscape::NodePath::Path::active_node = NULL;
3163                 ret = TRUE;
3164                 break;
3165             case GDK_c:
3166                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3167                 ret = TRUE;
3168                 break;
3169             case GDK_s:
3170                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3171                 ret = TRUE;
3172                 break;
3173             case GDK_y:
3174                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3175                 ret = TRUE;
3176                 break;
3177             case GDK_b:
3178                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3179                 ret = TRUE;
3180                 break;
3181         }
3182         return ret;
3183     }
3184     return FALSE;
3187 /**
3188  * Mouseclick on node callback.
3189  */
3190 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3192    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3194     if (state & GDK_CONTROL_MASK) {
3195         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3197         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3198             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3199                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3200             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3201                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3202             } else {
3203                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3204             }
3205             sp_nodepath_update_repr(nodepath, _("Change node type"));
3206             sp_nodepath_update_statusbar(nodepath);
3208         } else { //ctrl+alt+click: delete node
3209             GList *node_to_delete = NULL;
3210             node_to_delete = g_list_append(node_to_delete, n);
3211             sp_node_delete_preserve(node_to_delete);
3212         }
3214     } else {
3215         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3216     }
3219 /**
3220  * Mouse grabbed node callback.
3221  */
3222 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3224    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3226     if (!n->selected) {
3227         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3228     }
3230     n->is_dragging = true;
3231     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3233     sp_nodepath_remember_origins (n->subpath->nodepath);
3236 /**
3237  * Mouse ungrabbed node callback.
3238  */
3239 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3241    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3243    n->dragging_out = NULL;
3244    n->is_dragging = false;
3245    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3247    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3250 /**
3251  * The point on a line, given by its angle, closest to the given point.
3252  * \param p  A point.
3253  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3254  * \param closest  Pointer to the point struct where the result is stored.
3255  * \todo FIXME: use dot product perhaps?
3256  */
3257 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3259     if (a == HUGE_VAL) { // vertical
3260         *closest = NR::Point(0, (*p)[NR::Y]);
3261     } else {
3262         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3263         (*closest)[NR::Y] = a * (*closest)[NR::X];
3264     }
3267 /**
3268  * Distance from the point to a line given by its angle.
3269  * \param p  A point.
3270  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3271  */
3272 static double point_line_distance(NR::Point *p, double a)
3274     NR::Point c;
3275     point_line_closest(p, a, &c);
3276     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]));
3279 /**
3280  * Callback for node "request" signal.
3281  * \todo fixme: This goes to "moved" event? (lauris)
3282  */
3283 static gboolean
3284 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3286     double yn, xn, yp, xp;
3287     double an, ap, na, pa;
3288     double d_an, d_ap, d_na, d_pa;
3289     gboolean collinear = FALSE;
3290     NR::Point c;
3291     NR::Point pr;
3293    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3295     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3297    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3298     if ( (!n->subpath->nodepath->straight_path) &&
3299          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3300            || n->dragging_out ) )
3301     {
3302        NR::Point mouse = (*p);
3304        if (!n->dragging_out) {
3305            // This is the first drag-out event; find out which handle to drag out
3306            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3307            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3309            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3310                return FALSE;
3312            Inkscape::NodePath::NodeSide *opposite;
3313            if (appr_p > appr_n) { // closer to p
3314                n->dragging_out = &n->p;
3315                opposite = &n->n;
3316                n->code = NR_CURVETO;
3317            } else if (appr_p < appr_n) { // closer to n
3318                n->dragging_out = &n->n;
3319                opposite = &n->p;
3320                n->n.other->code = NR_CURVETO;
3321            } else { // p and n nodes are the same
3322                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3323                    n->dragging_out = &n->p;
3324                    opposite = &n->n;
3325                    n->code = NR_CURVETO;
3326                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3327                    n->dragging_out = &n->n;
3328                    opposite = &n->p;
3329                    n->n.other->code = NR_CURVETO;
3330                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3331                    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);
3332                    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);
3333                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3334                        n->dragging_out = &n->n;
3335                        opposite = &n->p;
3336                        n->n.other->code = NR_CURVETO;
3337                    } else { // closer to other's n handle
3338                        n->dragging_out = &n->p;
3339                        opposite = &n->n;
3340                        n->code = NR_CURVETO;
3341                    }
3342                }
3343            }
3345            // if there's another handle, make sure the one we drag out starts parallel to it
3346            if (opposite->pos != n->pos) {
3347                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3348            }
3350            // knots might not be created yet!
3351            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3352            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3353        }
3355        // pass this on to the handle-moved callback
3356        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3357        sp_node_update_handles(n);
3358        return TRUE;
3359    }
3361     if (state & GDK_CONTROL_MASK) { // constrained motion
3363         // calculate relative distances of handles
3364         // n handle:
3365         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3366         xn = n->n.pos[NR::X] - n->pos[NR::X];
3367         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3368         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3369             if (n->n.other) { // if there is the next point
3370                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3371                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3372                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3373             }
3374         }
3375         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3376         if (yn < 0) { xn = -xn; yn = -yn; }
3378         // p handle:
3379         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3380         xp = n->p.pos[NR::X] - n->pos[NR::X];
3381         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3382         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3383             if (n->p.other) {
3384                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3385                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3386                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3387             }
3388         }
3389         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3390         if (yp < 0) { xp = -xp; yp = -yp; }
3392         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3393             // sliding on handles, only if at least one of the handles is non-vertical
3394             // (otherwise it's the same as ctrl+drag anyway)
3396             // calculate angles of the handles
3397             if (xn == 0) {
3398                 if (yn == 0) { // no handle, consider it the continuation of the other one
3399                     an = 0;
3400                     collinear = TRUE;
3401                 }
3402                 else an = 0; // vertical; set the angle to horizontal
3403             } else an = yn/xn;
3405             if (xp == 0) {
3406                 if (yp == 0) { // no handle, consider it the continuation of the other one
3407                     ap = an;
3408                 }
3409                 else ap = 0; // vertical; set the angle to horizontal
3410             } else  ap = yp/xp;
3412             if (collinear) an = ap;
3414             // angles of the perpendiculars; HUGE_VAL means vertical
3415             if (an == 0) na = HUGE_VAL; else na = -1/an;
3416             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3418             // mouse point relative to the node's original pos
3419             pr = (*p) - n->origin;
3421             // distances to the four lines (two handles and two perpendiculars)
3422             d_an = point_line_distance(&pr, an);
3423             d_na = point_line_distance(&pr, na);
3424             d_ap = point_line_distance(&pr, ap);
3425             d_pa = point_line_distance(&pr, pa);
3427             // find out which line is the closest, save its closest point in c
3428             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3429                 point_line_closest(&pr, an, &c);
3430             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3431                 point_line_closest(&pr, ap, &c);
3432             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3433                 point_line_closest(&pr, na, &c);
3434             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3435                 point_line_closest(&pr, pa, &c);
3436             }
3438             // move the node to the closest point
3439             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3440                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3441                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3443         } else {  // constraining to hor/vert
3445             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3446                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3447             } else { // snap to vert
3448                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3449             }
3450         }
3451     } else { // move freely
3452         if (n->is_dragging) {
3453             if (state & GDK_MOD1_MASK) { // sculpt
3454                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3455             } else {
3456                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3457                                             (*p)[NR::X] - n->pos[NR::X],
3458                                             (*p)[NR::Y] - n->pos[NR::Y],
3459                                             (state & GDK_SHIFT_MASK) == 0);
3460             }
3461         }
3462     }
3464     n->subpath->nodepath->desktop->scroll_to_point(p);
3466     return TRUE;
3469 /**
3470  * Node handle clicked callback.
3471  */
3472 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3474    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3476     if (state & GDK_CONTROL_MASK) { // "delete" handle
3477         if (n->p.knot == knot) {
3478             n->p.pos = n->pos;
3479         } else if (n->n.knot == knot) {
3480             n->n.pos = n->pos;
3481         }
3482         sp_node_update_handles(n);
3483         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3484         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3485         sp_nodepath_update_statusbar(nodepath);
3487     } else { // just select or add to selection, depending in Shift
3488         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3489     }
3492 /**
3493  * Node handle grabbed callback.
3494  */
3495 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3497    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3499     if (!n->selected) {
3500         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3501     }
3503     // remember the origin point of the handle
3504     if (n->p.knot == knot) {
3505         n->p.origin_radial = n->p.pos - n->pos;
3506     } else if (n->n.knot == knot) {
3507         n->n.origin_radial = n->n.pos - n->pos;
3508     } else {
3509         g_assert_not_reached();
3510     }
3512     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3515 /**
3516  * Node handle ungrabbed callback.
3517  */
3518 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3520    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3522     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3523     if (n->p.knot == knot) {
3524         n->p.origin_radial.a = 0;
3525         sp_knot_set_position(knot, &n->p.pos, state);
3526     } else if (n->n.knot == knot) {
3527         n->n.origin_radial.a = 0;
3528         sp_knot_set_position(knot, &n->n.pos, state);
3529     } else {
3530         g_assert_not_reached();
3531     }
3533     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3536 /**
3537  * Node handle "request" signal callback.
3538  */
3539 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3541     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3543     Inkscape::NodePath::NodeSide *me, *opposite;
3544     gint which;
3545     if (n->p.knot == knot) {
3546         me = &n->p;
3547         opposite = &n->n;
3548         which = -1;
3549     } else if (n->n.knot == knot) {
3550         me = &n->n;
3551         opposite = &n->p;
3552         which = 1;
3553     } else {
3554         me = opposite = NULL;
3555         which = 0;
3556         g_assert_not_reached();
3557     }
3559     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3561     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3562     Inkscape::SnappedPoint s ;
3563     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3564         /* We are smooth node adjacent with line */
3565         NR::Point const delta = *p - n->pos;
3566         NR::Coord const len = NR::L2(delta);
3567         Inkscape::NodePath::Node *othernode = opposite->other;
3568         NR::Point const ndelta = n->pos - othernode->pos;
3569         NR::Coord const linelen = NR::L2(ndelta);
3570         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3571             NR::Coord const scal = dot(delta, ndelta) / linelen;
3572             (*p) = n->pos + (scal / linelen) * ndelta;
3573         }
3574         s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), n->subpath->nodepath->item);
3575     } else {
3576         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, n->subpath->nodepath->item);
3577     }
3578     *p = s.getPoint();
3579     if (s.getDistance() < NR_HUGE) {
3580         n->subpath->nodepath->desktop->snapindicator->set_new_snappoint((*p).to_2geom());
3581     }
3583     sp_node_adjust_handle(n, -which);
3585     return FALSE;
3588 /**
3589  * Node handle moved callback.
3590  */
3591 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3593    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3595    Inkscape::NodePath::NodeSide *me;
3596    Inkscape::NodePath::NodeSide *other;
3597     if (n->p.knot == knot) {
3598         me = &n->p;
3599         other = &n->n;
3600     } else if (n->n.knot == knot) {
3601         me = &n->n;
3602         other = &n->p;
3603     } else {
3604         me = NULL;
3605         other = NULL;
3606         g_assert_not_reached();
3607     }
3609     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3610     Radial rme(me->pos - n->pos);
3611     Radial rother(other->pos - n->pos);
3612     Radial rnew(*p - n->pos);
3614     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3615         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3616         /* 0 interpreted as "no snapping". */
3618         // The closest PI/snaps angle, starting from zero.
3619         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3620         if (me->origin_radial.a == HUGE_VAL) {
3621             // ortho doesn't exist: original handle was zero length.
3622             rnew.a = a_snapped;
3623         } else {
3624             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3625              * its opposite and perpendiculars). */
3626             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3628             // Snap to the closest.
3629             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3630                        ? a_snapped
3631                        : a_ortho );
3632         }
3633     }
3635     if (state & GDK_MOD1_MASK) {
3636         // lock handle length
3637         rnew.r = me->origin_radial.r;
3638     }
3640     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3641         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3642         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3643         rother.a += rnew.a - rme.a;
3644         other->pos = NR::Point(rother) + n->pos;
3645         if (other->knot) {
3646             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3647             sp_knot_moveto(other->knot, &other->pos);
3648         }
3649     }
3651     me->pos = NR::Point(rnew) + n->pos;
3652     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3654     // move knot, but without emitting the signal:
3655     // we cannot emit a "moved" signal because we're now processing it
3656     sp_knot_moveto(me->knot, &(me->pos));
3658     update_object(n->subpath->nodepath);
3660     /* status text */
3661     SPDesktop *desktop = n->subpath->nodepath->desktop;
3662     if (!desktop) return;
3663     SPEventContext *ec = desktop->event_context;
3664     if (!ec) return;
3665     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3666     if (!mc) return;
3668     double degrees = 180 / M_PI * rnew.a;
3669     if (degrees > 180) degrees -= 360;
3670     if (degrees < -180) degrees += 360;
3671     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3672         degrees = angle_to_compass (degrees);
3674     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3676     mc->setF(Inkscape::NORMAL_MESSAGE,
3677          _("<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);
3679     g_string_free(length, TRUE);
3682 /**
3683  * Node handle event callback.
3684  */
3685 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3687     gboolean ret = FALSE;
3688     switch (event->type) {
3689         case GDK_KEY_PRESS:
3690             switch (get_group0_keyval (&event->key)) {
3691                 case GDK_space:
3692                     if (event->key.state & GDK_BUTTON1_MASK) {
3693                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3694                         stamp_repr(nodepath);
3695                         ret = TRUE;
3696                     }
3697                     break;
3698                 default:
3699                     break;
3700             }
3701             break;
3702         case GDK_ENTER_NOTIFY:
3703             // we use an experimentally determined threshold that seems to work fine
3704             if (NR::L2(n->pos - knot->pos) < 0.75)
3705                 Inkscape::NodePath::Path::active_node = n;
3706             break;
3707         case GDK_LEAVE_NOTIFY:
3708             // we use an experimentally determined threshold that seems to work fine
3709             if (NR::L2(n->pos - knot->pos) < 0.75)
3710                 Inkscape::NodePath::Path::active_node = NULL;
3711             break;
3712         default:
3713             break;
3714     }
3716     return ret;
3719 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3720                                  Radial &rme, Radial &rother, gboolean const both)
3722     rme.a += angle;
3723     if ( both
3724          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3725          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3726     {
3727         rother.a += angle;
3728     }
3731 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3732                                         Radial &rme, Radial &rother, gboolean const both)
3734     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3736     gdouble r;
3737     if ( both
3738          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3739          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3740     {
3741         r = MAX(rme.r, rother.r);
3742     } else {
3743         r = rme.r;
3744     }
3746     gdouble const weird_angle = atan2(norm_angle, r);
3747 /* Bulia says norm_angle is just the visible distance that the
3748  * object's end must travel on the screen.  Left as 'angle' for want of
3749  * a better name.*/
3751     rme.a += weird_angle;
3752     if ( both
3753          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3754          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3755     {
3756         rother.a += weird_angle;
3757     }
3760 /**
3761  * Rotate one node.
3762  */
3763 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3765     Inkscape::NodePath::NodeSide *me, *other;
3766     bool both = false;
3768     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3769     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3771     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3772         me = &(n->p);
3773         other = &(n->n);
3774     } else if (!n->p.other) {
3775         me = &(n->n);
3776         other = &(n->p);
3777     } else {
3778         if (which > 0) { // right handle
3779             if (xn > xp) {
3780                 me = &(n->n);
3781                 other = &(n->p);
3782             } else {
3783                 me = &(n->p);
3784                 other = &(n->n);
3785             }
3786         } else if (which < 0){ // left handle
3787             if (xn <= xp) {
3788                 me = &(n->n);
3789                 other = &(n->p);
3790             } else {
3791                 me = &(n->p);
3792                 other = &(n->n);
3793             }
3794         } else { // both handles
3795             me = &(n->n);
3796             other = &(n->p);
3797             both = true;
3798         }
3799     }
3801     Radial rme(me->pos - n->pos);
3802     Radial rother(other->pos - n->pos);
3804     if (screen) {
3805         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3806     } else {
3807         node_rotate_one_internal (*n, angle, rme, rother, both);
3808     }
3810     me->pos = n->pos + NR::Point(rme);
3812     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3813         other->pos =  n->pos + NR::Point(rother);
3814     }
3816     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3817     // so here we just move all the knots without emitting move signals, for speed
3818     sp_node_update_handles(n, false);
3821 /**
3822  * Rotate selected nodes.
3823  */
3824 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3826     if (!nodepath || !nodepath->selected) return;
3828     if (g_list_length(nodepath->selected) == 1) {
3829        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3830         node_rotate_one (n, angle, which, screen);
3831     } else {
3832        // rotate as an object:
3834         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3835         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3836         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3837             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3838             box.expandTo (n->pos); // contain all selected nodes
3839         }
3841         gdouble rot;
3842         if (screen) {
3843             gdouble const zoom = nodepath->desktop->current_zoom();
3844             gdouble const zmove = angle / zoom;
3845             gdouble const r = NR::L2(box.max() - box.midpoint());
3846             rot = atan2(zmove, r);
3847         } else {
3848             rot = angle;
3849         }
3851         NR::Point rot_center;
3852         if (Inkscape::NodePath::Path::active_node == NULL)
3853             rot_center = box.midpoint();
3854         else
3855             rot_center = Inkscape::NodePath::Path::active_node->pos;
3857         NR::Matrix t =
3858             NR::Matrix (NR::translate(-rot_center)) *
3859             NR::Matrix (NR::rotate(rot)) *
3860             NR::Matrix (NR::translate(rot_center));
3862         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3863             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3864             n->pos *= t;
3865             n->n.pos *= t;
3866             n->p.pos *= t;
3867             sp_node_update_handles(n, false);
3868         }
3869     }
3871     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3874 /**
3875  * Scale one node.
3876  */
3877 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3879     bool both = false;
3880     Inkscape::NodePath::NodeSide *me, *other;
3882     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3883     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3885     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3886         me = &(n->p);
3887         other = &(n->n);
3888         n->code = NR_CURVETO;
3889     } else if (!n->p.other) {
3890         me = &(n->n);
3891         other = &(n->p);
3892         if (n->n.other)
3893             n->n.other->code = NR_CURVETO;
3894     } else {
3895         if (which > 0) { // right handle
3896             if (xn > xp) {
3897                 me = &(n->n);
3898                 other = &(n->p);
3899                 if (n->n.other)
3900                     n->n.other->code = NR_CURVETO;
3901             } else {
3902                 me = &(n->p);
3903                 other = &(n->n);
3904                 n->code = NR_CURVETO;
3905             }
3906         } else if (which < 0){ // left handle
3907             if (xn <= xp) {
3908                 me = &(n->n);
3909                 other = &(n->p);
3910                 if (n->n.other)
3911                     n->n.other->code = NR_CURVETO;
3912             } else {
3913                 me = &(n->p);
3914                 other = &(n->n);
3915                 n->code = NR_CURVETO;
3916             }
3917         } else { // both handles
3918             me = &(n->n);
3919             other = &(n->p);
3920             both = true;
3921             n->code = NR_CURVETO;
3922             if (n->n.other)
3923                 n->n.other->code = NR_CURVETO;
3924         }
3925     }
3927     Radial rme(me->pos - n->pos);
3928     Radial rother(other->pos - n->pos);
3930     rme.r += grow;
3931     if (rme.r < 0) rme.r = 0;
3932     if (rme.a == HUGE_VAL) {
3933         if (me->other) { // if direction is unknown, initialize it towards the next node
3934             Radial rme_next(me->other->pos - n->pos);
3935             rme.a = rme_next.a;
3936         } else { // if there's no next, initialize to 0
3937             rme.a = 0;
3938         }
3939     }
3940     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3941         rother.r += grow;
3942         if (rother.r < 0) rother.r = 0;
3943         if (rother.a == HUGE_VAL) {
3944             rother.a = rme.a + M_PI;
3945         }
3946     }
3948     me->pos = n->pos + NR::Point(rme);
3950     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3951         other->pos = n->pos + NR::Point(rother);
3952     }
3954     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3955     // so here we just move all the knots without emitting move signals, for speed
3956     sp_node_update_handles(n, false);
3959 /**
3960  * Scale selected nodes.
3961  */
3962 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3964     if (!nodepath || !nodepath->selected) return;
3966     if (g_list_length(nodepath->selected) == 1) {
3967         // scale handles of the single selected node
3968         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3969         node_scale_one (n, grow, which);
3970     } else {
3971         // scale nodes as an "object":
3973         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3974         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3975         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3976             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3977             box.expandTo (n->pos); // contain all selected nodes
3978         }
3980         double scale = (box.maxExtent() + grow)/box.maxExtent();
3982         NR::Point scale_center;
3983         if (Inkscape::NodePath::Path::active_node == NULL)
3984             scale_center = box.midpoint();
3985         else
3986             scale_center = Inkscape::NodePath::Path::active_node->pos;
3988         NR::Matrix t =
3989             NR::Matrix (NR::translate(-scale_center)) *
3990             NR::Matrix (NR::scale(scale, scale)) *
3991             NR::Matrix (NR::translate(scale_center));
3993         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3994             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3995             n->pos *= t;
3996             n->n.pos *= t;
3997             n->p.pos *= t;
3998             sp_node_update_handles(n, false);
3999         }
4000     }
4002     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4005 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4007     if (!nodepath) return;
4008     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4011 /**
4012  * Flip selected nodes horizontally/vertically.
4013  */
4014 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4016     if (!nodepath || !nodepath->selected) return;
4018     if (g_list_length(nodepath->selected) == 1 && !center) {
4019         // flip handles of the single selected node
4020         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4021         double temp = n->p.pos[axis];
4022         n->p.pos[axis] = n->n.pos[axis];
4023         n->n.pos[axis] = temp;
4024         sp_node_update_handles(n, false);
4025     } else {
4026         // scale nodes as an "object":
4028         NR::Rect box = sp_node_selected_bbox (nodepath);
4029         if (!center) {
4030             center = box.midpoint();
4031         }
4032         NR::Matrix t =
4033             NR::Matrix (NR::translate(- *center)) *
4034             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4035             NR::Matrix (NR::translate(*center));
4037         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4038             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4039             n->pos *= t;
4040             n->n.pos *= t;
4041             n->p.pos *= t;
4042             sp_node_update_handles(n, false);
4043         }
4044     }
4046     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4049 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4051     g_assert (nodepath->selected);
4053     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4054     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4055     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4056         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4057         box.expandTo (n->pos); // contain all selected nodes
4058     }
4059     return box;
4062 //-----------------------------------------------
4063 /**
4064  * Return new subpath under given nodepath.
4065  */
4066 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4068     g_assert(nodepath);
4069     g_assert(nodepath->desktop);
4071    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4073     s->nodepath = nodepath;
4074     s->closed = FALSE;
4075     s->nodes = NULL;
4076     s->first = NULL;
4077     s->last = NULL;
4079     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4080     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4081     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4083     return s;
4086 /**
4087  * Destroy nodes in subpath, then subpath itself.
4088  */
4089 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4091     g_assert(subpath);
4092     g_assert(subpath->nodepath);
4093     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4095     while (subpath->nodes) {
4096         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4097     }
4099     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4101     g_free(subpath);
4104 /**
4105  * Link head to tail in subpath.
4106  */
4107 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4109     g_assert(!sp->closed);
4110     g_assert(sp->last != sp->first);
4111     g_assert(sp->first->code == NR_MOVETO);
4113     sp->closed = TRUE;
4115     //Link the head to the tail
4116     sp->first->p.other = sp->last;
4117     sp->last->n.other  = sp->first;
4118     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4119     sp->first          = sp->last;
4121     //Remove the extra end node
4122     sp_nodepath_node_destroy(sp->last->n.other);
4125 /**
4126  * Open closed (loopy) subpath at node.
4127  */
4128 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4130     g_assert(sp->closed);
4131     g_assert(n->subpath == sp);
4132     g_assert(sp->first == sp->last);
4134     /* We create new startpoint, current node will become last one */
4136    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4137                                                 &n->pos, &n->pos, &n->n.pos);
4140     sp->closed        = FALSE;
4142     //Unlink to make a head and tail
4143     sp->first         = new_path;
4144     sp->last          = n;
4145     n->n.other        = NULL;
4146     new_path->p.other = NULL;
4149 /**
4150  * Return new node in subpath with given properties.
4151  * \param pos Position of node.
4152  * \param ppos Handle position in previous direction
4153  * \param npos Handle position in previous direction
4154  */
4155 Inkscape::NodePath::Node *
4156 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)
4158     g_assert(sp);
4159     g_assert(sp->nodepath);
4160     g_assert(sp->nodepath->desktop);
4162     if (nodechunk == NULL)
4163         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4165     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4167     n->subpath  = sp;
4169     if (type != Inkscape::NodePath::NODE_NONE) {
4170         // use the type from sodipodi:nodetypes
4171         n->type = type;
4172     } else {
4173         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4174             // points are (almost) collinear
4175             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4176                 // endnode, or a node with a retracted handle
4177                 n->type = Inkscape::NodePath::NODE_CUSP;
4178             } else {
4179                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4180             }
4181         } else {
4182             n->type = Inkscape::NodePath::NODE_CUSP;
4183         }
4184     }
4186     n->code     = code;
4187     n->selected = FALSE;
4188     n->pos      = *pos;
4189     n->p.pos    = *ppos;
4190     n->n.pos    = *npos;
4192     n->dragging_out = NULL;
4194     Inkscape::NodePath::Node *prev;
4195     if (next) {
4196         //g_assert(g_list_find(sp->nodes, next));
4197         prev = next->p.other;
4198     } else {
4199         prev = sp->last;
4200     }
4202     if (prev)
4203         prev->n.other = n;
4204     else
4205         sp->first = n;
4207     if (next)
4208         next->p.other = n;
4209     else
4210         sp->last = n;
4212     n->p.other = prev;
4213     n->n.other = next;
4215     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"));
4216     sp_knot_set_position(n->knot, pos, 0);
4218     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4219     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4220     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4221     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4222     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4223     sp_knot_update_ctrl(n->knot);
4225     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4226     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4227     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4228     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4229     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4230     sp_knot_show(n->knot);
4232     // We only create handle knots and lines on demand
4233     n->p.knot = NULL;
4234     n->p.line = NULL;
4235     n->n.knot = NULL;
4236     n->n.line = NULL;
4238     sp->nodes = g_list_prepend(sp->nodes, n);
4240     return n;
4243 /**
4244  * Destroy node and its knots, link neighbors in subpath.
4245  */
4246 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4248     g_assert(node);
4249     g_assert(node->subpath);
4250     g_assert(SP_IS_KNOT(node->knot));
4252    Inkscape::NodePath::SubPath *sp = node->subpath;
4254     if (node->selected) { // first, deselect
4255         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4256         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4257     }
4259     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4261     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4262     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4263     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4264     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4265     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4266     g_object_unref(G_OBJECT(node->knot));
4268     if (node->p.knot) {
4269         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4270         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4271         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4272         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4273         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4274         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4275         g_object_unref(G_OBJECT(node->p.knot));
4276         node->p.knot = NULL;
4277     }
4279     if (node->n.knot) {
4280         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4281         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4282         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4283         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4284         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4285         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4286         g_object_unref(G_OBJECT(node->n.knot));
4287         node->n.knot = NULL;
4288     }
4290     if (node->p.line)
4291         gtk_object_destroy(GTK_OBJECT(node->p.line));
4292     if (node->n.line)
4293         gtk_object_destroy(GTK_OBJECT(node->n.line));
4295     if (sp->nodes) { // there are others nodes on the subpath
4296         if (sp->closed) {
4297             if (sp->first == node) {
4298                 g_assert(sp->last == node);
4299                 sp->first = node->n.other;
4300                 sp->last = sp->first;
4301             }
4302             node->p.other->n.other = node->n.other;
4303             node->n.other->p.other = node->p.other;
4304         } else {
4305             if (sp->first == node) {
4306                 sp->first = node->n.other;
4307                 sp->first->code = NR_MOVETO;
4308             }
4309             if (sp->last == node) sp->last = node->p.other;
4310             if (node->p.other) node->p.other->n.other = node->n.other;
4311             if (node->n.other) node->n.other->p.other = node->p.other;
4312         }
4313     } else { // this was the last node on subpath
4314         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4315     }
4317     g_mem_chunk_free(nodechunk, node);
4320 /**
4321  * Returns one of the node's two sides.
4322  * \param which Indicates which side.
4323  * \return Pointer to previous node side if which==-1, next if which==1.
4324  */
4325 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4327     g_assert(node);
4329     switch (which) {
4330         case -1:
4331             return &node->p;
4332         case 1:
4333             return &node->n;
4334         default:
4335             break;
4336     }
4338     g_assert_not_reached();
4340     return NULL;
4343 /**
4344  * Return the other side of the node, given one of its sides.
4345  */
4346 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4348     g_assert(node);
4350     if (me == &node->p) return &node->n;
4351     if (me == &node->n) return &node->p;
4353     g_assert_not_reached();
4355     return NULL;
4358 /**
4359  * Return NRPathcode on the given side of the node.
4360  */
4361 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4363     g_assert(node);
4365     if (me == &node->p) {
4366         if (node->p.other) return (NRPathcode)node->code;
4367         return NR_MOVETO;
4368     }
4370     if (me == &node->n) {
4371         if (node->n.other) return (NRPathcode)node->n.other->code;
4372         return NR_MOVETO;
4373     }
4375     g_assert_not_reached();
4377     return NR_END;
4380 /**
4381  * Return node with the given index
4382  */
4383 Inkscape::NodePath::Node *
4384 sp_nodepath_get_node_by_index(int index)
4386     Inkscape::NodePath::Node *e = NULL;
4388     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4389     if (!nodepath) {
4390         return e;
4391     }
4393     //find segment
4394     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4396         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4397         int n = g_list_length(sp->nodes);
4398         if (sp->closed) {
4399             n++;
4400         }
4402         //if the piece belongs to this subpath grab it
4403         //otherwise move onto the next subpath
4404         if (index < n) {
4405             e = sp->first;
4406             for (int i = 0; i < index; ++i) {
4407                 e = e->n.other;
4408             }
4409             break;
4410         } else {
4411             if (sp->closed) {
4412                 index -= (n+1);
4413             } else {
4414                 index -= n;
4415             }
4416         }
4417     }
4419     return e;
4422 /**
4423  * Returns plain text meaning of node type.
4424  */
4425 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4427     unsigned retracted = 0;
4428     bool endnode = false;
4430     for (int which = -1; which <= 1; which += 2) {
4431         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4432         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4433             retracted ++;
4434         if (!side->other)
4435             endnode = true;
4436     }
4438     if (retracted == 0) {
4439         if (endnode) {
4440                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4441                 return _("end node");
4442         } else {
4443             switch (node->type) {
4444                 case Inkscape::NodePath::NODE_CUSP:
4445                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4446                     return _("cusp");
4447                 case Inkscape::NodePath::NODE_SMOOTH:
4448                     // TRANSLATORS: "smooth" is an adjective here
4449                     return _("smooth");
4450                 case Inkscape::NodePath::NODE_SYMM:
4451                     return _("symmetric");
4452             }
4453         }
4454     } else if (retracted == 1) {
4455         if (endnode) {
4456             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4457             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4458         } else {
4459             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4460         }
4461     } else {
4462         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4463     }
4465     return NULL;
4468 /**
4469  * Handles content of statusbar as long as node tool is active.
4470  */
4471 void
4472 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4474     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");
4475     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4477     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4478     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4479     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4480     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4482     SPDesktop *desktop = NULL;
4483     if (nodepath) {
4484         desktop = nodepath->desktop;
4485     } else {
4486         desktop = SP_ACTIVE_DESKTOP;
4487     }
4489     SPEventContext *ec = desktop->event_context;
4490     if (!ec) return;
4491     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4492     if (!mc) return;
4494     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4496     if (selected_nodes == 0) {
4497         Inkscape::Selection *sel = desktop->selection;
4498         if (!sel || sel->isEmpty()) {
4499             mc->setF(Inkscape::NORMAL_MESSAGE,
4500                      _("Select a single object to edit its nodes or handles."));
4501         } else {
4502             if (nodepath) {
4503             mc->setF(Inkscape::NORMAL_MESSAGE,
4504                      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.",
4505                               "<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.",
4506                               total_nodes),
4507                      total_nodes);
4508             } else {
4509                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4510                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4511                 } else {
4512                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4513                 }
4514             }
4515         }
4516     } else if (nodepath && selected_nodes == 1) {
4517         mc->setF(Inkscape::NORMAL_MESSAGE,
4518                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4519                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4520                           total_nodes),
4521                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4522     } else {
4523         if (selected_subpaths > 1) {
4524             mc->setF(Inkscape::NORMAL_MESSAGE,
4525                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4526                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4527                               total_nodes),
4528                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4529         } else {
4530             mc->setF(Inkscape::NORMAL_MESSAGE,
4531                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4532                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4533                               total_nodes),
4534                      selected_nodes, total_nodes, when_selected);
4535         }
4536     }
4539 /*
4540  * returns a *copy* of the curve of that object.
4541  */
4542 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4543     if (!object)
4544         return NULL;
4546     SPCurve *curve = NULL;
4547     if (SP_IS_PATH(object)) {
4548         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4549         curve = sp_curve_copy(curve_new);
4550     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4551         const gchar *svgd = object->repr->attribute(key);
4552         if (svgd) {
4553             NArtBpath *bpath = sp_svg_read_path(svgd);
4554             SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
4555             if (curve_new) {
4556                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4557             } else {
4558                 g_free(bpath);
4559             }
4560         }
4561     }
4563     return curve;
4566 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4567     if (!np || !np->object || !curve)
4568         return;
4570     if (SP_IS_PATH(np->object)) {
4571         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4572             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4573         } else {
4574             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4575         }
4576     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4577         // FIXME: this writing to string and then reading from string is bound to be slow.
4578         // create a method to convert from curve directly to 2geom...
4579         gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
4580         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4581         g_free(svgpath);
4583         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4584     }
4587 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4588     np->show_helperpath = show;
4590     if (show) {
4591         SPCurve *helper_curve = sp_curve_copy(np->curve);
4592         sp_curve_transform(helper_curve, np->i2d );
4593         if (!np->helper_path) {
4594             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4595             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);
4596             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4597             sp_canvas_item_move_to_z(np->helper_path, 0);
4598             sp_canvas_item_show(np->helper_path);
4599         } else {
4600             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4601         }
4602         sp_curve_unref(helper_curve);
4603     } else {
4604         if (np->helper_path) {
4605             GtkObject *temp = np->helper_path;
4606             np->helper_path = NULL;
4607             gtk_object_destroy(temp);
4608         }
4609     }
4612 /* this function does not work yet */
4613 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4614     np->straight_path = true;
4615     np->show_handles = false;
4616     g_message("add code to make the path straight.");
4617     // do sp_nodepath_convert_node_type on all nodes?
4618     // search for this text !!!   "Make selected segments lines"
4622 /*
4623   Local Variables:
4624   mode:c++
4625   c-file-style:"stroustrup"
4626   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4627   indent-tabs-mode:nil
4628   fill-column:99
4629   End:
4630 */
4631 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :