Code

f48a83b4f254e9e7e73b046c5338959494b66b53
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include <glibmm/i18n.h>
23 #include "libnr/n-art-bpath.h"
24 #include "libnr/nr-path.h"
25 #include "helper/units.h"
26 #include "knot.h"
27 #include "inkscape.h"
28 #include "document.h"
29 #include "sp-namedview.h"
30 #include "desktop.h"
31 #include "desktop-handles.h"
32 #include "snap.h"
33 #include "message-stack.h"
34 #include "message-context.h"
35 #include "node-context.h"
36 #include "shape-editor.h"
37 #include "selection-chemistry.h"
38 #include "selection.h"
39 #include "xml/repr.h"
40 #include "prefs-utils.h"
41 #include "sp-metrics.h"
42 #include "sp-path.h"
43 #include "libnr/nr-matrix-ops.h"
44 #include "splivarot.h"
45 #include "svg/svg.h"
46 #include "verbs.h"
47 #include "display/bezier-utils.h"
48 #include <vector>
49 #include <algorithm>
50 #include "live_effects/lpeobject.h"
51 #include "live_effects/parameter/parameter.h"
53 class NR::Matrix;
55 /// \todo
56 /// evil evil evil. FIXME: conflict of two different Path classes!
57 /// There is a conflict in the namespace between two classes named Path.
58 /// #include "sp-flowtext.h"
59 /// #include "sp-flowregion.h"
61 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
62 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
63 GType sp_flowregion_get_type (void);
64 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
65 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
66 GType sp_flowtext_get_type (void);
67 // end evil workaround
69 #include "helper/stlport.h"
72 /// \todo fixme: Implement these via preferences */
74 #define NODE_FILL          0xbfbfbf00
75 #define NODE_STROKE        0x000000ff
76 #define NODE_FILL_HI       0xff000000
77 #define NODE_STROKE_HI     0x000000ff
78 #define NODE_FILL_SEL      0x0000ffff
79 #define NODE_STROKE_SEL    0x000000ff
80 #define NODE_FILL_SEL_HI   0xff000000
81 #define NODE_STROKE_SEL_HI 0x000000ff
82 #define KNOT_FILL          0xffffffff
83 #define KNOT_STROKE        0x000000ff
84 #define KNOT_FILL_HI       0xff000000
85 #define KNOT_STROKE_HI     0x000000ff
87 static GMemChunk *nodechunk = NULL;
89 /* Creation from object */
91 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
92 static gchar *parse_nodetypes(gchar const *types, gint length);
94 /* Object updating */
96 static void stamp_repr(Inkscape::NodePath::Path *np);
97 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
98 static gchar *create_typestr(Inkscape::NodePath::Path *np);
100 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
102 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
104 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
106 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
108 /* Adjust handle placement, if the node or the other handle is moved */
109 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
110 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
112 /* Node event callbacks */
113 static void node_clicked(SPKnot *knot, guint state, gpointer data);
114 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
115 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
116 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
118 /* Handle event callbacks */
119 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
120 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
121 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
122 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
123 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
124 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
126 /* Constructors and destructors */
128 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
129 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
130 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
131 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
132 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
133                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
134 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
136 /* Helpers */
138 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
139 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
140 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
142 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
143 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
145 // active_node indicates mouseover node
146 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
148 /**
149  * \brief Creates new nodepath from item
150  */
151 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
153     Inkscape::XML::Node *repr = object->repr;
155     /** \todo
156      * FIXME: remove this. We don't want to edit paths inside flowtext.
157      * Instead we will build our flowtext with cloned paths, so that the
158      * real paths are outside the flowtext and thus editable as usual.
159      */
160     if (SP_IS_FLOWTEXT(object)) {
161         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
162             if SP_IS_FLOWREGION(child) {
163                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
164                 if (grandchild && SP_IS_PATH(grandchild)) {
165                     object = SP_ITEM(grandchild);
166                     break;
167                 }
168             }
169         }
170     }
172     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
174     if (curve == NULL)
175         return NULL;
177     NArtBpath *bpath = sp_curve_first_bpath(curve);
178     gint length = curve->end;
179     if (length == 0) {
180         sp_curve_unref(curve);
181         return NULL; // prevent crash for one-node paths
182     }
184     //Create new nodepath
185     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
186     if (!np) {
187         sp_curve_unref(curve);
188         return NULL;
189     }
191     // Set defaults
192     np->desktop     = desktop;
193     np->object      = object;
194     np->subpaths    = NULL;
195     np->selected    = NULL;
196     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
197     np->livarot_path = NULL;
198     np->local_change = 0;
199     np->show_handles = show_handles;
200     np->helper_path = NULL;
201     np->curve = sp_curve_copy(curve);
202     np->show_helperpath = false;
203     np->straight_path = false;
204     if (IS_LIVEPATHEFFECT(object) && item) {
205         np->item = item;
206     } else {
207         np->item = SP_ITEM(object);
208     }
210     // we need to update item's transform from the repr here,
211     // because they may be out of sync when we respond
212     // to a change in repr by regenerating nodepath     --bb
213     sp_object_read_attr(SP_OBJECT(np->item), "transform");
215     np->i2d  = sp_item_i2d_affine(np->item);
216     np->d2i  = np->i2d.inverse();
218     np->repr = repr;
219     if (repr_key_in) { // apparantly the object is an LPEObject
220         np->repr_key = g_strdup(repr_key_in);
221         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
222         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
223         if (lpeparam) {
224             lpeparam->param_setup_notepath(np);
225         }
226     } else {
227         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
228         if ( SP_SHAPE(np->object)->path_effect_href ) {
229             np->repr_key = g_strdup("inkscape:original-d");
231             LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(SP_SHAPE(np->object));
232             if (lpeobj && lpeobj->lpe) {
233                 lpeobj->lpe->setup_notepath(np);
234             }
235         } else {
236             np->repr_key = g_strdup("d");
237         }
238     }
240     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
241     gchar *typestr = parse_nodetypes(nodetypes, length);
243     // create the subpath(s) from the bpath
244     NArtBpath *b = bpath;
245     while (b->code != NR_END) {
246         b = subpath_from_bpath(np, b, typestr + (b - bpath));
247     }
249     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
250     np->subpaths = g_list_reverse(np->subpaths);
252     g_free(typestr);
253     sp_curve_unref(curve);
255     // create the livarot representation from the same item
256     sp_nodepath_ensure_livarot_path(np);
258     // Draw helper curve
259     if (np->show_helperpath) {
260         SPCurve *helper_curve = sp_curve_copy(np->curve);
261         sp_curve_transform(helper_curve, np->i2d );
262         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
263         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);
264         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
265         sp_canvas_item_show(np->helper_path);
266         sp_curve_unref(helper_curve);
267     }
269     return np;
272 /**
273  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
274  */
275 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
277     if (!np)  //soft fail, like delete
278         return;
280     while (np->subpaths) {
281         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
282     }
284     //Inform the ShapeEditor that made me, if any, that I am gone.
285     if (np->shape_editor)
286         np->shape_editor->nodepath_destroyed();
288     g_assert(!np->selected);
290     if (np->livarot_path) {
291         delete np->livarot_path;
292         np->livarot_path = NULL;
293     }
295     if (np->helper_path) {
296         GtkObject *temp = np->helper_path;
297         np->helper_path = NULL;
298         gtk_object_destroy(temp);
299     }
300     if (np->curve) {
301         sp_curve_unref(np->curve);
302         np->curve = NULL;
303     }
305     if (np->repr_key) {
306         g_free(np->repr_key);
307         np->repr_key = NULL;
308     }
309     if (np->repr_nodetypes_key) {
310         g_free(np->repr_nodetypes_key);
311         np->repr_nodetypes_key = NULL;
312     }
314     np->desktop = NULL;
316     g_free(np);
320 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
322     if (np && np->livarot_path == NULL) {
323         SPCurve *curve = create_curve(np);
324         NArtBpath *bpath = SP_CURVE_BPATH(curve);
325         np->livarot_path = bpath_to_Path(bpath);
327         if (np->livarot_path)
328             np->livarot_path->ConvertWithBackData(0.01);
330         sp_curve_unref(curve);
331     }
335 /**
336  *  Return the node count of a given NodeSubPath.
337  */
338 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
340     if (!subpath)
341         return 0;
342     gint nodeCount = g_list_length(subpath->nodes);
343     return nodeCount;
346 /**
347  *  Return the node count of a given NodePath.
348  */
349 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
351     if (!np)
352         return 0;
353     gint nodeCount = 0;
354     for (GList *item = np->subpaths ; item ; item=item->next) {
355        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
356         nodeCount += g_list_length(subpath->nodes);
357     }
358     return nodeCount;
361 /**
362  *  Return the subpath count of a given NodePath.
363  */
364 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
366     if (!np)
367         return 0;
368     return g_list_length (np->subpaths);
371 /**
372  *  Return the selected node count of a given NodePath.
373  */
374 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
376     if (!np)
377         return 0;
378     return g_list_length (np->selected);
381 /**
382  *  Return the number of subpaths where nodes are selected in a given NodePath.
383  */
384 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
386     if (!np)
387         return 0;
388     if (!np->selected)
389         return 0;
390     if (!np->selected->next)
391         return 1;
392     gint count = 0;
393     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
394         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
395         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
396             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
397             if (node->selected) {
398                 count ++;
399                 break;
400             }
401         }
402     }
403     return count;
406 /**
407  * Clean up a nodepath after editing.
408  *
409  * Currently we are deleting trivial subpaths.
410  */
411 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
413     GList *badSubPaths = NULL;
415     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
416     for (GList *l = nodepath->subpaths; l ; l=l->next) {
417        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
418        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
419             badSubPaths = g_list_append(badSubPaths, sp);
420     }
422     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
423     //also removes the subpath from nodepath->subpaths
424     for (GList *l = badSubPaths; l ; l=l->next) {
425        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
426         sp_nodepath_subpath_destroy(sp);
427     }
429     g_list_free(badSubPaths);
432 /**
433  * Create new nodepath from b, make it subpath of np.
434  * \param t The node type.
435  * \todo Fixme: t should be a proper type, rather than gchar
436  */
437 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
439     NR::Point ppos, pos, npos;
441     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
443     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
444     bool const closed = (b->code == NR_MOVETO);
446     pos = NR::Point(b->x3, b->y3) * np->i2d;
447     if (b[1].code == NR_CURVETO) {
448         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
449     } else {
450         npos = pos;
451     }
452     Inkscape::NodePath::Node *n;
453     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
454     g_assert(sp->first == n);
455     g_assert(sp->last  == n);
457     b++;
458     t++;
459     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
460         pos = NR::Point(b->x3, b->y3) * np->i2d;
461         if (b->code == NR_CURVETO) {
462             ppos = NR::Point(b->x2, b->y2) * np->i2d;
463         } else {
464             ppos = pos;
465         }
466         if (b[1].code == NR_CURVETO) {
467             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
468         } else {
469             npos = pos;
470         }
471         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
472         b++;
473         t++;
474     }
476     if (closed) sp_nodepath_subpath_close(sp);
478     return b;
481 /**
482  * Convert from sodipodi:nodetypes to new style type string.
483  */
484 static gchar *parse_nodetypes(gchar const *types, gint length)
486     g_assert(length > 0);
488     gchar *typestr = g_new(gchar, length + 1);
490     gint pos = 0;
492     if (types) {
493         for (gint i = 0; types[i] && ( i < length ); i++) {
494             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
495             if (types[i] != '\0') {
496                 switch (types[i]) {
497                     case 's':
498                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
499                         break;
500                     case 'z':
501                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
502                         break;
503                     case 'c':
504                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
505                         break;
506                     default:
507                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
508                         break;
509                 }
510             }
511         }
512     }
514     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
516     return typestr;
519 /**
520  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
521  * updated but repr is not (for speed). Used during curve and node drag.
522  */
523 static void update_object(Inkscape::NodePath::Path *np)
525     g_assert(np);
527     sp_curve_unref(np->curve);
528     np->curve = create_curve(np);
530     sp_nodepath_set_curve(np, np->curve);
532     if (np->show_helperpath) {
533         SPCurve * helper_curve = sp_curve_copy(np->curve);
534         sp_curve_transform(helper_curve, np->i2d );
535         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
536         sp_curve_unref(helper_curve);
537     }
540 /**
541  * Update XML path node with data from path object.
542  */
543 static void update_repr_internal(Inkscape::NodePath::Path *np)
545     g_assert(np);
547     Inkscape::XML::Node *repr = np->object->repr;
549     sp_curve_unref(np->curve);
550     np->curve = create_curve(np);
552     gchar *typestr = create_typestr(np);
553     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
555     // determine if path has an effect applied and write to correct "d" attribute.
556     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
557         np->local_change++;
558         repr->setAttribute(np->repr_key, svgpath);
559     }
561     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
562         np->local_change++;
563         repr->setAttribute(np->repr_nodetypes_key, typestr);
564     }
566     g_free(svgpath);
567     g_free(typestr);
569     if (np->show_helperpath) {
570         SPCurve * helper_curve = sp_curve_copy(np->curve);
571         sp_curve_transform(helper_curve, np->i2d );
572         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
573         sp_curve_unref(helper_curve);
574     }
575  }
577 /**
578  * Update XML path node with data from path object, commit changes forever.
579  */
580 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
582     //fixme: np can be NULL, so check before proceeding
583     g_return_if_fail(np != NULL);
585     if (np->livarot_path) {
586         delete np->livarot_path;
587         np->livarot_path = NULL;
588     }
590     update_repr_internal(np);
591     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
593     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
594                      annotation);
597 /**
598  * Update XML path node with data from path object, commit changes with undo.
599  */
600 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
602     if (np->livarot_path) {
603         delete np->livarot_path;
604         np->livarot_path = NULL;
605     }
607     update_repr_internal(np);
608     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
609                            annotation);
612 /**
613  * Make duplicate of path, replace corresponding XML node in tree, commit.
614  */
615 static void stamp_repr(Inkscape::NodePath::Path *np)
617     g_assert(np);
619     Inkscape::XML::Node *old_repr = np->object->repr;
620     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
622     // remember the position of the item
623     gint pos = old_repr->position();
624     // remember parent
625     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
627     SPCurve *curve = create_curve(np);
628     gchar *typestr = create_typestr(np);
630     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
632     new_repr->setAttribute(np->repr_key, svgpath);
633     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
635     // add the new repr to the parent
636     parent->appendChild(new_repr);
637     // move to the saved position
638     new_repr->setPosition(pos > 0 ? pos : 0);
640     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
641                      _("Stamp"));
643     Inkscape::GC::release(new_repr);
644     g_free(svgpath);
645     g_free(typestr);
646     sp_curve_unref(curve);
649 /**
650  * Create curve from path.
651  */
652 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
654     SPCurve *curve = sp_curve_new();
656     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
657        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
658         sp_curve_moveto(curve,
659                         sp->first->pos * np->d2i);
660        Inkscape::NodePath::Node *n = sp->first->n.other;
661         while (n) {
662             NR::Point const end_pt = n->pos * np->d2i;
663             switch (n->code) {
664                 case NR_LINETO:
665                     sp_curve_lineto(curve, end_pt);
666                     break;
667                 case NR_CURVETO:
668                     sp_curve_curveto(curve,
669                                      n->p.other->n.pos * np->d2i,
670                                      n->p.pos * np->d2i,
671                                      end_pt);
672                     break;
673                 default:
674                     g_assert_not_reached();
675                     break;
676             }
677             if (n != sp->last) {
678                 n = n->n.other;
679             } else {
680                 n = NULL;
681             }
682         }
683         if (sp->closed) {
684             sp_curve_closepath(curve);
685         }
686     }
688     return curve;
691 /**
692  * Convert path type string to sodipodi:nodetypes style.
693  */
694 static gchar *create_typestr(Inkscape::NodePath::Path *np)
696     gchar *typestr = g_new(gchar, 32);
697     gint len = 32;
698     gint pos = 0;
700     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
701        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
703         if (pos >= len) {
704             typestr = g_renew(gchar, typestr, len + 32);
705             len += 32;
706         }
708         typestr[pos++] = 'c';
710        Inkscape::NodePath::Node *n;
711         n = sp->first->n.other;
712         while (n) {
713             gchar code;
715             switch (n->type) {
716                 case Inkscape::NodePath::NODE_CUSP:
717                     code = 'c';
718                     break;
719                 case Inkscape::NodePath::NODE_SMOOTH:
720                     code = 's';
721                     break;
722                 case Inkscape::NodePath::NODE_SYMM:
723                     code = 'z';
724                     break;
725                 default:
726                     g_assert_not_reached();
727                     code = '\0';
728                     break;
729             }
731             if (pos >= len) {
732                 typestr = g_renew(gchar, typestr, len + 32);
733                 len += 32;
734             }
736             typestr[pos++] = code;
738             if (n != sp->last) {
739                 n = n->n.other;
740             } else {
741                 n = NULL;
742             }
743         }
744     }
746     if (pos >= len) {
747         typestr = g_renew(gchar, typestr, len + 1);
748         len += 1;
749     }
751     typestr[pos++] = '\0';
753     return typestr;
756 /**
757  * Returns current path in context. // later eliminate this function at all!
758  */
759 static Inkscape::NodePath::Path *sp_nodepath_current()
761     if (!SP_ACTIVE_DESKTOP) {
762         return NULL;
763     }
765     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
767     if (!SP_IS_NODE_CONTEXT(event_context)) {
768         return NULL;
769     }
771     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
776 /**
777  \brief Fills node and handle positions for three nodes, splitting line
778   marked by end at distance t.
779  */
780 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
782     g_assert(new_path != NULL);
783     g_assert(end      != NULL);
785     g_assert(end->p.other == new_path);
786    Inkscape::NodePath::Node *start = new_path->p.other;
787     g_assert(start);
789     if (end->code == NR_LINETO) {
790         new_path->type =Inkscape::NodePath::NODE_CUSP;
791         new_path->code = NR_LINETO;
792         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
793     } else {
794         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
795         new_path->code = NR_CURVETO;
796         gdouble s      = 1 - t;
797         for (int dim = 0; dim < 2; dim++) {
798             NR::Coord const f000 = start->pos[dim];
799             NR::Coord const f001 = start->n.pos[dim];
800             NR::Coord const f011 = end->p.pos[dim];
801             NR::Coord const f111 = end->pos[dim];
802             NR::Coord const f00t = s * f000 + t * f001;
803             NR::Coord const f01t = s * f001 + t * f011;
804             NR::Coord const f11t = s * f011 + t * f111;
805             NR::Coord const f0tt = s * f00t + t * f01t;
806             NR::Coord const f1tt = s * f01t + t * f11t;
807             NR::Coord const fttt = s * f0tt + t * f1tt;
808             start->n.pos[dim]    = f00t;
809             new_path->p.pos[dim] = f0tt;
810             new_path->pos[dim]   = fttt;
811             new_path->n.pos[dim] = f1tt;
812             end->p.pos[dim]      = f11t;
813         }
814     }
817 /**
818  * Adds new node on direct line between two nodes, activates handles of all
819  * three nodes.
820  */
821 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
823     g_assert(end);
824     g_assert(end->subpath);
825     g_assert(g_list_find(end->subpath->nodes, end));
827    Inkscape::NodePath::Node *start = end->p.other;
828     g_assert( start->n.other == end );
829    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
830                                                end,
831                                                (NRPathcode)end->code == NR_LINETO?
832                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
833                                                (NRPathcode)end->code,
834                                                &start->pos, &start->pos, &start->n.pos);
835     sp_nodepath_line_midpoint(newnode, end, t);
837     sp_node_adjust_handles(start);
838     sp_node_update_handles(start);
839     sp_node_update_handles(newnode);
840     sp_node_adjust_handles(end);
841     sp_node_update_handles(end);
843     return newnode;
846 /**
847 \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
848 */
849 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
851     g_assert(node);
852     g_assert(node->subpath);
853     g_assert(g_list_find(node->subpath->nodes, node));
855    Inkscape::NodePath::SubPath *sp = node->subpath;
856     Inkscape::NodePath::Path *np    = sp->nodepath;
858     if (sp->closed) {
859         sp_nodepath_subpath_open(sp, node);
860         return sp->first;
861     } else {
862         // no break for end nodes
863         if (node == sp->first) return NULL;
864         if (node == sp->last ) return NULL;
866         // create a new subpath
867        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
869         // duplicate the break node as start of the new subpath
870        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
872         while (node->n.other) { // copy the remaining nodes into the new subpath
873            Inkscape::NodePath::Node *n  = node->n.other;
874            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);
875             if (n->selected) {
876                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
877             }
878             sp_nodepath_node_destroy(n); // remove the point on the original subpath
879         }
881         return newnode;
882     }
885 /**
886  * Duplicate node and connect to neighbours.
887  */
888 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
890     g_assert(node);
891     g_assert(node->subpath);
892     g_assert(g_list_find(node->subpath->nodes, node));
894    Inkscape::NodePath::SubPath *sp = node->subpath;
896     NRPathcode code = (NRPathcode) node->code;
897     if (code == NR_MOVETO) { // if node is the endnode,
898         node->code = NR_LINETO; // new one is inserted before it, so change that to line
899     }
901     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
903     if (!node->n.other || !node->p.other) // if node is an endnode, select it
904         return node;
905     else
906         return newnode; // otherwise select the newly created node
909 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
911     node->p.pos = (node->pos + (node->pos - node->n.pos));
914 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
916     node->n.pos = (node->pos + (node->pos - node->p.pos));
919 /**
920  * Change line type at node, with side effects on neighbours.
921  */
922 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
924     g_assert(end);
925     g_assert(end->subpath);
926     g_assert(end->p.other);
928     if (end->code == static_cast< guint > ( code ) )
929         return;
931    Inkscape::NodePath::Node *start = end->p.other;
933     end->code = code;
935     if (code == NR_LINETO) {
936         if (start->code == NR_LINETO) {
937             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
938         }
939         if (end->n.other) {
940             if (end->n.other->code == NR_LINETO) {
941                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
942             }
943         }
944     } else {
945         NR::Point delta = end->pos - start->pos;
946         start->n.pos = start->pos + delta / 3;
947         end->p.pos = end->pos - delta / 3;
948         sp_node_adjust_handle(start, 1);
949         sp_node_adjust_handle(end, -1);
950     }
952     sp_node_update_handles(start);
953     sp_node_update_handles(end);
956 /**
957  * Change node type, and its handles accordingly.
958  */
959 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
961     g_assert(node);
962     g_assert(node->subpath);
964     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
965         return node;
967     if ((node->p.other != NULL) && (node->n.other != NULL)) {
968         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
969             type =Inkscape::NodePath::NODE_CUSP;
970         }
971     }
973     node->type = type;
975     if (node->type == Inkscape::NodePath::NODE_CUSP) {
976         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
977         node->knot->setSize (node->selected? 11 : 9);
978         sp_knot_update_ctrl(node->knot);
979     } else {
980         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
981         node->knot->setSize (node->selected? 9 : 7);
982         sp_knot_update_ctrl(node->knot);
983     }
985     // if one of handles is mouseovered, preserve its position
986     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
987         sp_node_adjust_handle(node, 1);
988     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
989         sp_node_adjust_handle(node, -1);
990     } else {
991         sp_node_adjust_handles(node);
992     }
994     sp_node_update_handles(node);
996     sp_nodepath_update_statusbar(node->subpath->nodepath);
998     return node;
1001 /**
1002  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
1003  * adjacent segments from lines to curves.
1004 */
1005 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1007     bool p_line = (node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos);
1008     bool n_line = (node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos);
1010     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1011         if (p_line && n_line) {
1012             // only if both adjacent segments are lines,
1013             // convert both to curves:
1015             node->code = NR_CURVETO;
1016             node->n.other->code = NR_CURVETO;
1018             NR::Point leg_prev = node->pos - node->p.other->pos;
1019             NR::Point leg_next = node->pos - node->n.other->pos;
1021             double norm_leg_prev = L2(leg_prev);
1022             double norm_leg_next = L2(leg_next);
1024             // delta has length 1 and is orthogonal to bisecting line
1025             NR::Point delta;
1026             if (norm_leg_next > 0.0) {
1027                 delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1028                 (&delta)->normalize();
1029             }
1031             if (type == Inkscape::NodePath::NODE_SYMM) {
1032                 double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1033                 node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1034                 node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1035             } else {
1036                 // length of handle is proportional to distance to adjacent node
1037                 node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1038                 node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1039             }
1041             sp_node_update_handles(node);
1042         }
1043     }
1045     sp_nodepath_set_node_type (node, type);
1048 /**
1049  * Move node to point, and adjust its and neighbouring handles.
1050  */
1051 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1053     NR::Point delta = p - node->pos;
1054     node->pos = p;
1056     node->p.pos += delta;
1057     node->n.pos += delta;
1059     Inkscape::NodePath::Node *node_p = NULL;
1060     Inkscape::NodePath::Node *node_n = NULL;
1062     if (node->p.other) {
1063         if (node->code == NR_LINETO) {
1064             sp_node_adjust_handle(node, 1);
1065             sp_node_adjust_handle(node->p.other, -1);
1066             node_p = node->p.other;
1067         }
1068     }
1069     if (node->n.other) {
1070         if (node->n.other->code == NR_LINETO) {
1071             sp_node_adjust_handle(node, -1);
1072             sp_node_adjust_handle(node->n.other, 1);
1073             node_n = node->n.other;
1074         }
1075     }
1077     // this function is only called from batch movers that will update display at the end
1078     // themselves, so here we just move all the knots without emitting move signals, for speed
1079     sp_node_update_handles(node, false);
1080     if (node_n) {
1081         sp_node_update_handles(node_n, false);
1082     }
1083     if (node_p) {
1084         sp_node_update_handles(node_p, false);
1085     }
1088 /**
1089  * Call sp_node_moveto() for node selection and handle possible snapping.
1090  */
1091 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1092                                             bool const snap = true)
1094     NR::Coord best = NR_HUGE;
1095     NR::Point delta(dx, dy);
1096     NR::Point best_pt = delta;
1098     if (snap) {
1099         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
1101         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1102             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1103             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, SP_PATH(n->subpath->nodepath->item));
1104             if (s.getDistance() < best) {
1105                 best = s.getDistance();
1106                 best_pt = s.getPoint() - n->pos;
1107             }
1108         }
1109     }
1111     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1112         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1113         sp_node_moveto(n, n->pos + best_pt);
1114     }
1116     // do not update repr here so that node dragging is acceptably fast
1117     update_object(nodepath);
1120 /**
1121 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1122 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1123 near x = 0.
1124  */
1125 double
1126 sculpt_profile (double x, double alpha, guint profile)
1128     if (x >= 1)
1129         return 0;
1130     if (x <= 0)
1131         return 1;
1133     switch (profile) {
1134         case SCULPT_PROFILE_LINEAR:
1135         return 1 - x;
1136         case SCULPT_PROFILE_BELL:
1137         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1138         case SCULPT_PROFILE_ELLIPTIC:
1139         return sqrt(1 - x*x);
1140     }
1142     return 1;
1145 double
1146 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1148     // extremely primitive for now, don't have time to look for the real one
1149     double lower = NR::L2(b - a);
1150     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1151     return (lower + upper)/2;
1154 void
1155 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1157     n->pos = n->origin + delta;
1158     n->n.pos = n->n.origin + delta_n;
1159     n->p.pos = n->p.origin + delta_p;
1160     sp_node_adjust_handles(n);
1161     sp_node_update_handles(n, false);
1164 /**
1165  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1166  * on how far they are from the dragged node n.
1167  */
1168 static void
1169 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1171     g_assert (n);
1172     g_assert (nodepath);
1173     g_assert (n->subpath->nodepath == nodepath);
1175     double pressure = n->knot->pressure;
1176     if (pressure == 0)
1177         pressure = 0.5; // default
1178     pressure = CLAMP (pressure, 0.2, 0.8);
1180     // map pressure to alpha = 1/5 ... 5
1181     double alpha = 1 - 2 * fabs(pressure - 0.5);
1182     if (pressure > 0.5)
1183         alpha = 1/alpha;
1185     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1187     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1188         // Only one subpath has selected nodes:
1189         // use linear mode, where the distance from n to node being dragged is calculated along the path
1191         double n_sel_range = 0, p_sel_range = 0;
1192         guint n_nodes = 0, p_nodes = 0;
1193         guint n_sel_nodes = 0, p_sel_nodes = 0;
1195         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1196         {
1197             double n_range = 0, p_range = 0;
1198             bool n_going = true, p_going = true;
1199             Inkscape::NodePath::Node *n_node = n;
1200             Inkscape::NodePath::Node *p_node = n;
1201             do {
1202                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1203                 if (n_node && n_going)
1204                     n_node = n_node->n.other;
1205                 if (n_node == NULL) {
1206                     n_going = false;
1207                 } else {
1208                     n_nodes ++;
1209                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1210                     if (n_node->selected) {
1211                         n_sel_nodes ++;
1212                         n_sel_range = n_range;
1213                     }
1214                     if (n_node == p_node) {
1215                         n_going = false;
1216                         p_going = false;
1217                     }
1218                 }
1219                 if (p_node && p_going)
1220                     p_node = p_node->p.other;
1221                 if (p_node == NULL) {
1222                     p_going = false;
1223                 } else {
1224                     p_nodes ++;
1225                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1226                     if (p_node->selected) {
1227                         p_sel_nodes ++;
1228                         p_sel_range = p_range;
1229                     }
1230                     if (p_node == n_node) {
1231                         n_going = false;
1232                         p_going = false;
1233                     }
1234                 }
1235             } while (n_going || p_going);
1236         }
1238         // Second pass: actually move nodes in this subpath
1239         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1240         {
1241             double n_range = 0, p_range = 0;
1242             bool n_going = true, p_going = true;
1243             Inkscape::NodePath::Node *n_node = n;
1244             Inkscape::NodePath::Node *p_node = n;
1245             do {
1246                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1247                 if (n_node && n_going)
1248                     n_node = n_node->n.other;
1249                 if (n_node == NULL) {
1250                     n_going = false;
1251                 } else {
1252                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1253                     if (n_node->selected) {
1254                         sp_nodepath_move_node_and_handles (n_node,
1255                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1256                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1257                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1258                     }
1259                     if (n_node == p_node) {
1260                         n_going = false;
1261                         p_going = false;
1262                     }
1263                 }
1264                 if (p_node && p_going)
1265                     p_node = p_node->p.other;
1266                 if (p_node == NULL) {
1267                     p_going = false;
1268                 } else {
1269                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1270                     if (p_node->selected) {
1271                         sp_nodepath_move_node_and_handles (p_node,
1272                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1273                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1274                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1275                     }
1276                     if (p_node == n_node) {
1277                         n_going = false;
1278                         p_going = false;
1279                     }
1280                 }
1281             } while (n_going || p_going);
1282         }
1284     } else {
1285         // Multiple subpaths have selected nodes:
1286         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1287         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1288         // fix the pear-like shape when sculpting e.g. a ring
1290         // First pass: calculate range
1291         gdouble direct_range = 0;
1292         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1293             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1294             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1295                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1296                 if (node->selected) {
1297                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1298                 }
1299             }
1300         }
1302         // Second pass: actually move nodes
1303         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1304             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1305             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1306                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1307                 if (node->selected) {
1308                     if (direct_range > 1e-6) {
1309                         sp_nodepath_move_node_and_handles (node,
1310                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1311                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1312                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1313                     } else {
1314                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1315                     }
1317                 }
1318             }
1319         }
1320     }
1322     // do not update repr here so that node dragging is acceptably fast
1323     update_object(nodepath);
1327 /**
1328  * Move node selection to point, adjust its and neighbouring handles,
1329  * handle possible snapping, and commit the change with possible undo.
1330  */
1331 void
1332 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1334     if (!nodepath) return;
1336     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1338     if (dx == 0) {
1339         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1340     } else if (dy == 0) {
1341         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1342     } else {
1343         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1344     }
1347 /**
1348  * Move node selection off screen and commit the change.
1349  */
1350 void
1351 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1353     // borrowed from sp_selection_move_screen in selection-chemistry.c
1354     // we find out the current zoom factor and divide deltas by it
1355     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1357     gdouble zoom = desktop->current_zoom();
1358     gdouble zdx = dx / zoom;
1359     gdouble zdy = dy / zoom;
1361     if (!nodepath) return;
1363     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1365     if (dx == 0) {
1366         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1367     } else if (dy == 0) {
1368         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1369     } else {
1370         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1371     }
1374 /** If they don't yet exist, creates knot and line for the given side of the node */
1375 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1377     if (!side->knot) {
1378         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"));
1380         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1381         side->knot->setSize (7);
1382         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1383         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1384         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1385         sp_knot_update_ctrl(side->knot);
1387         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1388         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1389         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1390         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1391         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1392         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1393     }
1395     if (!side->line) {
1396         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1397                                         SP_TYPE_CTRLLINE, NULL);
1398     }
1401 /**
1402  * Ensure the given handle of the node is visible/invisible, update its screen position
1403  */
1404 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1406     g_assert(node != NULL);
1408    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1409     NRPathcode code = sp_node_path_code_from_side(node, side);
1411     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1413     if (show_handle) {
1414         if (!side->knot) { // No handle knot at all
1415             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1416             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1417             side->knot->pos = side->pos;
1418             if (side->knot->item)
1419                 SP_CTRL(side->knot->item)->moveto(side->pos);
1420             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1421             sp_knot_show(side->knot);
1422         } else {
1423             if (side->knot->pos != side->pos) { // only if it's really moved
1424                 if (fire_move_signals) {
1425                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1426                 } else {
1427                     sp_knot_moveto(side->knot, &side->pos);
1428                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1429                 }
1430             }
1431             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1432                 sp_knot_show(side->knot);
1433             }
1434         }
1435         sp_canvas_item_show(side->line);
1436     } else {
1437         if (side->knot) {
1438             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1439                 sp_knot_hide(side->knot);
1440             }
1441         }
1442         if (side->line) {
1443             sp_canvas_item_hide(side->line);
1444         }
1445     }
1448 /**
1449  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1450  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1451  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1452  * updated; otherwise, just move the knots silently (used in batch moves).
1453  */
1454 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1456     g_assert(node != NULL);
1458     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1459         sp_knot_show(node->knot);
1460     }
1462     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1463         if (fire_move_signals)
1464             sp_knot_set_position(node->knot, &node->pos, 0);
1465         else
1466             sp_knot_moveto(node->knot, &node->pos);
1467     }
1469     gboolean show_handles = node->selected;
1470     if (node->p.other != NULL) {
1471         if (node->p.other->selected) show_handles = TRUE;
1472     }
1473     if (node->n.other != NULL) {
1474         if (node->n.other->selected) show_handles = TRUE;
1475     }
1477     if (node->subpath->nodepath->show_handles == false)
1478         show_handles = FALSE;
1480     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1481     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1484 /**
1485  * Call sp_node_update_handles() for all nodes on subpath.
1486  */
1487 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1489     g_assert(subpath != NULL);
1491     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1492         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1493     }
1496 /**
1497  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1498  */
1499 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1501     g_assert(nodepath != NULL);
1503     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1504         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1505     }
1508 void
1509 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1511     if (nodepath == NULL) return;
1513     nodepath->show_handles = show;
1514     sp_nodepath_update_handles(nodepath);
1517 /**
1518  * Adds all selected nodes in nodepath to list.
1519  */
1520 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1522     StlConv<Node *>::list(l, selected);
1523 /// \todo this adds a copying, rework when the selection becomes a stl list
1526 /**
1527  * Align selected nodes on the specified axis.
1528  */
1529 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1531     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1532         return;
1533     }
1535     if ( !nodepath->selected->next ) { // only one node selected
1536         return;
1537     }
1538    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1539     NR::Point dest(pNode->pos);
1540     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1541         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1542         if (pNode) {
1543             dest[axis] = pNode->pos[axis];
1544             sp_node_moveto(pNode, dest);
1545         }
1546     }
1548     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1551 /// Helper struct.
1552 struct NodeSort
1554    Inkscape::NodePath::Node *_node;
1555     NR::Coord _coord;
1556     /// \todo use vectorof pointers instead of calling copy ctor
1557     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1558         _node(node), _coord(node->pos[axis])
1559     {}
1561 };
1563 static bool operator<(NodeSort const &a, NodeSort const &b)
1565     return (a._coord < b._coord);
1568 /**
1569  * Distribute selected nodes on the specified axis.
1570  */
1571 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1573     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1574         return;
1575     }
1577     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1578         return;
1579     }
1581    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1582     std::vector<NodeSort> sorted;
1583     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1584         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1585         if (pNode) {
1586             NodeSort n(pNode, axis);
1587             sorted.push_back(n);
1588             //dest[axis] = pNode->pos[axis];
1589             //sp_node_moveto(pNode, dest);
1590         }
1591     }
1592     std::sort(sorted.begin(), sorted.end());
1593     unsigned int len = sorted.size();
1594     //overall bboxes span
1595     float dist = (sorted.back()._coord -
1596                   sorted.front()._coord);
1597     //new distance between each bbox
1598     float step = (dist) / (len - 1);
1599     float pos = sorted.front()._coord;
1600     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1601           it < sorted.end();
1602           it ++ )
1603     {
1604         NR::Point dest((*it)._node->pos);
1605         dest[axis] = pos;
1606         sp_node_moveto((*it)._node, dest);
1607         pos += step;
1608     }
1610     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1614 /**
1615  * Call sp_nodepath_line_add_node() for all selected segments.
1616  */
1617 void
1618 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1620     if (!nodepath) {
1621         return;
1622     }
1624     GList *nl = NULL;
1626     int n_added = 0;
1628     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1629        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1630         g_assert(t->selected);
1631         if (t->p.other && t->p.other->selected) {
1632             nl = g_list_prepend(nl, t);
1633         }
1634     }
1636     while (nl) {
1637        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1638        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1639        sp_nodepath_node_select(n, TRUE, FALSE);
1640        n_added ++;
1641        nl = g_list_remove(nl, t);
1642     }
1644     /** \todo fixme: adjust ? */
1645     sp_nodepath_update_handles(nodepath);
1647     if (n_added > 1) {
1648         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1649     } else if (n_added > 0) {
1650         sp_nodepath_update_repr(nodepath, _("Add node"));
1651     }
1653     sp_nodepath_update_statusbar(nodepath);
1656 /**
1657  * Select segment nearest to point
1658  */
1659 void
1660 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1662     if (!nodepath) {
1663         return;
1664     }
1666     sp_nodepath_ensure_livarot_path(nodepath);
1667     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1668     if (!maybe_position) {
1669         return;
1670     }
1671     Path::cut_position position = *maybe_position;
1673     //find segment to segment
1674     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1676     //fixme: this can return NULL, so check before proceeding.
1677     g_return_if_fail(e != NULL);
1679     gboolean force = FALSE;
1680     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1681         force = TRUE;
1682     }
1683     sp_nodepath_node_select(e, (gboolean) toggle, force);
1684     if (e->p.other)
1685         sp_nodepath_node_select(e->p.other, TRUE, force);
1687     sp_nodepath_update_handles(nodepath);
1689     sp_nodepath_update_statusbar(nodepath);
1692 /**
1693  * Add a node nearest to point
1694  */
1695 void
1696 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1698     if (!nodepath) {
1699         return;
1700     }
1702     sp_nodepath_ensure_livarot_path(nodepath);
1703     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1704     if (!maybe_position) {
1705         return;
1706     }
1707     Path::cut_position position = *maybe_position;
1709     //find segment to split
1710     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1712     //don't know why but t seems to flip for lines
1713     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1714         position.t = 1.0 - position.t;
1715     }
1716     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1717     sp_nodepath_node_select(n, FALSE, TRUE);
1719     /* fixme: adjust ? */
1720     sp_nodepath_update_handles(nodepath);
1722     sp_nodepath_update_repr(nodepath, _("Add node"));
1724     sp_nodepath_update_statusbar(nodepath);
1727 /*
1728  * Adjusts a segment so that t moves by a certain delta for dragging
1729  * converts lines to curves
1730  *
1731  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1732  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1733  */
1734 void
1735 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1737     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1739     //fixme: e and e->p can be NULL, so check for those before proceeding
1740     g_return_if_fail(e != NULL);
1741     g_return_if_fail(&e->p != NULL);
1743     /* feel good is an arbitrary parameter that distributes the delta between handles
1744      * if t of the drag point is less than 1/6 distance form the endpoint only
1745      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1746      */
1747     double feel_good;
1748     if (t <= 1.0 / 6.0)
1749         feel_good = 0;
1750     else if (t <= 0.5)
1751         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1752     else if (t <= 5.0 / 6.0)
1753         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1754     else
1755         feel_good = 1;
1757     //if we're dragging a line convert it to a curve
1758     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1759         sp_nodepath_set_line_type(e, NR_CURVETO);
1760     }
1762     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1763     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1764     e->p.other->n.pos += offsetcoord0;
1765     e->p.pos += offsetcoord1;
1767     // adjust handles of adjacent nodes where necessary
1768     sp_node_adjust_handle(e,1);
1769     sp_node_adjust_handle(e->p.other,-1);
1771     sp_nodepath_update_handles(e->subpath->nodepath);
1773     update_object(e->subpath->nodepath);
1775     sp_nodepath_update_statusbar(e->subpath->nodepath);
1779 /**
1780  * Call sp_nodepath_break() for all selected segments.
1781  */
1782 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1784     if (!nodepath) return;
1786     GList *temp = NULL;
1787     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1788        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1789        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1790         if (nn == NULL) continue; // no break, no new node
1791         temp = g_list_prepend(temp, nn);
1792     }
1794     if (temp) {
1795         sp_nodepath_deselect(nodepath);
1796     }
1797     for (GList *l = temp; l != NULL; l = l->next) {
1798         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1799     }
1801     sp_nodepath_update_handles(nodepath);
1803     sp_nodepath_update_repr(nodepath, _("Break path"));
1806 /**
1807  * Duplicate the selected node(s).
1808  */
1809 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1811     if (!nodepath) {
1812         return;
1813     }
1815     GList *temp = NULL;
1816     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1817        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1818        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1819         if (nn == NULL) continue; // could not duplicate
1820         temp = g_list_prepend(temp, nn);
1821     }
1823     if (temp) {
1824         sp_nodepath_deselect(nodepath);
1825     }
1826     for (GList *l = temp; l != NULL; l = l->next) {
1827         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1828     }
1830     sp_nodepath_update_handles(nodepath);
1832     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1835 /**
1836  *  Join two nodes by merging them into one.
1837  */
1838 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
1840     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1842     if (g_list_length(nodepath->selected) != 2) {
1843         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1844         return;
1845     }
1847    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1848    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1850     g_assert(a != b);
1851     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
1852         // someone tried to join an orphan node (i.e. a single-node subpath).
1853         // this is not worth an error message, just fail silently.
1854         return;
1855     }
1857     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1858         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1859         return;
1860     }
1862     /* a and b are endpoints */
1864     NR::Point c;
1865     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1866         c = a->pos;
1867     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1868         c = b->pos;
1869     } else {
1870         c = (a->pos + b->pos) / 2;
1871     }
1873     if (a->subpath == b->subpath) {
1874        Inkscape::NodePath::SubPath *sp = a->subpath;
1875         sp_nodepath_subpath_close(sp);
1876         sp_node_moveto (sp->first, c);
1878         sp_nodepath_update_handles(sp->nodepath);
1879         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1880         return;
1881     }
1883     /* a and b are separate subpaths */
1884    Inkscape::NodePath::SubPath *sa = a->subpath;
1885    Inkscape::NodePath::SubPath *sb = b->subpath;
1886     NR::Point p;
1887    Inkscape::NodePath::Node *n;
1888     NRPathcode code;
1889     if (a == sa->first) {
1890         p = sa->first->n.pos;
1891         code = (NRPathcode)sa->first->n.other->code;
1892        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1893         n = sa->last;
1894         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1895         n = n->p.other;
1896         while (n) {
1897             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1898             n = n->p.other;
1899             if (n == sa->first) n = NULL;
1900         }
1901         sp_nodepath_subpath_destroy(sa);
1902         sa = t;
1903     } else if (a == sa->last) {
1904         p = sa->last->p.pos;
1905         code = (NRPathcode)sa->last->code;
1906         sp_nodepath_node_destroy(sa->last);
1907     } else {
1908         code = NR_END;
1909         g_assert_not_reached();
1910     }
1912     if (b == sb->first) {
1913         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1914         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1915             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1916         }
1917     } else if (b == sb->last) {
1918         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1919         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1920             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1921         }
1922     } else {
1923         g_assert_not_reached();
1924     }
1925     /* and now destroy sb */
1927     sp_nodepath_subpath_destroy(sb);
1929     sp_nodepath_update_handles(sa->nodepath);
1931     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1933     sp_nodepath_update_statusbar(nodepath);
1936 /**
1937  *  Join two nodes by adding a segment between them.
1938  */
1939 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
1941     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1943     if (g_list_length(nodepath->selected) != 2) {
1944         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1945         return;
1946     }
1948    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1949    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1951     g_assert(a != b);
1952     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
1953         // someone tried to join an orphan node (i.e. a single-node subpath).
1954         // this is not worth an error message, just fail silently.
1955         return;
1956     }
1958     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1959         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1960         return;
1961     }
1963     if (a->subpath == b->subpath) {
1964        Inkscape::NodePath::SubPath *sp = a->subpath;
1966         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1967         sp->closed = TRUE;
1969         sp->first->p.other = sp->last;
1970         sp->last->n.other  = sp->first;
1972         sp_node_handle_mirror_p_to_n(sp->last);
1973         sp_node_handle_mirror_n_to_p(sp->first);
1975         sp->first->code = sp->last->code;
1976         sp->first       = sp->last;
1978         sp_nodepath_update_handles(sp->nodepath);
1980         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1982         return;
1983     }
1985     /* a and b are separate subpaths */
1986    Inkscape::NodePath::SubPath *sa = a->subpath;
1987    Inkscape::NodePath::SubPath *sb = b->subpath;
1989    Inkscape::NodePath::Node *n;
1990     NR::Point p;
1991     NRPathcode code;
1992     if (a == sa->first) {
1993         code = (NRPathcode) sa->first->n.other->code;
1994        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1995         n = sa->last;
1996         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1997         for (n = n->p.other; n != NULL; n = n->p.other) {
1998             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1999         }
2000         sp_nodepath_subpath_destroy(sa);
2001         sa = t;
2002     } else if (a == sa->last) {
2003         code = (NRPathcode)sa->last->code;
2004     } else {
2005         code = NR_END;
2006         g_assert_not_reached();
2007     }
2009     if (b == sb->first) {
2010         n = sb->first;
2011         sp_node_handle_mirror_p_to_n(sa->last);
2012         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2013         sp_node_handle_mirror_n_to_p(sa->last);
2014         for (n = n->n.other; n != NULL; n = n->n.other) {
2015             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2016         }
2017     } else if (b == sb->last) {
2018         n = sb->last;
2019         sp_node_handle_mirror_p_to_n(sa->last);
2020         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2021         sp_node_handle_mirror_n_to_p(sa->last);
2022         for (n = n->p.other; n != NULL; n = n->p.other) {
2023             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2024         }
2025     } else {
2026         g_assert_not_reached();
2027     }
2028     /* and now destroy sb */
2030     sp_nodepath_subpath_destroy(sb);
2032     sp_nodepath_update_handles(sa->nodepath);
2034     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2037 /**
2038  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2039  */
2040 void sp_node_delete_preserve(GList *nodes_to_delete)
2042     GSList *nodepaths = NULL;
2044     while (nodes_to_delete) {
2045         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2046         Inkscape::NodePath::SubPath *sp = node->subpath;
2047         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2048         Inkscape::NodePath::Node *sample_cursor = NULL;
2049         Inkscape::NodePath::Node *sample_end = NULL;
2050         Inkscape::NodePath::Node *delete_cursor = node;
2051         bool just_delete = false;
2053         //find the start of this contiguous selection
2054         //move left to the first node that is not selected
2055         //or the start of the non-closed path
2056         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2057             delete_cursor = curr;
2058         }
2060         //just delete at the beginning of an open path
2061         if (!delete_cursor->p.other) {
2062             sample_cursor = delete_cursor;
2063             just_delete = true;
2064         } else {
2065             sample_cursor = delete_cursor->p.other;
2066         }
2068         //calculate points for each segment
2069         int rate = 5;
2070         float period = 1.0 / rate;
2071         std::vector<NR::Point> data;
2072         if (!just_delete) {
2073             data.push_back(sample_cursor->pos);
2074             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2075                 //just delete at the end of an open path
2076                 if (!sp->closed && curr == sp->last) {
2077                     just_delete = true;
2078                     break;
2079                 }
2081                 //sample points on the contiguous selected segment
2082                 NR::Point *bez;
2083                 bez = new NR::Point [4];
2084                 bez[0] = curr->pos;
2085                 bez[1] = curr->n.pos;
2086                 bez[2] = curr->n.other->p.pos;
2087                 bez[3] = curr->n.other->pos;
2088                 for (int i=1; i<rate; i++) {
2089                     gdouble t = i * period;
2090                     NR::Point p = bezier_pt(3, bez, t);
2091                     data.push_back(p);
2092                 }
2093                 data.push_back(curr->n.other->pos);
2095                 sample_end = curr->n.other;
2096                 //break if we've come full circle or hit the end of the selection
2097                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2098                     break;
2099                 }
2100             }
2101         }
2103         if (!just_delete) {
2104             //calculate the best fitting single segment and adjust the endpoints
2105             NR::Point *adata;
2106             adata = new NR::Point [data.size()];
2107             copy(data.begin(), data.end(), adata);
2109             NR::Point *bez;
2110             bez = new NR::Point [4];
2111             //would decreasing error create a better fitting approximation?
2112             gdouble error = 1.0;
2113             gint ret;
2114             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2116             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2117             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2118             //the resulting nodes behave as expected.
2119             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2120             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2122             //adjust endpoints
2123             sample_cursor->n.pos = bez[1];
2124             sample_end->p.pos = bez[2];
2125         }
2127         //destroy this contiguous selection
2128         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2129             Inkscape::NodePath::Node *temp = delete_cursor;
2130             if (delete_cursor->n.other == delete_cursor) {
2131                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2132                 delete_cursor = NULL;
2133             } else {
2134                 delete_cursor = delete_cursor->n.other;
2135             }
2136             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2137             sp_nodepath_node_destroy(temp);
2138         }
2140         sp_nodepath_update_handles(nodepath);
2142         if (!g_slist_find(nodepaths, nodepath))
2143             nodepaths = g_slist_prepend (nodepaths, nodepath);
2144     }
2146     for (GSList *i = nodepaths; i; i = i->next) {
2147         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2148         // different nodepaths will give us one undo event per nodepath
2149         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2151         // if the entire nodepath is removed, delete the selected object.
2152         if (nodepath->subpaths == NULL ||
2153             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2154             //at least 2
2155             sp_nodepath_get_node_count(nodepath) < 2) {
2156             SPDocument *document = sp_desktop_document (nodepath->desktop);
2157             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2158             //delete this nodepath's object, not the entire selection! (though at this time, this
2159             //does not matter)
2160             sp_selection_delete();
2161             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2162                               _("Delete nodes"));
2163         } else {
2164             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2165             sp_nodepath_update_statusbar(nodepath);
2166         }
2167     }
2169     g_slist_free (nodepaths);
2172 /**
2173  * Delete one or more selected nodes.
2174  */
2175 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2177     if (!nodepath) return;
2178     if (!nodepath->selected) return;
2180     /** \todo fixme: do it the right way */
2181     while (nodepath->selected) {
2182        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2183         sp_nodepath_node_destroy(node);
2184     }
2187     //clean up the nodepath (such as for trivial subpaths)
2188     sp_nodepath_cleanup(nodepath);
2190     sp_nodepath_update_handles(nodepath);
2192     // if the entire nodepath is removed, delete the selected object.
2193     if (nodepath->subpaths == NULL ||
2194         sp_nodepath_get_node_count(nodepath) < 2) {
2195         SPDocument *document = sp_desktop_document (nodepath->desktop);
2196         sp_selection_delete();
2197         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2198                           _("Delete nodes"));
2199         return;
2200     }
2202     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2204     sp_nodepath_update_statusbar(nodepath);
2207 /**
2208  * Delete one or more segments between two selected nodes.
2209  * This is the code for 'split'.
2210  */
2211 void
2212 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2214    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2215    Inkscape::NodePath::Node *curr, *next;     //Iterators
2217     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2219     if (g_list_length(nodepath->selected) != 2) {
2220         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2221                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2222         return;
2223     }
2225     //Selected nodes, not inclusive
2226    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2227    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2229     if ( ( a==b)                       ||  //same node
2230          (a->subpath  != b->subpath )  ||  //not the same path
2231          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2232          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2233     {
2234         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2235                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2236         return;
2237     }
2239     //###########################################
2240     //# BEGIN EDITS
2241     //###########################################
2242     //##################################
2243     //# CLOSED PATH
2244     //##################################
2245     if (a->subpath->closed) {
2248         gboolean reversed = FALSE;
2250         //Since we can go in a circle, we need to find the shorter distance.
2251         //  a->b or b->a
2252         start = end = NULL;
2253         int distance    = 0;
2254         int minDistance = 0;
2255         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2256             if (curr==b) {
2257                 //printf("a to b:%d\n", distance);
2258                 start = a;//go from a to b
2259                 end   = b;
2260                 minDistance = distance;
2261                 //printf("A to B :\n");
2262                 break;
2263             }
2264             distance++;
2265         }
2267         //try again, the other direction
2268         distance = 0;
2269         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2270             if (curr==a) {
2271                 //printf("b to a:%d\n", distance);
2272                 if (distance < minDistance) {
2273                     start    = b;  //we go from b to a
2274                     end      = a;
2275                     reversed = TRUE;
2276                     //printf("B to A\n");
2277                 }
2278                 break;
2279             }
2280             distance++;
2281         }
2284         //Copy everything from 'end' to 'start' to a new subpath
2285        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2286         for (curr=end ; curr ; curr=curr->n.other) {
2287             NRPathcode code = (NRPathcode) curr->code;
2288             if (curr == end)
2289                 code = NR_MOVETO;
2290             sp_nodepath_node_new(t, NULL,
2291                                  (Inkscape::NodePath::NodeType)curr->type, code,
2292                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2293             if (curr == start)
2294                 break;
2295         }
2296         sp_nodepath_subpath_destroy(a->subpath);
2299     }
2303     //##################################
2304     //# OPEN PATH
2305     //##################################
2306     else {
2308         //We need to get the direction of the list between A and B
2309         //Can we walk from a to b?
2310         start = end = NULL;
2311         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2312             if (curr==b) {
2313                 start = a;  //did it!  we go from a to b
2314                 end   = b;
2315                 //printf("A to B\n");
2316                 break;
2317             }
2318         }
2319         if (!start) {//didn't work?  let's try the other direction
2320             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2321                 if (curr==a) {
2322                     start = b;  //did it!  we go from b to a
2323                     end   = a;
2324                     //printf("B to A\n");
2325                     break;
2326                 }
2327             }
2328         }
2329         if (!start) {
2330             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2331                                                      _("Cannot find path between nodes."));
2332             return;
2333         }
2337         //Copy everything after 'end' to a new subpath
2338        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2339         for (curr=end ; curr ; curr=curr->n.other) {
2340             NRPathcode code = (NRPathcode) curr->code;
2341             if (curr == end)
2342                 code = NR_MOVETO;
2343             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2344                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2345         }
2347         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2348         for (curr = start->n.other ; curr  ; curr=next) {
2349             next = curr->n.other;
2350             sp_nodepath_node_destroy(curr);
2351         }
2353     }
2354     //###########################################
2355     //# END EDITS
2356     //###########################################
2358     //clean up the nodepath (such as for trivial subpaths)
2359     sp_nodepath_cleanup(nodepath);
2361     sp_nodepath_update_handles(nodepath);
2363     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2365     sp_nodepath_update_statusbar(nodepath);
2368 /**
2369  * Call sp_nodepath_set_line() for all selected segments.
2370  */
2371 void
2372 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2374     if (nodepath == NULL) return;
2376     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2377        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2378         g_assert(n->selected);
2379         if (n->p.other && n->p.other->selected) {
2380             sp_nodepath_set_line_type(n, code);
2381         }
2382     }
2384     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2387 /**
2388  * Call sp_nodepath_convert_node_type() for all selected nodes.
2389  */
2390 void
2391 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2393     if (nodepath == NULL) return;
2395     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2397     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2398         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2399     }
2401     sp_nodepath_update_repr(nodepath, _("Change node type"));
2404 /**
2405  * Change select status of node, update its own and neighbour handles.
2406  */
2407 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2409     node->selected = selected;
2411     if (selected) {
2412         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2413         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2414         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2415         sp_knot_update_ctrl(node->knot);
2416     } else {
2417         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2418         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2419         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2420         sp_knot_update_ctrl(node->knot);
2421     }
2423     sp_node_update_handles(node);
2424     if (node->n.other) sp_node_update_handles(node->n.other);
2425     if (node->p.other) sp_node_update_handles(node->p.other);
2428 /**
2429 \brief Select a node
2430 \param node     The node to select
2431 \param incremental   If true, add to selection, otherwise deselect others
2432 \param override   If true, always select this node, otherwise toggle selected status
2433 */
2434 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2436     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2438     if (incremental) {
2439         if (override) {
2440             if (!g_list_find(nodepath->selected, node)) {
2441                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2442             }
2443             sp_node_set_selected(node, TRUE);
2444         } else { // toggle
2445             if (node->selected) {
2446                 g_assert(g_list_find(nodepath->selected, node));
2447                 nodepath->selected = g_list_remove(nodepath->selected, node);
2448             } else {
2449                 g_assert(!g_list_find(nodepath->selected, node));
2450                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2451             }
2452             sp_node_set_selected(node, !node->selected);
2453         }
2454     } else {
2455         sp_nodepath_deselect(nodepath);
2456         nodepath->selected = g_list_prepend(nodepath->selected, node);
2457         sp_node_set_selected(node, TRUE);
2458     }
2460     sp_nodepath_update_statusbar(nodepath);
2464 /**
2465 \brief Deselect all nodes in the nodepath
2466 */
2467 void
2468 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2470     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2472     while (nodepath->selected) {
2473         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2474         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2475     }
2476     sp_nodepath_update_statusbar(nodepath);
2479 /**
2480 \brief Select or invert selection of all nodes in the nodepath
2481 */
2482 void
2483 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2485     if (!nodepath) return;
2487     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2488        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2489         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2490            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2491            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2492         }
2493     }
2496 /**
2497  * If nothing selected, does the same as sp_nodepath_select_all();
2498  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2499  * (i.e., similar to "select all in layer", with the "selected" subpaths
2500  * being treated as "layers" in the path).
2501  */
2502 void
2503 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2505     if (!nodepath) return;
2507     if (g_list_length (nodepath->selected) == 0) {
2508         sp_nodepath_select_all (nodepath, invert);
2509         return;
2510     }
2512     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2513     GSList *subpaths = NULL;
2515     for (GList *l = copy; l != NULL; l = l->next) {
2516         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2517         Inkscape::NodePath::SubPath *subpath = n->subpath;
2518         if (!g_slist_find (subpaths, subpath))
2519             subpaths = g_slist_prepend (subpaths, subpath);
2520     }
2522     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2523         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2524         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2525             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2526             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2527         }
2528     }
2530     g_slist_free (subpaths);
2531     g_list_free (copy);
2534 /**
2535  * \brief Select the node after the last selected; if none is selected,
2536  * select the first within path.
2537  */
2538 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2540     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2542    Inkscape::NodePath::Node *last = NULL;
2543     if (nodepath->selected) {
2544         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2545            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2546             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2547             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2548                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2549                 if (node->selected) {
2550                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2551                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2552                             if (spl->next) { // there's a next subpath
2553                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2554                                 last = subpath_next->first;
2555                             } else if (spl->prev) { // there's a previous subpath
2556                                 last = NULL; // to be set later to the first node of first subpath
2557                             } else {
2558                                 last = node->n.other;
2559                             }
2560                         } else {
2561                             last = node->n.other;
2562                         }
2563                     } else {
2564                         if (node->n.other) {
2565                             last = node->n.other;
2566                         } else {
2567                             if (spl->next) { // there's a next subpath
2568                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2569                                 last = subpath_next->first;
2570                             } else if (spl->prev) { // there's a previous subpath
2571                                 last = NULL; // to be set later to the first node of first subpath
2572                             } else {
2573                                 last = (Inkscape::NodePath::Node *) subpath->first;
2574                             }
2575                         }
2576                     }
2577                 }
2578             }
2579         }
2580         sp_nodepath_deselect(nodepath);
2581     }
2583     if (last) { // there's at least one more node after selected
2584         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2585     } else { // no more nodes, select the first one in first subpath
2586        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2587         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2588     }
2591 /**
2592  * \brief Select the node before the first selected; if none is selected,
2593  * select the last within path
2594  */
2595 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2597     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2599    Inkscape::NodePath::Node *last = NULL;
2600     if (nodepath->selected) {
2601         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2602            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2603             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2604                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2605                 if (node->selected) {
2606                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2607                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2608                             if (spl->prev) { // there's a prev subpath
2609                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2610                                 last = subpath_prev->last;
2611                             } else if (spl->next) { // there's a next subpath
2612                                 last = NULL; // to be set later to the last node of last subpath
2613                             } else {
2614                                 last = node->p.other;
2615                             }
2616                         } else {
2617                             last = node->p.other;
2618                         }
2619                     } else {
2620                         if (node->p.other) {
2621                             last = node->p.other;
2622                         } else {
2623                             if (spl->prev) { // there's a prev subpath
2624                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2625                                 last = subpath_prev->last;
2626                             } else if (spl->next) { // there's a next subpath
2627                                 last = NULL; // to be set later to the last node of last subpath
2628                             } else {
2629                                 last = (Inkscape::NodePath::Node *) subpath->last;
2630                             }
2631                         }
2632                     }
2633                 }
2634             }
2635         }
2636         sp_nodepath_deselect(nodepath);
2637     }
2639     if (last) { // there's at least one more node before selected
2640         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2641     } else { // no more nodes, select the last one in last subpath
2642         GList *spl = g_list_last(nodepath->subpaths);
2643        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2644         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2645     }
2648 /**
2649  * \brief Select all nodes that are within the rectangle.
2650  */
2651 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2653     if (!incremental) {
2654         sp_nodepath_deselect(nodepath);
2655     }
2657     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2658        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2659         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2660            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2662             if (b.contains(node->pos)) {
2663                 sp_nodepath_node_select(node, TRUE, TRUE);
2664             }
2665         }
2666     }
2670 void
2671 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2673     g_assert (n);
2674     g_assert (nodepath);
2675     g_assert (n->subpath->nodepath == nodepath);
2677     if (g_list_length (nodepath->selected) == 0) {
2678         if (grow > 0) {
2679             sp_nodepath_node_select(n, TRUE, TRUE);
2680         }
2681         return;
2682     }
2684     if (g_list_length (nodepath->selected) == 1) {
2685         if (grow < 0) {
2686             sp_nodepath_deselect (nodepath);
2687             return;
2688         }
2689     }
2691         double n_sel_range = 0, p_sel_range = 0;
2692             Inkscape::NodePath::Node *farthest_n_node = n;
2693             Inkscape::NodePath::Node *farthest_p_node = n;
2695         // Calculate ranges
2696         {
2697             double n_range = 0, p_range = 0;
2698             bool n_going = true, p_going = true;
2699             Inkscape::NodePath::Node *n_node = n;
2700             Inkscape::NodePath::Node *p_node = n;
2701             do {
2702                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2703                 if (n_node && n_going)
2704                     n_node = n_node->n.other;
2705                 if (n_node == NULL) {
2706                     n_going = false;
2707                 } else {
2708                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2709                     if (n_node->selected) {
2710                         n_sel_range = n_range;
2711                         farthest_n_node = n_node;
2712                     }
2713                     if (n_node == p_node) {
2714                         n_going = false;
2715                         p_going = false;
2716                     }
2717                 }
2718                 if (p_node && p_going)
2719                     p_node = p_node->p.other;
2720                 if (p_node == NULL) {
2721                     p_going = false;
2722                 } else {
2723                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2724                     if (p_node->selected) {
2725                         p_sel_range = p_range;
2726                         farthest_p_node = p_node;
2727                     }
2728                     if (p_node == n_node) {
2729                         n_going = false;
2730                         p_going = false;
2731                     }
2732                 }
2733             } while (n_going || p_going);
2734         }
2736     if (grow > 0) {
2737         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2738                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2739         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2740                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2741         }
2742     } else {
2743         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2744                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2745         } else if (farthest_p_node && farthest_p_node->selected) {
2746                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2747         }
2748     }
2751 void
2752 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2754     g_assert (n);
2755     g_assert (nodepath);
2756     g_assert (n->subpath->nodepath == nodepath);
2758     if (g_list_length (nodepath->selected) == 0) {
2759         if (grow > 0) {
2760             sp_nodepath_node_select(n, TRUE, TRUE);
2761         }
2762         return;
2763     }
2765     if (g_list_length (nodepath->selected) == 1) {
2766         if (grow < 0) {
2767             sp_nodepath_deselect (nodepath);
2768             return;
2769         }
2770     }
2772     Inkscape::NodePath::Node *farthest_selected = NULL;
2773     double farthest_dist = 0;
2775     Inkscape::NodePath::Node *closest_unselected = NULL;
2776     double closest_dist = NR_HUGE;
2778     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2779        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2780         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2781            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2782            if (node == n)
2783                continue;
2784            if (node->selected) {
2785                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2786                    farthest_dist = NR::L2(node->pos - n->pos);
2787                    farthest_selected = node;
2788                }
2789            } else {
2790                if (NR::L2(node->pos - n->pos) < closest_dist) {
2791                    closest_dist = NR::L2(node->pos - n->pos);
2792                    closest_unselected = node;
2793                }
2794            }
2795         }
2796     }
2798     if (grow > 0) {
2799         if (closest_unselected) {
2800             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2801         }
2802     } else {
2803         if (farthest_selected) {
2804             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2805         }
2806     }
2810 /**
2811 \brief  Saves all nodes' and handles' current positions in their origin members
2812 */
2813 void
2814 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2816     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2817        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2818         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2819            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2820            n->origin = n->pos;
2821            n->p.origin = n->p.pos;
2822            n->n.origin = n->n.pos;
2823         }
2824     }
2827 /**
2828 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2829 */
2830 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2832     if (!nodepath->selected) {
2833         return NULL;
2834     }
2836     GList *r = NULL;
2837     guint i = 0;
2838     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2839        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2840         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2841            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2842             i++;
2843             if (node->selected) {
2844                 r = g_list_append(r, GINT_TO_POINTER(i));
2845             }
2846         }
2847     }
2848     return r;
2851 /**
2852 \brief  Restores selection by selecting nodes whose positions are in the list
2853 */
2854 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2856     sp_nodepath_deselect(nodepath);
2858     guint i = 0;
2859     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2860        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2861         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2862            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2863             i++;
2864             if (g_list_find(r, GINT_TO_POINTER(i))) {
2865                 sp_nodepath_node_select(node, TRUE, TRUE);
2866             }
2867         }
2868     }
2872 /**
2873 \brief Adjusts handle according to node type and line code.
2874 */
2875 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2877     double len, otherlen, linelen;
2879     g_assert(node);
2881    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2882    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2884     /** \todo fixme: */
2885     if (me->other == NULL) return;
2886     if (other->other == NULL) return;
2888     /* I have line */
2890     NRPathcode mecode, ocode;
2891     if (which_adjust == 1) {
2892         mecode = (NRPathcode)me->other->code;
2893         ocode = (NRPathcode)node->code;
2894     } else {
2895         mecode = (NRPathcode)node->code;
2896         ocode = (NRPathcode)other->other->code;
2897     }
2899     if (mecode == NR_LINETO) return;
2901     /* I am curve */
2903     if (other->other == NULL) return;
2905     /* Other has line */
2907     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2909     NR::Point delta;
2910     if (ocode == NR_LINETO) {
2911         /* other is lineto, we are either smooth or symm */
2912        Inkscape::NodePath::Node *othernode = other->other;
2913         len = NR::L2(me->pos - node->pos);
2914         delta = node->pos - othernode->pos;
2915         linelen = NR::L2(delta);
2916         if (linelen < 1e-18)
2917             return;
2918         me->pos = node->pos + (len / linelen)*delta;
2919         return;
2920     }
2922     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2924         me->pos = 2 * node->pos - other->pos;
2925         return;
2926     }
2928     /* We are smooth */
2930     len = NR::L2(me->pos - node->pos);
2931     delta = other->pos - node->pos;
2932     otherlen = NR::L2(delta);
2933     if (otherlen < 1e-18) return;
2935     me->pos = node->pos - (len / otherlen) * delta;
2938 /**
2939  \brief Adjusts both handles according to node type and line code
2940  */
2941 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2943     g_assert(node);
2945     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2947     /* we are either smooth or symm */
2949     if (node->p.other == NULL) return;
2951     if (node->n.other == NULL) return;
2953     if (node->code == NR_LINETO) {
2954         if (node->n.other->code == NR_LINETO) return;
2955         sp_node_adjust_handle(node, 1);
2956         return;
2957     }
2959     if (node->n.other->code == NR_LINETO) {
2960         if (node->code == NR_LINETO) return;
2961         sp_node_adjust_handle(node, -1);
2962         return;
2963     }
2965     /* both are curves */
2966     NR::Point const delta( node->n.pos - node->p.pos );
2968     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2969         node->p.pos = node->pos - delta / 2;
2970         node->n.pos = node->pos + delta / 2;
2971         return;
2972     }
2974     /* We are smooth */
2975     double plen = NR::L2(node->p.pos - node->pos);
2976     if (plen < 1e-18) return;
2977     double nlen = NR::L2(node->n.pos - node->pos);
2978     if (nlen < 1e-18) return;
2979     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2980     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2983 /**
2984  * Node event callback.
2985  */
2986 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
2988     gboolean ret = FALSE;
2989     switch (event->type) {
2990         case GDK_ENTER_NOTIFY:
2991             Inkscape::NodePath::Path::active_node = n;
2992             break;
2993         case GDK_LEAVE_NOTIFY:
2994             Inkscape::NodePath::Path::active_node = NULL;
2995             break;
2996         case GDK_SCROLL:
2997             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
2998                 switch (event->scroll.direction) {
2999                     case GDK_SCROLL_UP:
3000                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3001                         break;
3002                     case GDK_SCROLL_DOWN:
3003                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3004                         break;
3005                     default:
3006                         break;
3007                 }
3008                 ret = TRUE;
3009             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3010                 switch (event->scroll.direction) {
3011                     case GDK_SCROLL_UP:
3012                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3013                         break;
3014                     case GDK_SCROLL_DOWN:
3015                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3016                         break;
3017                     default:
3018                         break;
3019                 }
3020                 ret = TRUE;
3021             }
3022             break;
3023         case GDK_KEY_PRESS:
3024             switch (get_group0_keyval (&event->key)) {
3025                 case GDK_space:
3026                     if (event->key.state & GDK_BUTTON1_MASK) {
3027                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3028                         stamp_repr(nodepath);
3029                         ret = TRUE;
3030                     }
3031                     break;
3032                 case GDK_Page_Up:
3033                     if (event->key.state & GDK_CONTROL_MASK) {
3034                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3035                     } else {
3036                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3037                     }
3038                     break;
3039                 case GDK_Page_Down:
3040                     if (event->key.state & GDK_CONTROL_MASK) {
3041                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3042                     } else {
3043                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3044                     }
3045                     break;
3046                 default:
3047                     break;
3048             }
3049             break;
3050         default:
3051             break;
3052     }
3054     return ret;
3057 /**
3058  * Handle keypress on node; directly called.
3059  */
3060 gboolean node_key(GdkEvent *event)
3062     Inkscape::NodePath::Path *np;
3064     // there is no way to verify nodes so set active_node to nil when deleting!!
3065     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3067     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3068         gint ret = FALSE;
3069         switch (get_group0_keyval (&event->key)) {
3070             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3071             case GDK_BackSpace:
3072                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3073                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3074                 sp_nodepath_update_repr(np, _("Delete node"));
3075                 Inkscape::NodePath::Path::active_node = NULL;
3076                 ret = TRUE;
3077                 break;
3078             case GDK_c:
3079                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3080                 ret = TRUE;
3081                 break;
3082             case GDK_s:
3083                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3084                 ret = TRUE;
3085                 break;
3086             case GDK_y:
3087                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3088                 ret = TRUE;
3089                 break;
3090             case GDK_b:
3091                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3092                 ret = TRUE;
3093                 break;
3094         }
3095         return ret;
3096     }
3097     return FALSE;
3100 /**
3101  * Mouseclick on node callback.
3102  */
3103 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3105    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3107     if (state & GDK_CONTROL_MASK) {
3108         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3110         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3111             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3112                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3113             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3114                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3115             } else {
3116                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3117             }
3118             sp_nodepath_update_repr(nodepath, _("Change node type"));
3119             sp_nodepath_update_statusbar(nodepath);
3121         } else { //ctrl+alt+click: delete node
3122             GList *node_to_delete = NULL;
3123             node_to_delete = g_list_append(node_to_delete, n);
3124             sp_node_delete_preserve(node_to_delete);
3125         }
3127     } else {
3128         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3129     }
3132 /**
3133  * Mouse grabbed node callback.
3134  */
3135 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3137    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3139     if (!n->selected) {
3140         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3141     }
3143     n->is_dragging = true;
3144     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3146     sp_nodepath_remember_origins (n->subpath->nodepath);
3149 /**
3150  * Mouse ungrabbed node callback.
3151  */
3152 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3154    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3156    n->dragging_out = NULL;
3157    n->is_dragging = false;
3158    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3160    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3163 /**
3164  * The point on a line, given by its angle, closest to the given point.
3165  * \param p  A point.
3166  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3167  * \param closest  Pointer to the point struct where the result is stored.
3168  * \todo FIXME: use dot product perhaps?
3169  */
3170 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3172     if (a == HUGE_VAL) { // vertical
3173         *closest = NR::Point(0, (*p)[NR::Y]);
3174     } else {
3175         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3176         (*closest)[NR::Y] = a * (*closest)[NR::X];
3177     }
3180 /**
3181  * Distance from the point to a line given by its angle.
3182  * \param p  A point.
3183  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3184  */
3185 static double point_line_distance(NR::Point *p, double a)
3187     NR::Point c;
3188     point_line_closest(p, a, &c);
3189     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]));
3192 /**
3193  * Callback for node "request" signal.
3194  * \todo fixme: This goes to "moved" event? (lauris)
3195  */
3196 static gboolean
3197 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3199     double yn, xn, yp, xp;
3200     double an, ap, na, pa;
3201     double d_an, d_ap, d_na, d_pa;
3202     gboolean collinear = FALSE;
3203     NR::Point c;
3204     NR::Point pr;
3206    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3208    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3209     if ( (!n->subpath->nodepath->straight_path) &&
3210          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3211            || n->dragging_out ) )
3212     {
3213        NR::Point mouse = (*p);
3215        if (!n->dragging_out) {
3216            // This is the first drag-out event; find out which handle to drag out
3217            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3218            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3220            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3221                return FALSE;
3223            Inkscape::NodePath::NodeSide *opposite;
3224            if (appr_p > appr_n) { // closer to p
3225                n->dragging_out = &n->p;
3226                opposite = &n->n;
3227                n->code = NR_CURVETO;
3228            } else if (appr_p < appr_n) { // closer to n
3229                n->dragging_out = &n->n;
3230                opposite = &n->p;
3231                n->n.other->code = NR_CURVETO;
3232            } else { // p and n nodes are the same
3233                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3234                    n->dragging_out = &n->p;
3235                    opposite = &n->n;
3236                    n->code = NR_CURVETO;
3237                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3238                    n->dragging_out = &n->n;
3239                    opposite = &n->p;
3240                    n->n.other->code = NR_CURVETO;
3241                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3242                    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);
3243                    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);
3244                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3245                        n->dragging_out = &n->n;
3246                        opposite = &n->p;
3247                        n->n.other->code = NR_CURVETO;
3248                    } else { // closer to other's n handle
3249                        n->dragging_out = &n->p;
3250                        opposite = &n->n;
3251                        n->code = NR_CURVETO;
3252                    }
3253                }
3254            }
3256            // if there's another handle, make sure the one we drag out starts parallel to it
3257            if (opposite->pos != n->pos) {
3258                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3259            }
3261            // knots might not be created yet!
3262            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3263            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3264        }
3266        // pass this on to the handle-moved callback
3267        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3268        sp_node_update_handles(n);
3269        return TRUE;
3270    }
3272     if (state & GDK_CONTROL_MASK) { // constrained motion
3274         // calculate relative distances of handles
3275         // n handle:
3276         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3277         xn = n->n.pos[NR::X] - n->pos[NR::X];
3278         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3279         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3280             if (n->n.other) { // if there is the next point
3281                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3282                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3283                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3284             }
3285         }
3286         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3287         if (yn < 0) { xn = -xn; yn = -yn; }
3289         // p handle:
3290         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3291         xp = n->p.pos[NR::X] - n->pos[NR::X];
3292         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3293         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3294             if (n->p.other) {
3295                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3296                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3297                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3298             }
3299         }
3300         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3301         if (yp < 0) { xp = -xp; yp = -yp; }
3303         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3304             // sliding on handles, only if at least one of the handles is non-vertical
3305             // (otherwise it's the same as ctrl+drag anyway)
3307             // calculate angles of the handles
3308             if (xn == 0) {
3309                 if (yn == 0) { // no handle, consider it the continuation of the other one
3310                     an = 0;
3311                     collinear = TRUE;
3312                 }
3313                 else an = 0; // vertical; set the angle to horizontal
3314             } else an = yn/xn;
3316             if (xp == 0) {
3317                 if (yp == 0) { // no handle, consider it the continuation of the other one
3318                     ap = an;
3319                 }
3320                 else ap = 0; // vertical; set the angle to horizontal
3321             } else  ap = yp/xp;
3323             if (collinear) an = ap;
3325             // angles of the perpendiculars; HUGE_VAL means vertical
3326             if (an == 0) na = HUGE_VAL; else na = -1/an;
3327             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3329             // mouse point relative to the node's original pos
3330             pr = (*p) - n->origin;
3332             // distances to the four lines (two handles and two perpendiculars)
3333             d_an = point_line_distance(&pr, an);
3334             d_na = point_line_distance(&pr, na);
3335             d_ap = point_line_distance(&pr, ap);
3336             d_pa = point_line_distance(&pr, pa);
3338             // find out which line is the closest, save its closest point in c
3339             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3340                 point_line_closest(&pr, an, &c);
3341             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3342                 point_line_closest(&pr, ap, &c);
3343             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3344                 point_line_closest(&pr, na, &c);
3345             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3346                 point_line_closest(&pr, pa, &c);
3347             }
3349             // move the node to the closest point
3350             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3351                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3352                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3354         } else {  // constraining to hor/vert
3356             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3357                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3358             } else { // snap to vert
3359                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3360             }
3361         }
3362     } else { // move freely
3363         if (n->is_dragging) {
3364             if (state & GDK_MOD1_MASK) { // sculpt
3365                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3366             } else {
3367                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3368                                             (*p)[NR::X] - n->pos[NR::X],
3369                                             (*p)[NR::Y] - n->pos[NR::Y],
3370                                             (state & GDK_SHIFT_MASK) == 0);
3371             }
3372         }
3373     }
3375     n->subpath->nodepath->desktop->scroll_to_point(p);
3377     return TRUE;
3380 /**
3381  * Node handle clicked callback.
3382  */
3383 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3385    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3387     if (state & GDK_CONTROL_MASK) { // "delete" handle
3388         if (n->p.knot == knot) {
3389             n->p.pos = n->pos;
3390         } else if (n->n.knot == knot) {
3391             n->n.pos = n->pos;
3392         }
3393         sp_node_update_handles(n);
3394         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3395         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3396         sp_nodepath_update_statusbar(nodepath);
3398     } else { // just select or add to selection, depending in Shift
3399         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3400     }
3403 /**
3404  * Node handle grabbed callback.
3405  */
3406 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3408    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3410     if (!n->selected) {
3411         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3412     }
3414     // remember the origin point of the handle
3415     if (n->p.knot == knot) {
3416         n->p.origin_radial = n->p.pos - n->pos;
3417     } else if (n->n.knot == knot) {
3418         n->n.origin_radial = n->n.pos - n->pos;
3419     } else {
3420         g_assert_not_reached();
3421     }
3423     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3426 /**
3427  * Node handle ungrabbed callback.
3428  */
3429 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3431    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3433     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3434     if (n->p.knot == knot) {
3435         n->p.origin_radial.a = 0;
3436         sp_knot_set_position(knot, &n->p.pos, state);
3437     } else if (n->n.knot == knot) {
3438         n->n.origin_radial.a = 0;
3439         sp_knot_set_position(knot, &n->n.pos, state);
3440     } else {
3441         g_assert_not_reached();
3442     }
3444     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3447 /**
3448  * Node handle "request" signal callback.
3449  */
3450 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3452     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3454     Inkscape::NodePath::NodeSide *me, *opposite;
3455     gint which;
3456     if (n->p.knot == knot) {
3457         me = &n->p;
3458         opposite = &n->n;
3459         which = -1;
3460     } else if (n->n.knot == knot) {
3461         me = &n->n;
3462         opposite = &n->p;
3463         which = 1;
3464     } else {
3465         me = opposite = NULL;
3466         which = 0;
3467         g_assert_not_reached();
3468     }
3470     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3472     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3474     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3475         /* We are smooth node adjacent with line */
3476         NR::Point const delta = *p - n->pos;
3477         NR::Coord const len = NR::L2(delta);
3478         Inkscape::NodePath::Node *othernode = opposite->other;
3479         NR::Point const ndelta = n->pos - othernode->pos;
3480         NR::Coord const linelen = NR::L2(ndelta);
3481         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3482             NR::Coord const scal = dot(delta, ndelta) / linelen;
3483             (*p) = n->pos + (scal / linelen) * ndelta;
3484         }
3485         *p = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), n->subpath->nodepath->item).getPoint();
3486     } else {
3487         *p = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, n->subpath->nodepath->item).getPoint();
3488     }
3490     sp_node_adjust_handle(n, -which);
3492     return FALSE;
3495 /**
3496  * Node handle moved callback.
3497  */
3498 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3500    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3502    Inkscape::NodePath::NodeSide *me;
3503    Inkscape::NodePath::NodeSide *other;
3504     if (n->p.knot == knot) {
3505         me = &n->p;
3506         other = &n->n;
3507     } else if (n->n.knot == knot) {
3508         me = &n->n;
3509         other = &n->p;
3510     } else {
3511         me = NULL;
3512         other = NULL;
3513         g_assert_not_reached();
3514     }
3516     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3517     Radial rme(me->pos - n->pos);
3518     Radial rother(other->pos - n->pos);
3519     Radial rnew(*p - n->pos);
3521     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3522         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3523         /* 0 interpreted as "no snapping". */
3525         // The closest PI/snaps angle, starting from zero.
3526         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3527         if (me->origin_radial.a == HUGE_VAL) {
3528             // ortho doesn't exist: original handle was zero length.
3529             rnew.a = a_snapped;
3530         } else {
3531             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3532              * its opposite and perpendiculars). */
3533             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3535             // Snap to the closest.
3536             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3537                        ? a_snapped
3538                        : a_ortho );
3539         }
3540     }
3542     if (state & GDK_MOD1_MASK) {
3543         // lock handle length
3544         rnew.r = me->origin_radial.r;
3545     }
3547     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3548         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3549         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3550         rother.a += rnew.a - rme.a;
3551         other->pos = NR::Point(rother) + n->pos;
3552         if (other->knot) {
3553             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3554             sp_knot_moveto(other->knot, &other->pos);
3555         }
3556     }
3558     me->pos = NR::Point(rnew) + n->pos;
3559     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3561     // move knot, but without emitting the signal:
3562     // we cannot emit a "moved" signal because we're now processing it
3563     sp_knot_moveto(me->knot, &(me->pos));
3565     update_object(n->subpath->nodepath);
3567     /* status text */
3568     SPDesktop *desktop = n->subpath->nodepath->desktop;
3569     if (!desktop) return;
3570     SPEventContext *ec = desktop->event_context;
3571     if (!ec) return;
3572     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3573     if (!mc) return;
3575     double degrees = 180 / M_PI * rnew.a;
3576     if (degrees > 180) degrees -= 360;
3577     if (degrees < -180) degrees += 360;
3578     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3579         degrees = angle_to_compass (degrees);
3581     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3583     mc->setF(Inkscape::NORMAL_MESSAGE,
3584          _("<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);
3586     g_string_free(length, TRUE);
3589 /**
3590  * Node handle event callback.
3591  */
3592 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3594     gboolean ret = FALSE;
3595     switch (event->type) {
3596         case GDK_KEY_PRESS:
3597             switch (get_group0_keyval (&event->key)) {
3598                 case GDK_space:
3599                     if (event->key.state & GDK_BUTTON1_MASK) {
3600                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3601                         stamp_repr(nodepath);
3602                         ret = TRUE;
3603                     }
3604                     break;
3605                 default:
3606                     break;
3607             }
3608             break;
3609         case GDK_ENTER_NOTIFY:
3610             // we use an experimentally determined threshold that seems to work fine
3611             if (NR::L2(n->pos - knot->pos) < 0.75)
3612                 Inkscape::NodePath::Path::active_node = n;
3613             break;
3614         case GDK_LEAVE_NOTIFY:
3615             // we use an experimentally determined threshold that seems to work fine
3616             if (NR::L2(n->pos - knot->pos) < 0.75)
3617                 Inkscape::NodePath::Path::active_node = NULL;
3618             break;
3619         default:
3620             break;
3621     }
3623     return ret;
3626 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3627                                  Radial &rme, Radial &rother, gboolean const both)
3629     rme.a += angle;
3630     if ( both
3631          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3632          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3633     {
3634         rother.a += angle;
3635     }
3638 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3639                                         Radial &rme, Radial &rother, gboolean const both)
3641     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3643     gdouble r;
3644     if ( both
3645          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3646          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3647     {
3648         r = MAX(rme.r, rother.r);
3649     } else {
3650         r = rme.r;
3651     }
3653     gdouble const weird_angle = atan2(norm_angle, r);
3654 /* Bulia says norm_angle is just the visible distance that the
3655  * object's end must travel on the screen.  Left as 'angle' for want of
3656  * a better name.*/
3658     rme.a += weird_angle;
3659     if ( both
3660          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3661          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3662     {
3663         rother.a += weird_angle;
3664     }
3667 /**
3668  * Rotate one node.
3669  */
3670 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3672     Inkscape::NodePath::NodeSide *me, *other;
3673     bool both = false;
3675     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3676     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3678     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3679         me = &(n->p);
3680         other = &(n->n);
3681     } else if (!n->p.other) {
3682         me = &(n->n);
3683         other = &(n->p);
3684     } else {
3685         if (which > 0) { // right handle
3686             if (xn > xp) {
3687                 me = &(n->n);
3688                 other = &(n->p);
3689             } else {
3690                 me = &(n->p);
3691                 other = &(n->n);
3692             }
3693         } else if (which < 0){ // left handle
3694             if (xn <= xp) {
3695                 me = &(n->n);
3696                 other = &(n->p);
3697             } else {
3698                 me = &(n->p);
3699                 other = &(n->n);
3700             }
3701         } else { // both handles
3702             me = &(n->n);
3703             other = &(n->p);
3704             both = true;
3705         }
3706     }
3708     Radial rme(me->pos - n->pos);
3709     Radial rother(other->pos - n->pos);
3711     if (screen) {
3712         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3713     } else {
3714         node_rotate_one_internal (*n, angle, rme, rother, both);
3715     }
3717     me->pos = n->pos + NR::Point(rme);
3719     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3720         other->pos =  n->pos + NR::Point(rother);
3721     }
3723     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3724     // so here we just move all the knots without emitting move signals, for speed
3725     sp_node_update_handles(n, false);
3728 /**
3729  * Rotate selected nodes.
3730  */
3731 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3733     if (!nodepath || !nodepath->selected) return;
3735     if (g_list_length(nodepath->selected) == 1) {
3736        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3737         node_rotate_one (n, angle, which, screen);
3738     } else {
3739        // rotate as an object:
3741         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3742         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3743         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3744             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3745             box.expandTo (n->pos); // contain all selected nodes
3746         }
3748         gdouble rot;
3749         if (screen) {
3750             gdouble const zoom = nodepath->desktop->current_zoom();
3751             gdouble const zmove = angle / zoom;
3752             gdouble const r = NR::L2(box.max() - box.midpoint());
3753             rot = atan2(zmove, r);
3754         } else {
3755             rot = angle;
3756         }
3758         NR::Point rot_center;
3759         if (Inkscape::NodePath::Path::active_node == NULL)
3760             rot_center = box.midpoint();
3761         else
3762             rot_center = Inkscape::NodePath::Path::active_node->pos;
3764         NR::Matrix t =
3765             NR::Matrix (NR::translate(-rot_center)) *
3766             NR::Matrix (NR::rotate(rot)) *
3767             NR::Matrix (NR::translate(rot_center));
3769         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3770             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3771             n->pos *= t;
3772             n->n.pos *= t;
3773             n->p.pos *= t;
3774             sp_node_update_handles(n, false);
3775         }
3776     }
3778     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3781 /**
3782  * Scale one node.
3783  */
3784 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3786     bool both = false;
3787     Inkscape::NodePath::NodeSide *me, *other;
3789     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3790     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3792     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3793         me = &(n->p);
3794         other = &(n->n);
3795         n->code = NR_CURVETO;
3796     } else if (!n->p.other) {
3797         me = &(n->n);
3798         other = &(n->p);
3799         if (n->n.other)
3800             n->n.other->code = NR_CURVETO;
3801     } else {
3802         if (which > 0) { // right handle
3803             if (xn > xp) {
3804                 me = &(n->n);
3805                 other = &(n->p);
3806                 if (n->n.other)
3807                     n->n.other->code = NR_CURVETO;
3808             } else {
3809                 me = &(n->p);
3810                 other = &(n->n);
3811                 n->code = NR_CURVETO;
3812             }
3813         } else if (which < 0){ // left handle
3814             if (xn <= xp) {
3815                 me = &(n->n);
3816                 other = &(n->p);
3817                 if (n->n.other)
3818                     n->n.other->code = NR_CURVETO;
3819             } else {
3820                 me = &(n->p);
3821                 other = &(n->n);
3822                 n->code = NR_CURVETO;
3823             }
3824         } else { // both handles
3825             me = &(n->n);
3826             other = &(n->p);
3827             both = true;
3828             n->code = NR_CURVETO;
3829             if (n->n.other)
3830                 n->n.other->code = NR_CURVETO;
3831         }
3832     }
3834     Radial rme(me->pos - n->pos);
3835     Radial rother(other->pos - n->pos);
3837     rme.r += grow;
3838     if (rme.r < 0) rme.r = 0;
3839     if (rme.a == HUGE_VAL) {
3840         if (me->other) { // if direction is unknown, initialize it towards the next node
3841             Radial rme_next(me->other->pos - n->pos);
3842             rme.a = rme_next.a;
3843         } else { // if there's no next, initialize to 0
3844             rme.a = 0;
3845         }
3846     }
3847     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3848         rother.r += grow;
3849         if (rother.r < 0) rother.r = 0;
3850         if (rother.a == HUGE_VAL) {
3851             rother.a = rme.a + M_PI;
3852         }
3853     }
3855     me->pos = n->pos + NR::Point(rme);
3857     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3858         other->pos = n->pos + NR::Point(rother);
3859     }
3861     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3862     // so here we just move all the knots without emitting move signals, for speed
3863     sp_node_update_handles(n, false);
3866 /**
3867  * Scale selected nodes.
3868  */
3869 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3871     if (!nodepath || !nodepath->selected) return;
3873     if (g_list_length(nodepath->selected) == 1) {
3874         // scale handles of the single selected node
3875         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3876         node_scale_one (n, grow, which);
3877     } else {
3878         // scale nodes as an "object":
3880         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3881         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3882         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3883             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3884             box.expandTo (n->pos); // contain all selected nodes
3885         }
3887         double scale = (box.maxExtent() + grow)/box.maxExtent();
3889         NR::Point scale_center;
3890         if (Inkscape::NodePath::Path::active_node == NULL)
3891             scale_center = box.midpoint();
3892         else
3893             scale_center = Inkscape::NodePath::Path::active_node->pos;
3895         NR::Matrix t =
3896             NR::Matrix (NR::translate(-scale_center)) *
3897             NR::Matrix (NR::scale(scale, scale)) *
3898             NR::Matrix (NR::translate(scale_center));
3900         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3901             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3902             n->pos *= t;
3903             n->n.pos *= t;
3904             n->p.pos *= t;
3905             sp_node_update_handles(n, false);
3906         }
3907     }
3909     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3912 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3914     if (!nodepath) return;
3915     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3918 /**
3919  * Flip selected nodes horizontally/vertically.
3920  */
3921 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
3923     if (!nodepath || !nodepath->selected) return;
3925     if (g_list_length(nodepath->selected) == 1 && !center) {
3926         // flip handles of the single selected node
3927         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3928         double temp = n->p.pos[axis];
3929         n->p.pos[axis] = n->n.pos[axis];
3930         n->n.pos[axis] = temp;
3931         sp_node_update_handles(n, false);
3932     } else {
3933         // scale nodes as an "object":
3935         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3936         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3937         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3938             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3939             box.expandTo (n->pos); // contain all selected nodes
3940         }
3942         if (!center) {
3943             center = box.midpoint();
3944         }
3945         NR::Matrix t =
3946             NR::Matrix (NR::translate(- *center)) *
3947             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3948             NR::Matrix (NR::translate(*center));
3950         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3951             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3952             n->pos *= t;
3953             n->n.pos *= t;
3954             n->p.pos *= t;
3955             sp_node_update_handles(n, false);
3956         }
3957     }
3959     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3962 //-----------------------------------------------
3963 /**
3964  * Return new subpath under given nodepath.
3965  */
3966 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3968     g_assert(nodepath);
3969     g_assert(nodepath->desktop);
3971    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3973     s->nodepath = nodepath;
3974     s->closed = FALSE;
3975     s->nodes = NULL;
3976     s->first = NULL;
3977     s->last = NULL;
3979     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3980     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3981     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3983     return s;
3986 /**
3987  * Destroy nodes in subpath, then subpath itself.
3988  */
3989 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3991     g_assert(subpath);
3992     g_assert(subpath->nodepath);
3993     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3995     while (subpath->nodes) {
3996         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3997     }
3999     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4001     g_free(subpath);
4004 /**
4005  * Link head to tail in subpath.
4006  */
4007 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4009     g_assert(!sp->closed);
4010     g_assert(sp->last != sp->first);
4011     g_assert(sp->first->code == NR_MOVETO);
4013     sp->closed = TRUE;
4015     //Link the head to the tail
4016     sp->first->p.other = sp->last;
4017     sp->last->n.other  = sp->first;
4018     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4019     sp->first          = sp->last;
4021     //Remove the extra end node
4022     sp_nodepath_node_destroy(sp->last->n.other);
4025 /**
4026  * Open closed (loopy) subpath at node.
4027  */
4028 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4030     g_assert(sp->closed);
4031     g_assert(n->subpath == sp);
4032     g_assert(sp->first == sp->last);
4034     /* We create new startpoint, current node will become last one */
4036    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4037                                                 &n->pos, &n->pos, &n->n.pos);
4040     sp->closed        = FALSE;
4042     //Unlink to make a head and tail
4043     sp->first         = new_path;
4044     sp->last          = n;
4045     n->n.other        = NULL;
4046     new_path->p.other = NULL;
4049 /**
4050  * Returns area in triangle given by points; may be negative.
4051  */
4052 inline double
4053 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
4055     return (p1[NR::X]*p2[NR::Y] + p1[NR::Y]*p3[NR::X] + p2[NR::X]*p3[NR::Y] - p2[NR::Y]*p3[NR::X] - p1[NR::Y]*p2[NR::X] - p1[NR::X]*p3[NR::Y]);
4058 /**
4059  * Return new node in subpath with given properties.
4060  * \param pos Position of node.
4061  * \param ppos Handle position in previous direction
4062  * \param npos Handle position in previous direction
4063  */
4064 Inkscape::NodePath::Node *
4065 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)
4067     g_assert(sp);
4068     g_assert(sp->nodepath);
4069     g_assert(sp->nodepath->desktop);
4071     if (nodechunk == NULL)
4072         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4074     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4076     n->subpath  = sp;
4078     if (type != Inkscape::NodePath::NODE_NONE) {
4079         // use the type from sodipodi:nodetypes
4080         n->type = type;
4081     } else {
4082         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4083             // points are (almost) collinear
4084             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4085                 // endnode, or a node with a retracted handle
4086                 n->type = Inkscape::NodePath::NODE_CUSP;
4087             } else {
4088                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4089             }
4090         } else {
4091             n->type = Inkscape::NodePath::NODE_CUSP;
4092         }
4093     }
4095     n->code     = code;
4096     n->selected = FALSE;
4097     n->pos      = *pos;
4098     n->p.pos    = *ppos;
4099     n->n.pos    = *npos;
4101     n->dragging_out = NULL;
4103     Inkscape::NodePath::Node *prev;
4104     if (next) {
4105         //g_assert(g_list_find(sp->nodes, next));
4106         prev = next->p.other;
4107     } else {
4108         prev = sp->last;
4109     }
4111     if (prev)
4112         prev->n.other = n;
4113     else
4114         sp->first = n;
4116     if (next)
4117         next->p.other = n;
4118     else
4119         sp->last = n;
4121     n->p.other = prev;
4122     n->n.other = next;
4124     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"));
4125     sp_knot_set_position(n->knot, pos, 0);
4127     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4128     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4129     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4130     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4131     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4132     sp_knot_update_ctrl(n->knot);
4134     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4135     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4136     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4137     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4138     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4139     sp_knot_show(n->knot);
4141     // We only create handle knots and lines on demand
4142     n->p.knot = NULL;
4143     n->p.line = NULL;
4144     n->n.knot = NULL;
4145     n->n.line = NULL;
4147     sp->nodes = g_list_prepend(sp->nodes, n);
4149     return n;
4152 /**
4153  * Destroy node and its knots, link neighbors in subpath.
4154  */
4155 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4157     g_assert(node);
4158     g_assert(node->subpath);
4159     g_assert(SP_IS_KNOT(node->knot));
4161    Inkscape::NodePath::SubPath *sp = node->subpath;
4163     if (node->selected) { // first, deselect
4164         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4165         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4166     }
4168     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4170     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4171     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4172     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4173     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4174     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4175     g_object_unref(G_OBJECT(node->knot));
4177     if (node->p.knot) {
4178         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4179         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4180         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4181         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4182         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4183         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4184         g_object_unref(G_OBJECT(node->p.knot));
4185         node->p.knot = NULL;
4186     }
4188     if (node->n.knot) {
4189         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4190         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4191         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4192         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4193         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4194         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4195         g_object_unref(G_OBJECT(node->n.knot));
4196         node->n.knot = NULL;
4197     }
4199     if (node->p.line)
4200         gtk_object_destroy(GTK_OBJECT(node->p.line));
4201     if (node->n.line)
4202         gtk_object_destroy(GTK_OBJECT(node->n.line));
4204     if (sp->nodes) { // there are others nodes on the subpath
4205         if (sp->closed) {
4206             if (sp->first == node) {
4207                 g_assert(sp->last == node);
4208                 sp->first = node->n.other;
4209                 sp->last = sp->first;
4210             }
4211             node->p.other->n.other = node->n.other;
4212             node->n.other->p.other = node->p.other;
4213         } else {
4214             if (sp->first == node) {
4215                 sp->first = node->n.other;
4216                 sp->first->code = NR_MOVETO;
4217             }
4218             if (sp->last == node) sp->last = node->p.other;
4219             if (node->p.other) node->p.other->n.other = node->n.other;
4220             if (node->n.other) node->n.other->p.other = node->p.other;
4221         }
4222     } else { // this was the last node on subpath
4223         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4224     }
4226     g_mem_chunk_free(nodechunk, node);
4229 /**
4230  * Returns one of the node's two sides.
4231  * \param which Indicates which side.
4232  * \return Pointer to previous node side if which==-1, next if which==1.
4233  */
4234 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4236     g_assert(node);
4238     switch (which) {
4239         case -1:
4240             return &node->p;
4241         case 1:
4242             return &node->n;
4243         default:
4244             break;
4245     }
4247     g_assert_not_reached();
4249     return NULL;
4252 /**
4253  * Return the other side of the node, given one of its sides.
4254  */
4255 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4257     g_assert(node);
4259     if (me == &node->p) return &node->n;
4260     if (me == &node->n) return &node->p;
4262     g_assert_not_reached();
4264     return NULL;
4267 /**
4268  * Return NRPathcode on the given side of the node.
4269  */
4270 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4272     g_assert(node);
4274     if (me == &node->p) {
4275         if (node->p.other) return (NRPathcode)node->code;
4276         return NR_MOVETO;
4277     }
4279     if (me == &node->n) {
4280         if (node->n.other) return (NRPathcode)node->n.other->code;
4281         return NR_MOVETO;
4282     }
4284     g_assert_not_reached();
4286     return NR_END;
4289 /**
4290  * Return node with the given index
4291  */
4292 Inkscape::NodePath::Node *
4293 sp_nodepath_get_node_by_index(int index)
4295     Inkscape::NodePath::Node *e = NULL;
4297     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4298     if (!nodepath) {
4299         return e;
4300     }
4302     //find segment
4303     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4305         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4306         int n = g_list_length(sp->nodes);
4307         if (sp->closed) {
4308             n++;
4309         }
4311         //if the piece belongs to this subpath grab it
4312         //otherwise move onto the next subpath
4313         if (index < n) {
4314             e = sp->first;
4315             for (int i = 0; i < index; ++i) {
4316                 e = e->n.other;
4317             }
4318             break;
4319         } else {
4320             if (sp->closed) {
4321                 index -= (n+1);
4322             } else {
4323                 index -= n;
4324             }
4325         }
4326     }
4328     return e;
4331 /**
4332  * Returns plain text meaning of node type.
4333  */
4334 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4336     unsigned retracted = 0;
4337     bool endnode = false;
4339     for (int which = -1; which <= 1; which += 2) {
4340         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4341         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4342             retracted ++;
4343         if (!side->other)
4344             endnode = true;
4345     }
4347     if (retracted == 0) {
4348         if (endnode) {
4349                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4350                 return _("end node");
4351         } else {
4352             switch (node->type) {
4353                 case Inkscape::NodePath::NODE_CUSP:
4354                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4355                     return _("cusp");
4356                 case Inkscape::NodePath::NODE_SMOOTH:
4357                     // TRANSLATORS: "smooth" is an adjective here
4358                     return _("smooth");
4359                 case Inkscape::NodePath::NODE_SYMM:
4360                     return _("symmetric");
4361             }
4362         }
4363     } else if (retracted == 1) {
4364         if (endnode) {
4365             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4366             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4367         } else {
4368             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4369         }
4370     } else {
4371         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4372     }
4374     return NULL;
4377 /**
4378  * Handles content of statusbar as long as node tool is active.
4379  */
4380 void
4381 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4383     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");
4384     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4386     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4387     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4388     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4389     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4391     SPDesktop *desktop = NULL;
4392     if (nodepath) {
4393         desktop = nodepath->desktop;
4394     } else {
4395         desktop = SP_ACTIVE_DESKTOP;
4396     }
4398     SPEventContext *ec = desktop->event_context;
4399     if (!ec) return;
4400     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4401     if (!mc) return;
4403     if (selected_nodes == 0) {
4404         Inkscape::Selection *sel = desktop->selection;
4405         if (!sel || sel->isEmpty()) {
4406             mc->setF(Inkscape::NORMAL_MESSAGE,
4407                      _("Select a single object to edit its nodes or handles."));
4408         } else {
4409             if (nodepath) {
4410             mc->setF(Inkscape::NORMAL_MESSAGE,
4411                      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.",
4412                               "<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.",
4413                               total_nodes),
4414                      total_nodes);
4415             } else {
4416                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4417                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4418                 } else {
4419                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4420                 }
4421             }
4422         }
4423     } else if (nodepath && selected_nodes == 1) {
4424         mc->setF(Inkscape::NORMAL_MESSAGE,
4425                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4426                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4427                           total_nodes),
4428                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4429     } else {
4430         if (selected_subpaths > 1) {
4431             mc->setF(Inkscape::NORMAL_MESSAGE,
4432                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4433                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4434                               total_nodes),
4435                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4436         } else {
4437             mc->setF(Inkscape::NORMAL_MESSAGE,
4438                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4439                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4440                               total_nodes),
4441                      selected_nodes, total_nodes, when_selected);
4442         }
4443     }
4446 /*
4447  * returns a *copy* of the curve of that object.
4448  */
4449 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4450     if (!object)
4451         return NULL;
4453     SPCurve *curve = NULL;
4454     if (SP_IS_PATH(object)) {
4455         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4456         curve = sp_curve_copy(curve_new);
4457     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4458         const gchar *svgd = object->repr->attribute(key);
4459         if (svgd) {
4460             NArtBpath *bpath = sp_svg_read_path(svgd);
4461             SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
4462             if (curve_new) {
4463                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4464             } else {
4465                 g_free(bpath);
4466             }
4467         }
4468     }
4470     return curve;
4473 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4474     if (!np || !np->object || !curve)
4475         return;
4477     if (SP_IS_PATH(np->object)) {
4478         if (SP_SHAPE(np->object)->path_effect_href) {
4479             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4480         } else {
4481             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4482         }
4483     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4484         // FIXME: this writing to string and then reading from string is bound to be slow.
4485         // create a method to convert from curve directly to 2geom...
4486         gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
4487         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4488         g_free(svgpath);
4490         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4491     }
4494 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4495     np->show_helperpath = show;
4498 /* this function does not work yet */
4499 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4500     np->straight_path = true;
4501     np->show_handles = false;
4502     g_message("add code to make the path straight.");
4503     // do sp_nodepath_convert_node_type on all nodes?
4504     // search for this text !!!   "Make selected segments lines"
4508 /*
4509   Local Variables:
4510   mode:c++
4511   c-file-style:"stroustrup"
4512   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4513   indent-tabs-mode:nil
4514   fill-column:99
4515   End:
4516 */
4517 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :