Code

fix for gcc 3.3 by John Faith. https://bugs.launchpad.net/inkscape/+bug/178180
[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"
52 #include "util/mathfns.h"
54 class NR::Matrix;
56 /// \todo
57 /// evil evil evil. FIXME: conflict of two different Path classes!
58 /// There is a conflict in the namespace between two classes named Path.
59 /// #include "sp-flowtext.h"
60 /// #include "sp-flowregion.h"
62 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
63 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
64 GType sp_flowregion_get_type (void);
65 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
66 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
67 GType sp_flowtext_get_type (void);
68 // end evil workaround
70 #include "helper/stlport.h"
73 /// \todo fixme: Implement these via preferences */
75 #define NODE_FILL          0xbfbfbf00
76 #define NODE_STROKE        0x000000ff
77 #define NODE_FILL_HI       0xff000000
78 #define NODE_STROKE_HI     0x000000ff
79 #define NODE_FILL_SEL      0x0000ffff
80 #define NODE_STROKE_SEL    0x000000ff
81 #define NODE_FILL_SEL_HI   0xff000000
82 #define NODE_STROKE_SEL_HI 0x000000ff
83 #define KNOT_FILL          0xffffffff
84 #define KNOT_STROKE        0x000000ff
85 #define KNOT_FILL_HI       0xff000000
86 #define KNOT_STROKE_HI     0x000000ff
88 static GMemChunk *nodechunk = NULL;
90 /* Creation from object */
92 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
93 static gchar *parse_nodetypes(gchar const *types, gint length);
95 /* Object updating */
97 static void stamp_repr(Inkscape::NodePath::Path *np);
98 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
99 static gchar *create_typestr(Inkscape::NodePath::Path *np);
101 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
103 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
105 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
107 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
109 /* Adjust handle placement, if the node or the other handle is moved */
110 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
111 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
113 /* Node event callbacks */
114 static void node_clicked(SPKnot *knot, guint state, gpointer data);
115 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
116 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
117 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
119 /* Handle event callbacks */
120 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
121 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
122 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
123 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
124 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
125 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
127 /* Constructors and destructors */
129 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
130 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
131 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
132 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
133 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
134                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
135 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
137 /* Helpers */
139 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
140 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
141 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
143 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
144 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
146 // active_node indicates mouseover node
147 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
149 /**
150  * \brief Creates new nodepath from item
151  */
152 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
154     Inkscape::XML::Node *repr = object->repr;
156     /** \todo
157      * FIXME: remove this. We don't want to edit paths inside flowtext.
158      * Instead we will build our flowtext with cloned paths, so that the
159      * real paths are outside the flowtext and thus editable as usual.
160      */
161     if (SP_IS_FLOWTEXT(object)) {
162         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
163             if SP_IS_FLOWREGION(child) {
164                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
165                 if (grandchild && SP_IS_PATH(grandchild)) {
166                     object = SP_ITEM(grandchild);
167                     break;
168                 }
169             }
170         }
171     }
173     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
175     if (curve == NULL)
176         return NULL;
178     NArtBpath *bpath = sp_curve_first_bpath(curve);
179     gint length = curve->end;
180     if (length == 0) {
181         sp_curve_unref(curve);
182         return NULL; // prevent crash for one-node paths
183     }
185     //Create new nodepath
186     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
187     if (!np) {
188         sp_curve_unref(curve);
189         return NULL;
190     }
192     // Set defaults
193     np->desktop     = desktop;
194     np->object      = object;
195     np->subpaths    = NULL;
196     np->selected    = NULL;
197     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
198     np->livarot_path = NULL;
199     np->local_change = 0;
200     np->show_handles = show_handles;
201     np->helper_path = NULL;
202     np->curve = sp_curve_copy(curve);
203     np->show_helperpath = false;
204     np->straight_path = false;
205     if (IS_LIVEPATHEFFECT(object) && item) {
206         np->item = item;
207     } else {
208         np->item = SP_ITEM(object);
209     }
211     // we need to update item's transform from the repr here,
212     // because they may be out of sync when we respond
213     // to a change in repr by regenerating nodepath     --bb
214     sp_object_read_attr(SP_OBJECT(np->item), "transform");
216     np->i2d  = sp_item_i2d_affine(np->item);
217     np->d2i  = np->i2d.inverse();
219     np->repr = repr;
220     if (repr_key_in) { // apparantly the object is an LPEObject
221         np->repr_key = g_strdup(repr_key_in);
222         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
223         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
224         if (lpeparam) {
225             lpeparam->param_setup_nodepath(np);
226         }
227     } else {
228         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
229         if ( SP_SHAPE(np->object)->path_effect_href ) {
230             np->repr_key = g_strdup("inkscape:original-d");
232             LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(SP_SHAPE(np->object));
233             if (lpeobj && lpeobj->lpe) {
234                 lpeobj->lpe->setup_nodepath(np);
235             }
236         } else {
237             np->repr_key = g_strdup("d");
238         }
239     }
241     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
242     gchar *typestr = parse_nodetypes(nodetypes, length);
244     // create the subpath(s) from the bpath
245     NArtBpath *b = bpath;
246     while (b->code != NR_END) {
247         b = subpath_from_bpath(np, b, typestr + (b - bpath));
248     }
250     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
251     np->subpaths = g_list_reverse(np->subpaths);
253     g_free(typestr);
254     sp_curve_unref(curve);
256     // create the livarot representation from the same item
257     sp_nodepath_ensure_livarot_path(np);
259     // Draw helper curve
260     if (np->show_helperpath) {
261         SPCurve *helper_curve = sp_curve_copy(np->curve);
262         sp_curve_transform(helper_curve, np->i2d );
263         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
264         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);
265         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
266         sp_canvas_item_show(np->helper_path);
267         sp_curve_unref(helper_curve);
268     }
270     return np;
273 /**
274  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
275  */
276 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
278     if (!np)  //soft fail, like delete
279         return;
281     while (np->subpaths) {
282         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
283     }
285     //Inform the ShapeEditor that made me, if any, that I am gone.
286     if (np->shape_editor)
287         np->shape_editor->nodepath_destroyed();
289     g_assert(!np->selected);
291     if (np->livarot_path) {
292         delete np->livarot_path;
293         np->livarot_path = NULL;
294     }
296     if (np->helper_path) {
297         GtkObject *temp = np->helper_path;
298         np->helper_path = NULL;
299         gtk_object_destroy(temp);
300     }
301     if (np->curve) {
302         sp_curve_unref(np->curve);
303         np->curve = NULL;
304     }
306     if (np->repr_key) {
307         g_free(np->repr_key);
308         np->repr_key = NULL;
309     }
310     if (np->repr_nodetypes_key) {
311         g_free(np->repr_nodetypes_key);
312         np->repr_nodetypes_key = NULL;
313     }
315     np->desktop = NULL;
317     g_free(np);
321 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
323     if (np && np->livarot_path == NULL) {
324         SPCurve *curve = create_curve(np);
325         NArtBpath *bpath = SP_CURVE_BPATH(curve);
326         np->livarot_path = bpath_to_Path(bpath);
328         if (np->livarot_path)
329             np->livarot_path->ConvertWithBackData(0.01);
331         sp_curve_unref(curve);
332     }
336 /**
337  *  Return the node count of a given NodeSubPath.
338  */
339 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
341     if (!subpath)
342         return 0;
343     gint nodeCount = g_list_length(subpath->nodes);
344     return nodeCount;
347 /**
348  *  Return the node count of a given NodePath.
349  */
350 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
352     if (!np)
353         return 0;
354     gint nodeCount = 0;
355     for (GList *item = np->subpaths ; item ; item=item->next) {
356        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
357         nodeCount += g_list_length(subpath->nodes);
358     }
359     return nodeCount;
362 /**
363  *  Return the subpath count of a given NodePath.
364  */
365 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
367     if (!np)
368         return 0;
369     return g_list_length (np->subpaths);
372 /**
373  *  Return the selected node count of a given NodePath.
374  */
375 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
377     if (!np)
378         return 0;
379     return g_list_length (np->selected);
382 /**
383  *  Return the number of subpaths where nodes are selected in a given NodePath.
384  */
385 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
387     if (!np)
388         return 0;
389     if (!np->selected)
390         return 0;
391     if (!np->selected->next)
392         return 1;
393     gint count = 0;
394     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
395         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
396         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
397             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
398             if (node->selected) {
399                 count ++;
400                 break;
401             }
402         }
403     }
404     return count;
407 /**
408  * Clean up a nodepath after editing.
409  *
410  * Currently we are deleting trivial subpaths.
411  */
412 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
414     GList *badSubPaths = NULL;
416     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
417     for (GList *l = nodepath->subpaths; l ; l=l->next) {
418        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
419        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
420             badSubPaths = g_list_append(badSubPaths, sp);
421     }
423     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
424     //also removes the subpath from nodepath->subpaths
425     for (GList *l = badSubPaths; l ; l=l->next) {
426        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
427         sp_nodepath_subpath_destroy(sp);
428     }
430     g_list_free(badSubPaths);
433 /**
434  * Create new nodepath from b, make it subpath of np.
435  * \param t The node type.
436  * \todo Fixme: t should be a proper type, rather than gchar
437  */
438 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
440     NR::Point ppos, pos, npos;
442     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
444     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
445     bool const closed = (b->code == NR_MOVETO);
447     pos = NR::Point(b->x3, b->y3) * np->i2d;
448     if (b[1].code == NR_CURVETO) {
449         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
450     } else {
451         npos = pos;
452     }
453     Inkscape::NodePath::Node *n;
454     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
455     g_assert(sp->first == n);
456     g_assert(sp->last  == n);
458     b++;
459     t++;
460     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
461         pos = NR::Point(b->x3, b->y3) * np->i2d;
462         if (b->code == NR_CURVETO) {
463             ppos = NR::Point(b->x2, b->y2) * np->i2d;
464         } else {
465             ppos = pos;
466         }
467         if (b[1].code == NR_CURVETO) {
468             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
469         } else {
470             npos = pos;
471         }
472         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
473         b++;
474         t++;
475     }
477     if (closed) sp_nodepath_subpath_close(sp);
479     return b;
482 /**
483  * Convert from sodipodi:nodetypes to new style type string.
484  */
485 static gchar *parse_nodetypes(gchar const *types, gint length)
487     g_assert(length > 0);
489     gchar *typestr = g_new(gchar, length + 1);
491     gint pos = 0;
493     if (types) {
494         for (gint i = 0; types[i] && ( i < length ); i++) {
495             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
496             if (types[i] != '\0') {
497                 switch (types[i]) {
498                     case 's':
499                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
500                         break;
501                     case 'z':
502                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
503                         break;
504                     case 'c':
505                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
506                         break;
507                     default:
508                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
509                         break;
510                 }
511             }
512         }
513     }
515     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
517     return typestr;
520 /**
521  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
522  * updated but repr is not (for speed). Used during curve and node drag.
523  */
524 static void update_object(Inkscape::NodePath::Path *np)
526     g_assert(np);
528     sp_curve_unref(np->curve);
529     np->curve = create_curve(np);
531     sp_nodepath_set_curve(np, np->curve);
533     if (np->show_helperpath) {
534         SPCurve * helper_curve = sp_curve_copy(np->curve);
535         sp_curve_transform(helper_curve, np->i2d );
536         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
537         sp_curve_unref(helper_curve);
538     }
541 /**
542  * Update XML path node with data from path object.
543  */
544 static void update_repr_internal(Inkscape::NodePath::Path *np)
546     g_assert(np);
548     Inkscape::XML::Node *repr = np->object->repr;
550     sp_curve_unref(np->curve);
551     np->curve = create_curve(np);
553     gchar *typestr = create_typestr(np);
554     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
556     // determine if path has an effect applied and write to correct "d" attribute.
557     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
558         np->local_change++;
559         repr->setAttribute(np->repr_key, svgpath);
560     }
562     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
563         np->local_change++;
564         repr->setAttribute(np->repr_nodetypes_key, typestr);
565     }
567     g_free(svgpath);
568     g_free(typestr);
570     if (np->show_helperpath) {
571         SPCurve * helper_curve = sp_curve_copy(np->curve);
572         sp_curve_transform(helper_curve, np->i2d );
573         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
574         sp_curve_unref(helper_curve);
575     }
576  }
578 /**
579  * Update XML path node with data from path object, commit changes forever.
580  */
581 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
583     //fixme: np can be NULL, so check before proceeding
584     g_return_if_fail(np != NULL);
586     if (np->livarot_path) {
587         delete np->livarot_path;
588         np->livarot_path = NULL;
589     }
591     update_repr_internal(np);
592     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
594     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
595                      annotation);
598 /**
599  * Update XML path node with data from path object, commit changes with undo.
600  */
601 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
603     if (np->livarot_path) {
604         delete np->livarot_path;
605         np->livarot_path = NULL;
606     }
608     update_repr_internal(np);
609     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
610                            annotation);
613 /**
614  * Make duplicate of path, replace corresponding XML node in tree, commit.
615  */
616 static void stamp_repr(Inkscape::NodePath::Path *np)
618     g_assert(np);
620     Inkscape::XML::Node *old_repr = np->object->repr;
621     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
623     // remember the position of the item
624     gint pos = old_repr->position();
625     // remember parent
626     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
628     SPCurve *curve = create_curve(np);
629     gchar *typestr = create_typestr(np);
631     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
633     new_repr->setAttribute(np->repr_key, svgpath);
634     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
636     // add the new repr to the parent
637     parent->appendChild(new_repr);
638     // move to the saved position
639     new_repr->setPosition(pos > 0 ? pos : 0);
641     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
642                      _("Stamp"));
644     Inkscape::GC::release(new_repr);
645     g_free(svgpath);
646     g_free(typestr);
647     sp_curve_unref(curve);
650 /**
651  * Create curve from path.
652  */
653 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
655     SPCurve *curve = sp_curve_new();
657     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
658        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
659         sp_curve_moveto(curve,
660                         sp->first->pos * np->d2i);
661        Inkscape::NodePath::Node *n = sp->first->n.other;
662         while (n) {
663             NR::Point const end_pt = n->pos * np->d2i;
664             switch (n->code) {
665                 case NR_LINETO:
666                     sp_curve_lineto(curve, end_pt);
667                     break;
668                 case NR_CURVETO:
669                     sp_curve_curveto(curve,
670                                      n->p.other->n.pos * np->d2i,
671                                      n->p.pos * np->d2i,
672                                      end_pt);
673                     break;
674                 default:
675                     g_assert_not_reached();
676                     break;
677             }
678             if (n != sp->last) {
679                 n = n->n.other;
680             } else {
681                 n = NULL;
682             }
683         }
684         if (sp->closed) {
685             sp_curve_closepath(curve);
686         }
687     }
689     return curve;
692 /**
693  * Convert path type string to sodipodi:nodetypes style.
694  */
695 static gchar *create_typestr(Inkscape::NodePath::Path *np)
697     gchar *typestr = g_new(gchar, 32);
698     gint len = 32;
699     gint pos = 0;
701     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
702        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
704         if (pos >= len) {
705             typestr = g_renew(gchar, typestr, len + 32);
706             len += 32;
707         }
709         typestr[pos++] = 'c';
711        Inkscape::NodePath::Node *n;
712         n = sp->first->n.other;
713         while (n) {
714             gchar code;
716             switch (n->type) {
717                 case Inkscape::NodePath::NODE_CUSP:
718                     code = 'c';
719                     break;
720                 case Inkscape::NodePath::NODE_SMOOTH:
721                     code = 's';
722                     break;
723                 case Inkscape::NodePath::NODE_SYMM:
724                     code = 'z';
725                     break;
726                 default:
727                     g_assert_not_reached();
728                     code = '\0';
729                     break;
730             }
732             if (pos >= len) {
733                 typestr = g_renew(gchar, typestr, len + 32);
734                 len += 32;
735             }
737             typestr[pos++] = code;
739             if (n != sp->last) {
740                 n = n->n.other;
741             } else {
742                 n = NULL;
743             }
744         }
745     }
747     if (pos >= len) {
748         typestr = g_renew(gchar, typestr, len + 1);
749         len += 1;
750     }
752     typestr[pos++] = '\0';
754     return typestr;
757 /**
758  * Returns current path in context. // later eliminate this function at all!
759  */
760 static Inkscape::NodePath::Path *sp_nodepath_current()
762     if (!SP_ACTIVE_DESKTOP) {
763         return NULL;
764     }
766     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
768     if (!SP_IS_NODE_CONTEXT(event_context)) {
769         return NULL;
770     }
772     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
777 /**
778  \brief Fills node and handle positions for three nodes, splitting line
779   marked by end at distance t.
780  */
781 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
783     g_assert(new_path != NULL);
784     g_assert(end      != NULL);
786     g_assert(end->p.other == new_path);
787    Inkscape::NodePath::Node *start = new_path->p.other;
788     g_assert(start);
790     if (end->code == NR_LINETO) {
791         new_path->type =Inkscape::NodePath::NODE_CUSP;
792         new_path->code = NR_LINETO;
793         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
794     } else {
795         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
796         new_path->code = NR_CURVETO;
797         gdouble s      = 1 - t;
798         for (int dim = 0; dim < 2; dim++) {
799             NR::Coord const f000 = start->pos[dim];
800             NR::Coord const f001 = start->n.pos[dim];
801             NR::Coord const f011 = end->p.pos[dim];
802             NR::Coord const f111 = end->pos[dim];
803             NR::Coord const f00t = s * f000 + t * f001;
804             NR::Coord const f01t = s * f001 + t * f011;
805             NR::Coord const f11t = s * f011 + t * f111;
806             NR::Coord const f0tt = s * f00t + t * f01t;
807             NR::Coord const f1tt = s * f01t + t * f11t;
808             NR::Coord const fttt = s * f0tt + t * f1tt;
809             start->n.pos[dim]    = f00t;
810             new_path->p.pos[dim] = f0tt;
811             new_path->pos[dim]   = fttt;
812             new_path->n.pos[dim] = f1tt;
813             end->p.pos[dim]      = f11t;
814         }
815     }
818 /**
819  * Adds new node on direct line between two nodes, activates handles of all
820  * three nodes.
821  */
822 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
824     g_assert(end);
825     g_assert(end->subpath);
826     g_assert(g_list_find(end->subpath->nodes, end));
828    Inkscape::NodePath::Node *start = end->p.other;
829     g_assert( start->n.other == end );
830    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
831                                                end,
832                                                (NRPathcode)end->code == NR_LINETO?
833                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
834                                                (NRPathcode)end->code,
835                                                &start->pos, &start->pos, &start->n.pos);
836     sp_nodepath_line_midpoint(newnode, end, t);
838     sp_node_adjust_handles(start);
839     sp_node_update_handles(start);
840     sp_node_update_handles(newnode);
841     sp_node_adjust_handles(end);
842     sp_node_update_handles(end);
844     return newnode;
847 /**
848 \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
849 */
850 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
852     g_assert(node);
853     g_assert(node->subpath);
854     g_assert(g_list_find(node->subpath->nodes, node));
856    Inkscape::NodePath::SubPath *sp = node->subpath;
857     Inkscape::NodePath::Path *np    = sp->nodepath;
859     if (sp->closed) {
860         sp_nodepath_subpath_open(sp, node);
861         return sp->first;
862     } else {
863         // no break for end nodes
864         if (node == sp->first) return NULL;
865         if (node == sp->last ) return NULL;
867         // create a new subpath
868        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
870         // duplicate the break node as start of the new subpath
871        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
873         while (node->n.other) { // copy the remaining nodes into the new subpath
874            Inkscape::NodePath::Node *n  = node->n.other;
875            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);
876             if (n->selected) {
877                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
878             }
879             sp_nodepath_node_destroy(n); // remove the point on the original subpath
880         }
882         return newnode;
883     }
886 /**
887  * Duplicate node and connect to neighbours.
888  */
889 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
891     g_assert(node);
892     g_assert(node->subpath);
893     g_assert(g_list_find(node->subpath->nodes, node));
895    Inkscape::NodePath::SubPath *sp = node->subpath;
897     NRPathcode code = (NRPathcode) node->code;
898     if (code == NR_MOVETO) { // if node is the endnode,
899         node->code = NR_LINETO; // new one is inserted before it, so change that to line
900     }
902     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
904     if (!node->n.other || !node->p.other) // if node is an endnode, select it
905         return node;
906     else
907         return newnode; // otherwise select the newly created node
910 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
912     node->p.pos = (node->pos + (node->pos - node->n.pos));
915 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
917     node->n.pos = (node->pos + (node->pos - node->p.pos));
920 /**
921  * Change line type at node, with side effects on neighbours.
922  */
923 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
925     g_assert(end);
926     g_assert(end->subpath);
927     g_assert(end->p.other);
929     if (end->code == static_cast< guint > ( code ) )
930         return;
932    Inkscape::NodePath::Node *start = end->p.other;
934     end->code = code;
936     if (code == NR_LINETO) {
937         if (start->code == NR_LINETO) {
938             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
939         }
940         if (end->n.other) {
941             if (end->n.other->code == NR_LINETO) {
942                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
943             }
944         }
945     } else {
946         NR::Point delta = end->pos - start->pos;
947         start->n.pos = start->pos + delta / 3;
948         end->p.pos = end->pos - delta / 3;
949         sp_node_adjust_handle(start, 1);
950         sp_node_adjust_handle(end, -1);
951     }
953     sp_node_update_handles(start);
954     sp_node_update_handles(end);
957 /**
958  * Change node type, and its handles accordingly.
959  */
960 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
962     g_assert(node);
963     g_assert(node->subpath);
965     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
966         return node;
968     if ((node->p.other != NULL) && (node->n.other != NULL)) {
969         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
970             type =Inkscape::NodePath::NODE_CUSP;
971         }
972     }
974     node->type = type;
976     if (node->type == Inkscape::NodePath::NODE_CUSP) {
977         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
978         node->knot->setSize (node->selected? 11 : 9);
979         sp_knot_update_ctrl(node->knot);
980     } else {
981         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
982         node->knot->setSize (node->selected? 9 : 7);
983         sp_knot_update_ctrl(node->knot);
984     }
986     // if one of handles is mouseovered, preserve its position
987     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
988         sp_node_adjust_handle(node, 1);
989     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
990         sp_node_adjust_handle(node, -1);
991     } else {
992         sp_node_adjust_handles(node);
993     }
995     sp_node_update_handles(node);
997     sp_nodepath_update_statusbar(node->subpath->nodepath);
999     return node;
1002 /**
1003  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
1004  * adjacent segments from lines to curves.
1005 */
1006 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1008     bool p_line = (node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos);
1009     bool n_line = (node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos);
1011     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1012         if (p_line && n_line) {
1013             // only if both adjacent segments are lines,
1014             // convert both to curves:
1016             node->code = NR_CURVETO;
1017             node->n.other->code = NR_CURVETO;
1019             NR::Point leg_prev = node->pos - node->p.other->pos;
1020             NR::Point leg_next = node->pos - node->n.other->pos;
1022             double norm_leg_prev = L2(leg_prev);
1023             double norm_leg_next = L2(leg_next);
1025             // delta has length 1 and is orthogonal to bisecting line
1026             NR::Point delta;
1027             if (norm_leg_next > 0.0) {
1028                 delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1029                 (&delta)->normalize();
1030             }
1032             if (type == Inkscape::NodePath::NODE_SYMM) {
1033                 double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1034                 node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1035                 node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1036             } else {
1037                 // length of handle is proportional to distance to adjacent node
1038                 node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1039                 node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1040             }
1042             sp_node_update_handles(node);
1043         }
1044     }
1046     sp_nodepath_set_node_type (node, type);
1049 /**
1050  * Move node to point, and adjust its and neighbouring handles.
1051  */
1052 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1054     NR::Point delta = p - node->pos;
1055     node->pos = p;
1057     node->p.pos += delta;
1058     node->n.pos += delta;
1060     Inkscape::NodePath::Node *node_p = NULL;
1061     Inkscape::NodePath::Node *node_n = NULL;
1063     if (node->p.other) {
1064         if (node->code == NR_LINETO) {
1065             sp_node_adjust_handle(node, 1);
1066             sp_node_adjust_handle(node->p.other, -1);
1067             node_p = node->p.other;
1068         }
1069     }
1070     if (node->n.other) {
1071         if (node->n.other->code == NR_LINETO) {
1072             sp_node_adjust_handle(node, -1);
1073             sp_node_adjust_handle(node->n.other, 1);
1074             node_n = node->n.other;
1075         }
1076     }
1078     // this function is only called from batch movers that will update display at the end
1079     // themselves, so here we just move all the knots without emitting move signals, for speed
1080     sp_node_update_handles(node, false);
1081     if (node_n) {
1082         sp_node_update_handles(node_n, false);
1083     }
1084     if (node_p) {
1085         sp_node_update_handles(node_p, false);
1086     }
1089 /**
1090  * Call sp_node_moveto() for node selection and handle possible snapping.
1091  */
1092 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1093                                             bool const snap = true)
1095     NR::Coord best = NR_HUGE;
1096     NR::Point delta(dx, dy);
1097     NR::Point best_pt = delta;
1099     if (snap) {
1100         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
1102         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1103             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1104             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, SP_PATH(n->subpath->nodepath->item));
1105             if (s.getDistance() < best) {
1106                 best = s.getDistance();
1107                 best_pt = s.getPoint() - n->pos;
1108             }
1109         }
1110     }
1112     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1113         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1114         sp_node_moveto(n, n->pos + best_pt);
1115     }
1117     // do not update repr here so that node dragging is acceptably fast
1118     update_object(nodepath);
1121 /**
1122 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1123 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1124 near x = 0.
1125  */
1126 double
1127 sculpt_profile (double x, double alpha, guint profile)
1129     if (x >= 1)
1130         return 0;
1131     if (x <= 0)
1132         return 1;
1134     switch (profile) {
1135         case SCULPT_PROFILE_LINEAR:
1136         return 1 - x;
1137         case SCULPT_PROFILE_BELL:
1138         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1139         case SCULPT_PROFILE_ELLIPTIC:
1140         return sqrt(1 - x*x);
1141     }
1143     return 1;
1146 double
1147 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1149     // extremely primitive for now, don't have time to look for the real one
1150     double lower = NR::L2(b - a);
1151     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1152     return (lower + upper)/2;
1155 void
1156 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1158     n->pos = n->origin + delta;
1159     n->n.pos = n->n.origin + delta_n;
1160     n->p.pos = n->p.origin + delta_p;
1161     sp_node_adjust_handles(n);
1162     sp_node_update_handles(n, false);
1165 /**
1166  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1167  * on how far they are from the dragged node n.
1168  */
1169 static void
1170 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1172     g_assert (n);
1173     g_assert (nodepath);
1174     g_assert (n->subpath->nodepath == nodepath);
1176     double pressure = n->knot->pressure;
1177     if (pressure == 0)
1178         pressure = 0.5; // default
1179     pressure = CLAMP (pressure, 0.2, 0.8);
1181     // map pressure to alpha = 1/5 ... 5
1182     double alpha = 1 - 2 * fabs(pressure - 0.5);
1183     if (pressure > 0.5)
1184         alpha = 1/alpha;
1186     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1188     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1189         // Only one subpath has selected nodes:
1190         // use linear mode, where the distance from n to node being dragged is calculated along the path
1192         double n_sel_range = 0, p_sel_range = 0;
1193         guint n_nodes = 0, p_nodes = 0;
1194         guint n_sel_nodes = 0, p_sel_nodes = 0;
1196         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1197         {
1198             double n_range = 0, p_range = 0;
1199             bool n_going = true, p_going = true;
1200             Inkscape::NodePath::Node *n_node = n;
1201             Inkscape::NodePath::Node *p_node = n;
1202             do {
1203                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1204                 if (n_node && n_going)
1205                     n_node = n_node->n.other;
1206                 if (n_node == NULL) {
1207                     n_going = false;
1208                 } else {
1209                     n_nodes ++;
1210                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1211                     if (n_node->selected) {
1212                         n_sel_nodes ++;
1213                         n_sel_range = n_range;
1214                     }
1215                     if (n_node == p_node) {
1216                         n_going = false;
1217                         p_going = false;
1218                     }
1219                 }
1220                 if (p_node && p_going)
1221                     p_node = p_node->p.other;
1222                 if (p_node == NULL) {
1223                     p_going = false;
1224                 } else {
1225                     p_nodes ++;
1226                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1227                     if (p_node->selected) {
1228                         p_sel_nodes ++;
1229                         p_sel_range = p_range;
1230                     }
1231                     if (p_node == n_node) {
1232                         n_going = false;
1233                         p_going = false;
1234                     }
1235                 }
1236             } while (n_going || p_going);
1237         }
1239         // Second pass: actually move nodes in this subpath
1240         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1241         {
1242             double n_range = 0, p_range = 0;
1243             bool n_going = true, p_going = true;
1244             Inkscape::NodePath::Node *n_node = n;
1245             Inkscape::NodePath::Node *p_node = n;
1246             do {
1247                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1248                 if (n_node && n_going)
1249                     n_node = n_node->n.other;
1250                 if (n_node == NULL) {
1251                     n_going = false;
1252                 } else {
1253                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1254                     if (n_node->selected) {
1255                         sp_nodepath_move_node_and_handles (n_node,
1256                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1257                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1258                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1259                     }
1260                     if (n_node == p_node) {
1261                         n_going = false;
1262                         p_going = false;
1263                     }
1264                 }
1265                 if (p_node && p_going)
1266                     p_node = p_node->p.other;
1267                 if (p_node == NULL) {
1268                     p_going = false;
1269                 } else {
1270                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1271                     if (p_node->selected) {
1272                         sp_nodepath_move_node_and_handles (p_node,
1273                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1274                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1275                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1276                     }
1277                     if (p_node == n_node) {
1278                         n_going = false;
1279                         p_going = false;
1280                     }
1281                 }
1282             } while (n_going || p_going);
1283         }
1285     } else {
1286         // Multiple subpaths have selected nodes:
1287         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1288         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1289         // fix the pear-like shape when sculpting e.g. a ring
1291         // First pass: calculate range
1292         gdouble direct_range = 0;
1293         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1294             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1295             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1296                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1297                 if (node->selected) {
1298                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1299                 }
1300             }
1301         }
1303         // Second pass: actually move nodes
1304         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1305             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1306             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1307                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1308                 if (node->selected) {
1309                     if (direct_range > 1e-6) {
1310                         sp_nodepath_move_node_and_handles (node,
1311                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1312                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1313                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1314                     } else {
1315                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1316                     }
1318                 }
1319             }
1320         }
1321     }
1323     // do not update repr here so that node dragging is acceptably fast
1324     update_object(nodepath);
1328 /**
1329  * Move node selection to point, adjust its and neighbouring handles,
1330  * handle possible snapping, and commit the change with possible undo.
1331  */
1332 void
1333 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1335     if (!nodepath) return;
1337     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1339     if (dx == 0) {
1340         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1341     } else if (dy == 0) {
1342         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1343     } else {
1344         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1345     }
1348 /**
1349  * Move node selection off screen and commit the change.
1350  */
1351 void
1352 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1354     // borrowed from sp_selection_move_screen in selection-chemistry.c
1355     // we find out the current zoom factor and divide deltas by it
1356     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1358     gdouble zoom = desktop->current_zoom();
1359     gdouble zdx = dx / zoom;
1360     gdouble zdy = dy / zoom;
1362     if (!nodepath) return;
1364     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1366     if (dx == 0) {
1367         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1368     } else if (dy == 0) {
1369         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1370     } else {
1371         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1372     }
1375 /**
1376  * Move selected nodes to the absolute position given
1377  */
1378 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1380     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1381         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1382         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1383         sp_node_moveto(n, npos);
1384     }
1386     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1389 /**
1390  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1391  */
1392 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1394     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1395     g_return_val_if_fail(nodepath->selected, no_coord);
1397     // determine coordinate of first selected node
1398     GList *nsel = nodepath->selected;
1399     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1400     NR::Coord coord = n->pos[axis];
1401     bool coincide = true;
1403     // compare it to the coordinates of all the other selected nodes
1404     for (GList *l = nsel->next; l != NULL; l = l->next) {
1405         n = (Inkscape::NodePath::Node *) l->data;
1406         if (n->pos[axis] != coord) {
1407             coincide = false;
1408         }
1409     }
1410     if (coincide) {
1411         return coord;
1412     } else {
1413         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1414         // currently we return the coordinate of the bounding box midpoint because I don't know how
1415         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1416         return bbox.midpoint()[axis];
1417     }
1420 /** If they don't yet exist, creates knot and line for the given side of the node */
1421 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1423     if (!side->knot) {
1424         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"));
1426         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1427         side->knot->setSize (7);
1428         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1429         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1430         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1431         sp_knot_update_ctrl(side->knot);
1433         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1434         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1435         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1436         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1437         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1438         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1439     }
1441     if (!side->line) {
1442         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1443                                         SP_TYPE_CTRLLINE, NULL);
1444     }
1447 /**
1448  * Ensure the given handle of the node is visible/invisible, update its screen position
1449  */
1450 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1452     g_assert(node != NULL);
1454    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1455     NRPathcode code = sp_node_path_code_from_side(node, side);
1457     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1459     if (show_handle) {
1460         if (!side->knot) { // No handle knot at all
1461             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1462             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1463             side->knot->pos = side->pos;
1464             if (side->knot->item)
1465                 SP_CTRL(side->knot->item)->moveto(side->pos);
1466             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1467             sp_knot_show(side->knot);
1468         } else {
1469             if (side->knot->pos != side->pos) { // only if it's really moved
1470                 if (fire_move_signals) {
1471                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1472                 } else {
1473                     sp_knot_moveto(side->knot, &side->pos);
1474                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1475                 }
1476             }
1477             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1478                 sp_knot_show(side->knot);
1479             }
1480         }
1481         sp_canvas_item_show(side->line);
1482     } else {
1483         if (side->knot) {
1484             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1485                 sp_knot_hide(side->knot);
1486             }
1487         }
1488         if (side->line) {
1489             sp_canvas_item_hide(side->line);
1490         }
1491     }
1494 /**
1495  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1496  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1497  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1498  * updated; otherwise, just move the knots silently (used in batch moves).
1499  */
1500 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1502     g_assert(node != NULL);
1504     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1505         sp_knot_show(node->knot);
1506     }
1508     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1509         if (fire_move_signals)
1510             sp_knot_set_position(node->knot, &node->pos, 0);
1511         else
1512             sp_knot_moveto(node->knot, &node->pos);
1513     }
1515     gboolean show_handles = node->selected;
1516     if (node->p.other != NULL) {
1517         if (node->p.other->selected) show_handles = TRUE;
1518     }
1519     if (node->n.other != NULL) {
1520         if (node->n.other->selected) show_handles = TRUE;
1521     }
1523     if (node->subpath->nodepath->show_handles == false)
1524         show_handles = FALSE;
1526     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1527     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1530 /**
1531  * Call sp_node_update_handles() for all nodes on subpath.
1532  */
1533 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1535     g_assert(subpath != NULL);
1537     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1538         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1539     }
1542 /**
1543  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1544  */
1545 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1547     g_assert(nodepath != NULL);
1549     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1550         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1551     }
1554 void
1555 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1557     if (nodepath == NULL) return;
1559     nodepath->show_handles = show;
1560     sp_nodepath_update_handles(nodepath);
1563 /**
1564  * Adds all selected nodes in nodepath to list.
1565  */
1566 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1568     StlConv<Node *>::list(l, selected);
1569 /// \todo this adds a copying, rework when the selection becomes a stl list
1572 /**
1573  * Align selected nodes on the specified axis.
1574  */
1575 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1577     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1578         return;
1579     }
1581     if ( !nodepath->selected->next ) { // only one node selected
1582         return;
1583     }
1584    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1585     NR::Point dest(pNode->pos);
1586     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1587         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1588         if (pNode) {
1589             dest[axis] = pNode->pos[axis];
1590             sp_node_moveto(pNode, dest);
1591         }
1592     }
1594     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1597 /// Helper struct.
1598 struct NodeSort
1600    Inkscape::NodePath::Node *_node;
1601     NR::Coord _coord;
1602     /// \todo use vectorof pointers instead of calling copy ctor
1603     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1604         _node(node), _coord(node->pos[axis])
1605     {}
1607 };
1609 static bool operator<(NodeSort const &a, NodeSort const &b)
1611     return (a._coord < b._coord);
1614 /**
1615  * Distribute selected nodes on the specified axis.
1616  */
1617 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1619     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1620         return;
1621     }
1623     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1624         return;
1625     }
1627    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1628     std::vector<NodeSort> sorted;
1629     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1630         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1631         if (pNode) {
1632             NodeSort n(pNode, axis);
1633             sorted.push_back(n);
1634             //dest[axis] = pNode->pos[axis];
1635             //sp_node_moveto(pNode, dest);
1636         }
1637     }
1638     std::sort(sorted.begin(), sorted.end());
1639     unsigned int len = sorted.size();
1640     //overall bboxes span
1641     float dist = (sorted.back()._coord -
1642                   sorted.front()._coord);
1643     //new distance between each bbox
1644     float step = (dist) / (len - 1);
1645     float pos = sorted.front()._coord;
1646     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1647           it < sorted.end();
1648           it ++ )
1649     {
1650         NR::Point dest((*it)._node->pos);
1651         dest[axis] = pos;
1652         sp_node_moveto((*it)._node, dest);
1653         pos += step;
1654     }
1656     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1660 /**
1661  * Call sp_nodepath_line_add_node() for all selected segments.
1662  */
1663 void
1664 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1666     if (!nodepath) {
1667         return;
1668     }
1670     GList *nl = NULL;
1672     int n_added = 0;
1674     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1675        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1676         g_assert(t->selected);
1677         if (t->p.other && t->p.other->selected) {
1678             nl = g_list_prepend(nl, t);
1679         }
1680     }
1682     while (nl) {
1683        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1684        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1685        sp_nodepath_node_select(n, TRUE, FALSE);
1686        n_added ++;
1687        nl = g_list_remove(nl, t);
1688     }
1690     /** \todo fixme: adjust ? */
1691     sp_nodepath_update_handles(nodepath);
1693     if (n_added > 1) {
1694         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1695     } else if (n_added > 0) {
1696         sp_nodepath_update_repr(nodepath, _("Add node"));
1697     }
1699     sp_nodepath_update_statusbar(nodepath);
1702 /**
1703  * Select segment nearest to point
1704  */
1705 void
1706 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1708     if (!nodepath) {
1709         return;
1710     }
1712     sp_nodepath_ensure_livarot_path(nodepath);
1713     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1714     if (!maybe_position) {
1715         return;
1716     }
1717     Path::cut_position position = *maybe_position;
1719     //find segment to segment
1720     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1722     //fixme: this can return NULL, so check before proceeding.
1723     g_return_if_fail(e != NULL);
1725     gboolean force = FALSE;
1726     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1727         force = TRUE;
1728     }
1729     sp_nodepath_node_select(e, (gboolean) toggle, force);
1730     if (e->p.other)
1731         sp_nodepath_node_select(e->p.other, TRUE, force);
1733     sp_nodepath_update_handles(nodepath);
1735     sp_nodepath_update_statusbar(nodepath);
1738 /**
1739  * Add a node nearest to point
1740  */
1741 void
1742 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1744     if (!nodepath) {
1745         return;
1746     }
1748     sp_nodepath_ensure_livarot_path(nodepath);
1749     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1750     if (!maybe_position) {
1751         return;
1752     }
1753     Path::cut_position position = *maybe_position;
1755     //find segment to split
1756     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1758     //don't know why but t seems to flip for lines
1759     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1760         position.t = 1.0 - position.t;
1761     }
1762     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1763     sp_nodepath_node_select(n, FALSE, TRUE);
1765     /* fixme: adjust ? */
1766     sp_nodepath_update_handles(nodepath);
1768     sp_nodepath_update_repr(nodepath, _("Add node"));
1770     sp_nodepath_update_statusbar(nodepath);
1773 /*
1774  * Adjusts a segment so that t moves by a certain delta for dragging
1775  * converts lines to curves
1776  *
1777  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1778  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1779  */
1780 void
1781 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1783     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1785     //fixme: e and e->p can be NULL, so check for those before proceeding
1786     g_return_if_fail(e != NULL);
1787     g_return_if_fail(&e->p != NULL);
1789     /* feel good is an arbitrary parameter that distributes the delta between handles
1790      * if t of the drag point is less than 1/6 distance form the endpoint only
1791      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1792      */
1793     double feel_good;
1794     if (t <= 1.0 / 6.0)
1795         feel_good = 0;
1796     else if (t <= 0.5)
1797         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1798     else if (t <= 5.0 / 6.0)
1799         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1800     else
1801         feel_good = 1;
1803     //if we're dragging a line convert it to a curve
1804     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1805         sp_nodepath_set_line_type(e, NR_CURVETO);
1806     }
1808     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1809     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1810     e->p.other->n.pos += offsetcoord0;
1811     e->p.pos += offsetcoord1;
1813     // adjust handles of adjacent nodes where necessary
1814     sp_node_adjust_handle(e,1);
1815     sp_node_adjust_handle(e->p.other,-1);
1817     sp_nodepath_update_handles(e->subpath->nodepath);
1819     update_object(e->subpath->nodepath);
1821     sp_nodepath_update_statusbar(e->subpath->nodepath);
1825 /**
1826  * Call sp_nodepath_break() for all selected segments.
1827  */
1828 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1830     if (!nodepath) return;
1832     GList *temp = NULL;
1833     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1834        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1835        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1836         if (nn == NULL) continue; // no break, no new node
1837         temp = g_list_prepend(temp, nn);
1838     }
1840     if (temp) {
1841         sp_nodepath_deselect(nodepath);
1842     }
1843     for (GList *l = temp; l != NULL; l = l->next) {
1844         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1845     }
1847     sp_nodepath_update_handles(nodepath);
1849     sp_nodepath_update_repr(nodepath, _("Break path"));
1852 /**
1853  * Duplicate the selected node(s).
1854  */
1855 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1857     if (!nodepath) {
1858         return;
1859     }
1861     GList *temp = NULL;
1862     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1863        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1864        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1865         if (nn == NULL) continue; // could not duplicate
1866         temp = g_list_prepend(temp, nn);
1867     }
1869     if (temp) {
1870         sp_nodepath_deselect(nodepath);
1871     }
1872     for (GList *l = temp; l != NULL; l = l->next) {
1873         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1874     }
1876     sp_nodepath_update_handles(nodepath);
1878     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1881 /**
1882  *  Join two nodes by merging them into one.
1883  */
1884 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
1886     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1888     if (g_list_length(nodepath->selected) != 2) {
1889         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1890         return;
1891     }
1893    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1894    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1896     g_assert(a != b);
1897     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
1898         // someone tried to join an orphan node (i.e. a single-node subpath).
1899         // this is not worth an error message, just fail silently.
1900         return;
1901     }
1903     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1904         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1905         return;
1906     }
1908     /* a and b are endpoints */
1910     NR::Point c;
1911     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1912         c = a->pos;
1913     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1914         c = b->pos;
1915     } else {
1916         c = (a->pos + b->pos) / 2;
1917     }
1919     if (a->subpath == b->subpath) {
1920        Inkscape::NodePath::SubPath *sp = a->subpath;
1921         sp_nodepath_subpath_close(sp);
1922         sp_node_moveto (sp->first, c);
1924         sp_nodepath_update_handles(sp->nodepath);
1925         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1926         return;
1927     }
1929     /* a and b are separate subpaths */
1930    Inkscape::NodePath::SubPath *sa = a->subpath;
1931    Inkscape::NodePath::SubPath *sb = b->subpath;
1932     NR::Point p;
1933    Inkscape::NodePath::Node *n;
1934     NRPathcode code;
1935     if (a == sa->first) {
1936         p = sa->first->n.pos;
1937         code = (NRPathcode)sa->first->n.other->code;
1938        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1939         n = sa->last;
1940         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1941         n = n->p.other;
1942         while (n) {
1943             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1944             n = n->p.other;
1945             if (n == sa->first) n = NULL;
1946         }
1947         sp_nodepath_subpath_destroy(sa);
1948         sa = t;
1949     } else if (a == sa->last) {
1950         p = sa->last->p.pos;
1951         code = (NRPathcode)sa->last->code;
1952         sp_nodepath_node_destroy(sa->last);
1953     } else {
1954         code = NR_END;
1955         g_assert_not_reached();
1956     }
1958     if (b == sb->first) {
1959         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1960         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1961             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1962         }
1963     } else if (b == sb->last) {
1964         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1965         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1966             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1967         }
1968     } else {
1969         g_assert_not_reached();
1970     }
1971     /* and now destroy sb */
1973     sp_nodepath_subpath_destroy(sb);
1975     sp_nodepath_update_handles(sa->nodepath);
1977     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1979     sp_nodepath_update_statusbar(nodepath);
1982 /**
1983  *  Join two nodes by adding a segment between them.
1984  */
1985 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
1987     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1989     if (g_list_length(nodepath->selected) != 2) {
1990         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1991         return;
1992     }
1994    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1995    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1997     g_assert(a != b);
1998     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
1999         // someone tried to join an orphan node (i.e. a single-node subpath).
2000         // this is not worth an error message, just fail silently.
2001         return;
2002     }
2004     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2005         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2006         return;
2007     }
2009     if (a->subpath == b->subpath) {
2010        Inkscape::NodePath::SubPath *sp = a->subpath;
2012         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2013         sp->closed = TRUE;
2015         sp->first->p.other = sp->last;
2016         sp->last->n.other  = sp->first;
2018         sp_node_handle_mirror_p_to_n(sp->last);
2019         sp_node_handle_mirror_n_to_p(sp->first);
2021         sp->first->code = sp->last->code;
2022         sp->first       = sp->last;
2024         sp_nodepath_update_handles(sp->nodepath);
2026         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2028         return;
2029     }
2031     /* a and b are separate subpaths */
2032    Inkscape::NodePath::SubPath *sa = a->subpath;
2033    Inkscape::NodePath::SubPath *sb = b->subpath;
2035    Inkscape::NodePath::Node *n;
2036     NR::Point p;
2037     NRPathcode code;
2038     if (a == sa->first) {
2039         code = (NRPathcode) sa->first->n.other->code;
2040        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2041         n = sa->last;
2042         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2043         for (n = n->p.other; n != NULL; n = n->p.other) {
2044             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2045         }
2046         sp_nodepath_subpath_destroy(sa);
2047         sa = t;
2048     } else if (a == sa->last) {
2049         code = (NRPathcode)sa->last->code;
2050     } else {
2051         code = NR_END;
2052         g_assert_not_reached();
2053     }
2055     if (b == sb->first) {
2056         n = sb->first;
2057         sp_node_handle_mirror_p_to_n(sa->last);
2058         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2059         sp_node_handle_mirror_n_to_p(sa->last);
2060         for (n = n->n.other; n != NULL; n = n->n.other) {
2061             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2062         }
2063     } else if (b == sb->last) {
2064         n = sb->last;
2065         sp_node_handle_mirror_p_to_n(sa->last);
2066         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2067         sp_node_handle_mirror_n_to_p(sa->last);
2068         for (n = n->p.other; n != NULL; n = n->p.other) {
2069             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2070         }
2071     } else {
2072         g_assert_not_reached();
2073     }
2074     /* and now destroy sb */
2076     sp_nodepath_subpath_destroy(sb);
2078     sp_nodepath_update_handles(sa->nodepath);
2080     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2083 /**
2084  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2085  */
2086 void sp_node_delete_preserve(GList *nodes_to_delete)
2088     GSList *nodepaths = NULL;
2090     while (nodes_to_delete) {
2091         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2092         Inkscape::NodePath::SubPath *sp = node->subpath;
2093         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2094         Inkscape::NodePath::Node *sample_cursor = NULL;
2095         Inkscape::NodePath::Node *sample_end = NULL;
2096         Inkscape::NodePath::Node *delete_cursor = node;
2097         bool just_delete = false;
2099         //find the start of this contiguous selection
2100         //move left to the first node that is not selected
2101         //or the start of the non-closed path
2102         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2103             delete_cursor = curr;
2104         }
2106         //just delete at the beginning of an open path
2107         if (!delete_cursor->p.other) {
2108             sample_cursor = delete_cursor;
2109             just_delete = true;
2110         } else {
2111             sample_cursor = delete_cursor->p.other;
2112         }
2114         //calculate points for each segment
2115         int rate = 5;
2116         float period = 1.0 / rate;
2117         std::vector<NR::Point> data;
2118         if (!just_delete) {
2119             data.push_back(sample_cursor->pos);
2120             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2121                 //just delete at the end of an open path
2122                 if (!sp->closed && curr == sp->last) {
2123                     just_delete = true;
2124                     break;
2125                 }
2127                 //sample points on the contiguous selected segment
2128                 NR::Point *bez;
2129                 bez = new NR::Point [4];
2130                 bez[0] = curr->pos;
2131                 bez[1] = curr->n.pos;
2132                 bez[2] = curr->n.other->p.pos;
2133                 bez[3] = curr->n.other->pos;
2134                 for (int i=1; i<rate; i++) {
2135                     gdouble t = i * period;
2136                     NR::Point p = bezier_pt(3, bez, t);
2137                     data.push_back(p);
2138                 }
2139                 data.push_back(curr->n.other->pos);
2141                 sample_end = curr->n.other;
2142                 //break if we've come full circle or hit the end of the selection
2143                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2144                     break;
2145                 }
2146             }
2147         }
2149         if (!just_delete) {
2150             //calculate the best fitting single segment and adjust the endpoints
2151             NR::Point *adata;
2152             adata = new NR::Point [data.size()];
2153             copy(data.begin(), data.end(), adata);
2155             NR::Point *bez;
2156             bez = new NR::Point [4];
2157             //would decreasing error create a better fitting approximation?
2158             gdouble error = 1.0;
2159             gint ret;
2160             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2162             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2163             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2164             //the resulting nodes behave as expected.
2165             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2166             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2168             //adjust endpoints
2169             sample_cursor->n.pos = bez[1];
2170             sample_end->p.pos = bez[2];
2171         }
2173         //destroy this contiguous selection
2174         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2175             Inkscape::NodePath::Node *temp = delete_cursor;
2176             if (delete_cursor->n.other == delete_cursor) {
2177                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2178                 delete_cursor = NULL;
2179             } else {
2180                 delete_cursor = delete_cursor->n.other;
2181             }
2182             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2183             sp_nodepath_node_destroy(temp);
2184         }
2186         sp_nodepath_update_handles(nodepath);
2188         if (!g_slist_find(nodepaths, nodepath))
2189             nodepaths = g_slist_prepend (nodepaths, nodepath);
2190     }
2192     for (GSList *i = nodepaths; i; i = i->next) {
2193         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2194         // different nodepaths will give us one undo event per nodepath
2195         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2197         // if the entire nodepath is removed, delete the selected object.
2198         if (nodepath->subpaths == NULL ||
2199             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2200             //at least 2
2201             sp_nodepath_get_node_count(nodepath) < 2) {
2202             SPDocument *document = sp_desktop_document (nodepath->desktop);
2203             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2204             //delete this nodepath's object, not the entire selection! (though at this time, this
2205             //does not matter)
2206             sp_selection_delete();
2207             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2208                               _("Delete nodes"));
2209         } else {
2210             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2211             sp_nodepath_update_statusbar(nodepath);
2212         }
2213     }
2215     g_slist_free (nodepaths);
2218 /**
2219  * Delete one or more selected nodes.
2220  */
2221 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2223     if (!nodepath) return;
2224     if (!nodepath->selected) return;
2226     /** \todo fixme: do it the right way */
2227     while (nodepath->selected) {
2228        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2229         sp_nodepath_node_destroy(node);
2230     }
2233     //clean up the nodepath (such as for trivial subpaths)
2234     sp_nodepath_cleanup(nodepath);
2236     sp_nodepath_update_handles(nodepath);
2238     // if the entire nodepath is removed, delete the selected object.
2239     if (nodepath->subpaths == NULL ||
2240         sp_nodepath_get_node_count(nodepath) < 2) {
2241         SPDocument *document = sp_desktop_document (nodepath->desktop);
2242         sp_selection_delete();
2243         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2244                           _("Delete nodes"));
2245         return;
2246     }
2248     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2250     sp_nodepath_update_statusbar(nodepath);
2253 /**
2254  * Delete one or more segments between two selected nodes.
2255  * This is the code for 'split'.
2256  */
2257 void
2258 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2260    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2261    Inkscape::NodePath::Node *curr, *next;     //Iterators
2263     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2265     if (g_list_length(nodepath->selected) != 2) {
2266         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2267                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2268         return;
2269     }
2271     //Selected nodes, not inclusive
2272    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2273    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2275     if ( ( a==b)                       ||  //same node
2276          (a->subpath  != b->subpath )  ||  //not the same path
2277          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2278          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2279     {
2280         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2281                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2282         return;
2283     }
2285     //###########################################
2286     //# BEGIN EDITS
2287     //###########################################
2288     //##################################
2289     //# CLOSED PATH
2290     //##################################
2291     if (a->subpath->closed) {
2294         gboolean reversed = FALSE;
2296         //Since we can go in a circle, we need to find the shorter distance.
2297         //  a->b or b->a
2298         start = end = NULL;
2299         int distance    = 0;
2300         int minDistance = 0;
2301         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2302             if (curr==b) {
2303                 //printf("a to b:%d\n", distance);
2304                 start = a;//go from a to b
2305                 end   = b;
2306                 minDistance = distance;
2307                 //printf("A to B :\n");
2308                 break;
2309             }
2310             distance++;
2311         }
2313         //try again, the other direction
2314         distance = 0;
2315         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2316             if (curr==a) {
2317                 //printf("b to a:%d\n", distance);
2318                 if (distance < minDistance) {
2319                     start    = b;  //we go from b to a
2320                     end      = a;
2321                     reversed = TRUE;
2322                     //printf("B to A\n");
2323                 }
2324                 break;
2325             }
2326             distance++;
2327         }
2330         //Copy everything from 'end' to 'start' to a new subpath
2331        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2332         for (curr=end ; curr ; curr=curr->n.other) {
2333             NRPathcode code = (NRPathcode) curr->code;
2334             if (curr == end)
2335                 code = NR_MOVETO;
2336             sp_nodepath_node_new(t, NULL,
2337                                  (Inkscape::NodePath::NodeType)curr->type, code,
2338                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2339             if (curr == start)
2340                 break;
2341         }
2342         sp_nodepath_subpath_destroy(a->subpath);
2345     }
2349     //##################################
2350     //# OPEN PATH
2351     //##################################
2352     else {
2354         //We need to get the direction of the list between A and B
2355         //Can we walk from a to b?
2356         start = end = NULL;
2357         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2358             if (curr==b) {
2359                 start = a;  //did it!  we go from a to b
2360                 end   = b;
2361                 //printf("A to B\n");
2362                 break;
2363             }
2364         }
2365         if (!start) {//didn't work?  let's try the other direction
2366             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2367                 if (curr==a) {
2368                     start = b;  //did it!  we go from b to a
2369                     end   = a;
2370                     //printf("B to A\n");
2371                     break;
2372                 }
2373             }
2374         }
2375         if (!start) {
2376             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2377                                                      _("Cannot find path between nodes."));
2378             return;
2379         }
2383         //Copy everything after 'end' to a new subpath
2384        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2385         for (curr=end ; curr ; curr=curr->n.other) {
2386             NRPathcode code = (NRPathcode) curr->code;
2387             if (curr == end)
2388                 code = NR_MOVETO;
2389             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2390                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2391         }
2393         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2394         for (curr = start->n.other ; curr  ; curr=next) {
2395             next = curr->n.other;
2396             sp_nodepath_node_destroy(curr);
2397         }
2399     }
2400     //###########################################
2401     //# END EDITS
2402     //###########################################
2404     //clean up the nodepath (such as for trivial subpaths)
2405     sp_nodepath_cleanup(nodepath);
2407     sp_nodepath_update_handles(nodepath);
2409     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2411     sp_nodepath_update_statusbar(nodepath);
2414 /**
2415  * Call sp_nodepath_set_line() for all selected segments.
2416  */
2417 void
2418 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2420     if (nodepath == NULL) return;
2422     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2423        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2424         g_assert(n->selected);
2425         if (n->p.other && n->p.other->selected) {
2426             sp_nodepath_set_line_type(n, code);
2427         }
2428     }
2430     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2433 /**
2434  * Call sp_nodepath_convert_node_type() for all selected nodes.
2435  */
2436 void
2437 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2439     if (nodepath == NULL) return;
2441     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2443     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2444         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2445     }
2447     sp_nodepath_update_repr(nodepath, _("Change node type"));
2450 /**
2451  * Change select status of node, update its own and neighbour handles.
2452  */
2453 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2455     node->selected = selected;
2457     if (selected) {
2458         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2459         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2460         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2461         sp_knot_update_ctrl(node->knot);
2462     } else {
2463         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2464         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2465         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2466         sp_knot_update_ctrl(node->knot);
2467     }
2469     sp_node_update_handles(node);
2470     if (node->n.other) sp_node_update_handles(node->n.other);
2471     if (node->p.other) sp_node_update_handles(node->p.other);
2474 /**
2475 \brief Select a node
2476 \param node     The node to select
2477 \param incremental   If true, add to selection, otherwise deselect others
2478 \param override   If true, always select this node, otherwise toggle selected status
2479 */
2480 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2482     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2484     if (incremental) {
2485         if (override) {
2486             if (!g_list_find(nodepath->selected, node)) {
2487                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2488             }
2489             sp_node_set_selected(node, TRUE);
2490         } else { // toggle
2491             if (node->selected) {
2492                 g_assert(g_list_find(nodepath->selected, node));
2493                 nodepath->selected = g_list_remove(nodepath->selected, node);
2494             } else {
2495                 g_assert(!g_list_find(nodepath->selected, node));
2496                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2497             }
2498             sp_node_set_selected(node, !node->selected);
2499         }
2500     } else {
2501         sp_nodepath_deselect(nodepath);
2502         nodepath->selected = g_list_prepend(nodepath->selected, node);
2503         sp_node_set_selected(node, TRUE);
2504     }
2506     sp_nodepath_update_statusbar(nodepath);
2510 /**
2511 \brief Deselect all nodes in the nodepath
2512 */
2513 void
2514 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2516     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2518     while (nodepath->selected) {
2519         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2520         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2521     }
2522     sp_nodepath_update_statusbar(nodepath);
2525 /**
2526 \brief Select or invert selection of all nodes in the nodepath
2527 */
2528 void
2529 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2531     if (!nodepath) return;
2533     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2534        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2535         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2536            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2537            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2538         }
2539     }
2542 /**
2543  * If nothing selected, does the same as sp_nodepath_select_all();
2544  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2545  * (i.e., similar to "select all in layer", with the "selected" subpaths
2546  * being treated as "layers" in the path).
2547  */
2548 void
2549 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2551     if (!nodepath) return;
2553     if (g_list_length (nodepath->selected) == 0) {
2554         sp_nodepath_select_all (nodepath, invert);
2555         return;
2556     }
2558     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2559     GSList *subpaths = NULL;
2561     for (GList *l = copy; l != NULL; l = l->next) {
2562         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2563         Inkscape::NodePath::SubPath *subpath = n->subpath;
2564         if (!g_slist_find (subpaths, subpath))
2565             subpaths = g_slist_prepend (subpaths, subpath);
2566     }
2568     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2569         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2570         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2571             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2572             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2573         }
2574     }
2576     g_slist_free (subpaths);
2577     g_list_free (copy);
2580 /**
2581  * \brief Select the node after the last selected; if none is selected,
2582  * select the first within path.
2583  */
2584 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2586     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2588    Inkscape::NodePath::Node *last = NULL;
2589     if (nodepath->selected) {
2590         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2591            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2592             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2593             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2594                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2595                 if (node->selected) {
2596                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2597                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2598                             if (spl->next) { // there's a next subpath
2599                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2600                                 last = subpath_next->first;
2601                             } else if (spl->prev) { // there's a previous subpath
2602                                 last = NULL; // to be set later to the first node of first subpath
2603                             } else {
2604                                 last = node->n.other;
2605                             }
2606                         } else {
2607                             last = node->n.other;
2608                         }
2609                     } else {
2610                         if (node->n.other) {
2611                             last = node->n.other;
2612                         } else {
2613                             if (spl->next) { // there's a next subpath
2614                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2615                                 last = subpath_next->first;
2616                             } else if (spl->prev) { // there's a previous subpath
2617                                 last = NULL; // to be set later to the first node of first subpath
2618                             } else {
2619                                 last = (Inkscape::NodePath::Node *) subpath->first;
2620                             }
2621                         }
2622                     }
2623                 }
2624             }
2625         }
2626         sp_nodepath_deselect(nodepath);
2627     }
2629     if (last) { // there's at least one more node after selected
2630         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2631     } else { // no more nodes, select the first one in first subpath
2632        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2633         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2634     }
2637 /**
2638  * \brief Select the node before the first selected; if none is selected,
2639  * select the last within path
2640  */
2641 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2643     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2645    Inkscape::NodePath::Node *last = NULL;
2646     if (nodepath->selected) {
2647         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2648            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2649             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2650                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2651                 if (node->selected) {
2652                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2653                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2654                             if (spl->prev) { // there's a prev subpath
2655                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2656                                 last = subpath_prev->last;
2657                             } else if (spl->next) { // there's a next subpath
2658                                 last = NULL; // to be set later to the last node of last subpath
2659                             } else {
2660                                 last = node->p.other;
2661                             }
2662                         } else {
2663                             last = node->p.other;
2664                         }
2665                     } else {
2666                         if (node->p.other) {
2667                             last = node->p.other;
2668                         } else {
2669                             if (spl->prev) { // there's a prev subpath
2670                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2671                                 last = subpath_prev->last;
2672                             } else if (spl->next) { // there's a next subpath
2673                                 last = NULL; // to be set later to the last node of last subpath
2674                             } else {
2675                                 last = (Inkscape::NodePath::Node *) subpath->last;
2676                             }
2677                         }
2678                     }
2679                 }
2680             }
2681         }
2682         sp_nodepath_deselect(nodepath);
2683     }
2685     if (last) { // there's at least one more node before selected
2686         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2687     } else { // no more nodes, select the last one in last subpath
2688         GList *spl = g_list_last(nodepath->subpaths);
2689        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2690         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2691     }
2694 /**
2695  * \brief Select all nodes that are within the rectangle.
2696  */
2697 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2699     if (!incremental) {
2700         sp_nodepath_deselect(nodepath);
2701     }
2703     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2704        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2705         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2706            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2708             if (b.contains(node->pos)) {
2709                 sp_nodepath_node_select(node, TRUE, TRUE);
2710             }
2711         }
2712     }
2716 void
2717 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2719     g_assert (n);
2720     g_assert (nodepath);
2721     g_assert (n->subpath->nodepath == nodepath);
2723     if (g_list_length (nodepath->selected) == 0) {
2724         if (grow > 0) {
2725             sp_nodepath_node_select(n, TRUE, TRUE);
2726         }
2727         return;
2728     }
2730     if (g_list_length (nodepath->selected) == 1) {
2731         if (grow < 0) {
2732             sp_nodepath_deselect (nodepath);
2733             return;
2734         }
2735     }
2737         double n_sel_range = 0, p_sel_range = 0;
2738             Inkscape::NodePath::Node *farthest_n_node = n;
2739             Inkscape::NodePath::Node *farthest_p_node = n;
2741         // Calculate ranges
2742         {
2743             double n_range = 0, p_range = 0;
2744             bool n_going = true, p_going = true;
2745             Inkscape::NodePath::Node *n_node = n;
2746             Inkscape::NodePath::Node *p_node = n;
2747             do {
2748                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2749                 if (n_node && n_going)
2750                     n_node = n_node->n.other;
2751                 if (n_node == NULL) {
2752                     n_going = false;
2753                 } else {
2754                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2755                     if (n_node->selected) {
2756                         n_sel_range = n_range;
2757                         farthest_n_node = n_node;
2758                     }
2759                     if (n_node == p_node) {
2760                         n_going = false;
2761                         p_going = false;
2762                     }
2763                 }
2764                 if (p_node && p_going)
2765                     p_node = p_node->p.other;
2766                 if (p_node == NULL) {
2767                     p_going = false;
2768                 } else {
2769                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2770                     if (p_node->selected) {
2771                         p_sel_range = p_range;
2772                         farthest_p_node = p_node;
2773                     }
2774                     if (p_node == n_node) {
2775                         n_going = false;
2776                         p_going = false;
2777                     }
2778                 }
2779             } while (n_going || p_going);
2780         }
2782     if (grow > 0) {
2783         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2784                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2785         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2786                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2787         }
2788     } else {
2789         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2790                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2791         } else if (farthest_p_node && farthest_p_node->selected) {
2792                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2793         }
2794     }
2797 void
2798 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2800     g_assert (n);
2801     g_assert (nodepath);
2802     g_assert (n->subpath->nodepath == nodepath);
2804     if (g_list_length (nodepath->selected) == 0) {
2805         if (grow > 0) {
2806             sp_nodepath_node_select(n, TRUE, TRUE);
2807         }
2808         return;
2809     }
2811     if (g_list_length (nodepath->selected) == 1) {
2812         if (grow < 0) {
2813             sp_nodepath_deselect (nodepath);
2814             return;
2815         }
2816     }
2818     Inkscape::NodePath::Node *farthest_selected = NULL;
2819     double farthest_dist = 0;
2821     Inkscape::NodePath::Node *closest_unselected = NULL;
2822     double closest_dist = NR_HUGE;
2824     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2825        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2826         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2827            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2828            if (node == n)
2829                continue;
2830            if (node->selected) {
2831                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2832                    farthest_dist = NR::L2(node->pos - n->pos);
2833                    farthest_selected = node;
2834                }
2835            } else {
2836                if (NR::L2(node->pos - n->pos) < closest_dist) {
2837                    closest_dist = NR::L2(node->pos - n->pos);
2838                    closest_unselected = node;
2839                }
2840            }
2841         }
2842     }
2844     if (grow > 0) {
2845         if (closest_unselected) {
2846             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2847         }
2848     } else {
2849         if (farthest_selected) {
2850             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2851         }
2852     }
2856 /**
2857 \brief  Saves all nodes' and handles' current positions in their origin members
2858 */
2859 void
2860 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2862     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2863        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2864         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2865            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2866            n->origin = n->pos;
2867            n->p.origin = n->p.pos;
2868            n->n.origin = n->n.pos;
2869         }
2870     }
2873 /**
2874 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2875 */
2876 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2878     if (!nodepath->selected) {
2879         return NULL;
2880     }
2882     GList *r = NULL;
2883     guint i = 0;
2884     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2885        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2886         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2887            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2888             i++;
2889             if (node->selected) {
2890                 r = g_list_append(r, GINT_TO_POINTER(i));
2891             }
2892         }
2893     }
2894     return r;
2897 /**
2898 \brief  Restores selection by selecting nodes whose positions are in the list
2899 */
2900 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2902     sp_nodepath_deselect(nodepath);
2904     guint i = 0;
2905     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2906        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2907         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2908            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2909             i++;
2910             if (g_list_find(r, GINT_TO_POINTER(i))) {
2911                 sp_nodepath_node_select(node, TRUE, TRUE);
2912             }
2913         }
2914     }
2918 /**
2919 \brief Adjusts handle according to node type and line code.
2920 */
2921 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2923     double len, otherlen, linelen;
2925     g_assert(node);
2927    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2928    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2930     /** \todo fixme: */
2931     if (me->other == NULL) return;
2932     if (other->other == NULL) return;
2934     /* I have line */
2936     NRPathcode mecode, ocode;
2937     if (which_adjust == 1) {
2938         mecode = (NRPathcode)me->other->code;
2939         ocode = (NRPathcode)node->code;
2940     } else {
2941         mecode = (NRPathcode)node->code;
2942         ocode = (NRPathcode)other->other->code;
2943     }
2945     if (mecode == NR_LINETO) return;
2947     /* I am curve */
2949     if (other->other == NULL) return;
2951     /* Other has line */
2953     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2955     NR::Point delta;
2956     if (ocode == NR_LINETO) {
2957         /* other is lineto, we are either smooth or symm */
2958        Inkscape::NodePath::Node *othernode = other->other;
2959         len = NR::L2(me->pos - node->pos);
2960         delta = node->pos - othernode->pos;
2961         linelen = NR::L2(delta);
2962         if (linelen < 1e-18)
2963             return;
2964         me->pos = node->pos + (len / linelen)*delta;
2965         return;
2966     }
2968     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2970         me->pos = 2 * node->pos - other->pos;
2971         return;
2972     }
2974     /* We are smooth */
2976     len = NR::L2(me->pos - node->pos);
2977     delta = other->pos - node->pos;
2978     otherlen = NR::L2(delta);
2979     if (otherlen < 1e-18) return;
2981     me->pos = node->pos - (len / otherlen) * delta;
2984 /**
2985  \brief Adjusts both handles according to node type and line code
2986  */
2987 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2989     g_assert(node);
2991     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2993     /* we are either smooth or symm */
2995     if (node->p.other == NULL) return;
2997     if (node->n.other == NULL) return;
2999     if (node->code == NR_LINETO) {
3000         if (node->n.other->code == NR_LINETO) return;
3001         sp_node_adjust_handle(node, 1);
3002         return;
3003     }
3005     if (node->n.other->code == NR_LINETO) {
3006         if (node->code == NR_LINETO) return;
3007         sp_node_adjust_handle(node, -1);
3008         return;
3009     }
3011     /* both are curves */
3012     NR::Point const delta( node->n.pos - node->p.pos );
3014     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3015         node->p.pos = node->pos - delta / 2;
3016         node->n.pos = node->pos + delta / 2;
3017         return;
3018     }
3020     /* We are smooth */
3021     double plen = NR::L2(node->p.pos - node->pos);
3022     if (plen < 1e-18) return;
3023     double nlen = NR::L2(node->n.pos - node->pos);
3024     if (nlen < 1e-18) return;
3025     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3026     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3029 /**
3030  * Node event callback.
3031  */
3032 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3034     gboolean ret = FALSE;
3035     switch (event->type) {
3036         case GDK_ENTER_NOTIFY:
3037             Inkscape::NodePath::Path::active_node = n;
3038             break;
3039         case GDK_LEAVE_NOTIFY:
3040             Inkscape::NodePath::Path::active_node = NULL;
3041             break;
3042         case GDK_SCROLL:
3043             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3044                 switch (event->scroll.direction) {
3045                     case GDK_SCROLL_UP:
3046                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3047                         break;
3048                     case GDK_SCROLL_DOWN:
3049                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3050                         break;
3051                     default:
3052                         break;
3053                 }
3054                 ret = TRUE;
3055             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3056                 switch (event->scroll.direction) {
3057                     case GDK_SCROLL_UP:
3058                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3059                         break;
3060                     case GDK_SCROLL_DOWN:
3061                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3062                         break;
3063                     default:
3064                         break;
3065                 }
3066                 ret = TRUE;
3067             }
3068             break;
3069         case GDK_KEY_PRESS:
3070             switch (get_group0_keyval (&event->key)) {
3071                 case GDK_space:
3072                     if (event->key.state & GDK_BUTTON1_MASK) {
3073                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3074                         stamp_repr(nodepath);
3075                         ret = TRUE;
3076                     }
3077                     break;
3078                 case GDK_Page_Up:
3079                     if (event->key.state & GDK_CONTROL_MASK) {
3080                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3081                     } else {
3082                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3083                     }
3084                     break;
3085                 case GDK_Page_Down:
3086                     if (event->key.state & GDK_CONTROL_MASK) {
3087                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3088                     } else {
3089                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3090                     }
3091                     break;
3092                 default:
3093                     break;
3094             }
3095             break;
3096         default:
3097             break;
3098     }
3100     return ret;
3103 /**
3104  * Handle keypress on node; directly called.
3105  */
3106 gboolean node_key(GdkEvent *event)
3108     Inkscape::NodePath::Path *np;
3110     // there is no way to verify nodes so set active_node to nil when deleting!!
3111     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3113     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3114         gint ret = FALSE;
3115         switch (get_group0_keyval (&event->key)) {
3116             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3117             case GDK_BackSpace:
3118                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3119                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3120                 sp_nodepath_update_repr(np, _("Delete node"));
3121                 Inkscape::NodePath::Path::active_node = NULL;
3122                 ret = TRUE;
3123                 break;
3124             case GDK_c:
3125                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3126                 ret = TRUE;
3127                 break;
3128             case GDK_s:
3129                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3130                 ret = TRUE;
3131                 break;
3132             case GDK_y:
3133                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3134                 ret = TRUE;
3135                 break;
3136             case GDK_b:
3137                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3138                 ret = TRUE;
3139                 break;
3140         }
3141         return ret;
3142     }
3143     return FALSE;
3146 /**
3147  * Mouseclick on node callback.
3148  */
3149 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3151    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3153     if (state & GDK_CONTROL_MASK) {
3154         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3156         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3157             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3158                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3159             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3160                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3161             } else {
3162                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3163             }
3164             sp_nodepath_update_repr(nodepath, _("Change node type"));
3165             sp_nodepath_update_statusbar(nodepath);
3167         } else { //ctrl+alt+click: delete node
3168             GList *node_to_delete = NULL;
3169             node_to_delete = g_list_append(node_to_delete, n);
3170             sp_node_delete_preserve(node_to_delete);
3171         }
3173     } else {
3174         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3175     }
3178 /**
3179  * Mouse grabbed node callback.
3180  */
3181 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3183    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3185     if (!n->selected) {
3186         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3187     }
3189     n->is_dragging = true;
3190     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3192     sp_nodepath_remember_origins (n->subpath->nodepath);
3195 /**
3196  * Mouse ungrabbed node callback.
3197  */
3198 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3200    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3202    n->dragging_out = NULL;
3203    n->is_dragging = false;
3204    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3206    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3209 /**
3210  * The point on a line, given by its angle, closest to the given point.
3211  * \param p  A point.
3212  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3213  * \param closest  Pointer to the point struct where the result is stored.
3214  * \todo FIXME: use dot product perhaps?
3215  */
3216 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3218     if (a == HUGE_VAL) { // vertical
3219         *closest = NR::Point(0, (*p)[NR::Y]);
3220     } else {
3221         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3222         (*closest)[NR::Y] = a * (*closest)[NR::X];
3223     }
3226 /**
3227  * Distance from the point to a line given by its angle.
3228  * \param p  A point.
3229  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3230  */
3231 static double point_line_distance(NR::Point *p, double a)
3233     NR::Point c;
3234     point_line_closest(p, a, &c);
3235     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]));
3238 /**
3239  * Callback for node "request" signal.
3240  * \todo fixme: This goes to "moved" event? (lauris)
3241  */
3242 static gboolean
3243 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3245     double yn, xn, yp, xp;
3246     double an, ap, na, pa;
3247     double d_an, d_ap, d_na, d_pa;
3248     gboolean collinear = FALSE;
3249     NR::Point c;
3250     NR::Point pr;
3252    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3254    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3255     if ( (!n->subpath->nodepath->straight_path) &&
3256          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3257            || n->dragging_out ) )
3258     {
3259        NR::Point mouse = (*p);
3261        if (!n->dragging_out) {
3262            // This is the first drag-out event; find out which handle to drag out
3263            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3264            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3266            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3267                return FALSE;
3269            Inkscape::NodePath::NodeSide *opposite;
3270            if (appr_p > appr_n) { // closer to p
3271                n->dragging_out = &n->p;
3272                opposite = &n->n;
3273                n->code = NR_CURVETO;
3274            } else if (appr_p < appr_n) { // closer to n
3275                n->dragging_out = &n->n;
3276                opposite = &n->p;
3277                n->n.other->code = NR_CURVETO;
3278            } else { // p and n nodes are the same
3279                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3280                    n->dragging_out = &n->p;
3281                    opposite = &n->n;
3282                    n->code = NR_CURVETO;
3283                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3284                    n->dragging_out = &n->n;
3285                    opposite = &n->p;
3286                    n->n.other->code = NR_CURVETO;
3287                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3288                    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);
3289                    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);
3290                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3291                        n->dragging_out = &n->n;
3292                        opposite = &n->p;
3293                        n->n.other->code = NR_CURVETO;
3294                    } else { // closer to other's n handle
3295                        n->dragging_out = &n->p;
3296                        opposite = &n->n;
3297                        n->code = NR_CURVETO;
3298                    }
3299                }
3300            }
3302            // if there's another handle, make sure the one we drag out starts parallel to it
3303            if (opposite->pos != n->pos) {
3304                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3305            }
3307            // knots might not be created yet!
3308            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3309            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3310        }
3312        // pass this on to the handle-moved callback
3313        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3314        sp_node_update_handles(n);
3315        return TRUE;
3316    }
3318     if (state & GDK_CONTROL_MASK) { // constrained motion
3320         // calculate relative distances of handles
3321         // n handle:
3322         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3323         xn = n->n.pos[NR::X] - n->pos[NR::X];
3324         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3325         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3326             if (n->n.other) { // if there is the next point
3327                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3328                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3329                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3330             }
3331         }
3332         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3333         if (yn < 0) { xn = -xn; yn = -yn; }
3335         // p handle:
3336         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3337         xp = n->p.pos[NR::X] - n->pos[NR::X];
3338         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3339         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3340             if (n->p.other) {
3341                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3342                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3343                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3344             }
3345         }
3346         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3347         if (yp < 0) { xp = -xp; yp = -yp; }
3349         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3350             // sliding on handles, only if at least one of the handles is non-vertical
3351             // (otherwise it's the same as ctrl+drag anyway)
3353             // calculate angles of the handles
3354             if (xn == 0) {
3355                 if (yn == 0) { // no handle, consider it the continuation of the other one
3356                     an = 0;
3357                     collinear = TRUE;
3358                 }
3359                 else an = 0; // vertical; set the angle to horizontal
3360             } else an = yn/xn;
3362             if (xp == 0) {
3363                 if (yp == 0) { // no handle, consider it the continuation of the other one
3364                     ap = an;
3365                 }
3366                 else ap = 0; // vertical; set the angle to horizontal
3367             } else  ap = yp/xp;
3369             if (collinear) an = ap;
3371             // angles of the perpendiculars; HUGE_VAL means vertical
3372             if (an == 0) na = HUGE_VAL; else na = -1/an;
3373             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3375             // mouse point relative to the node's original pos
3376             pr = (*p) - n->origin;
3378             // distances to the four lines (two handles and two perpendiculars)
3379             d_an = point_line_distance(&pr, an);
3380             d_na = point_line_distance(&pr, na);
3381             d_ap = point_line_distance(&pr, ap);
3382             d_pa = point_line_distance(&pr, pa);
3384             // find out which line is the closest, save its closest point in c
3385             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3386                 point_line_closest(&pr, an, &c);
3387             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3388                 point_line_closest(&pr, ap, &c);
3389             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3390                 point_line_closest(&pr, na, &c);
3391             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3392                 point_line_closest(&pr, pa, &c);
3393             }
3395             // move the node to the closest point
3396             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3397                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3398                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3400         } else {  // constraining to hor/vert
3402             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3403                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3404             } else { // snap to vert
3405                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3406             }
3407         }
3408     } else { // move freely
3409         if (n->is_dragging) {
3410             if (state & GDK_MOD1_MASK) { // sculpt
3411                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3412             } else {
3413                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3414                                             (*p)[NR::X] - n->pos[NR::X],
3415                                             (*p)[NR::Y] - n->pos[NR::Y],
3416                                             (state & GDK_SHIFT_MASK) == 0);
3417             }
3418         }
3419     }
3421     n->subpath->nodepath->desktop->scroll_to_point(p);
3423     return TRUE;
3426 /**
3427  * Node handle clicked callback.
3428  */
3429 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3431    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3433     if (state & GDK_CONTROL_MASK) { // "delete" handle
3434         if (n->p.knot == knot) {
3435             n->p.pos = n->pos;
3436         } else if (n->n.knot == knot) {
3437             n->n.pos = n->pos;
3438         }
3439         sp_node_update_handles(n);
3440         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3441         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3442         sp_nodepath_update_statusbar(nodepath);
3444     } else { // just select or add to selection, depending in Shift
3445         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3446     }
3449 /**
3450  * Node handle grabbed callback.
3451  */
3452 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3454    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3456     if (!n->selected) {
3457         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3458     }
3460     // remember the origin point of the handle
3461     if (n->p.knot == knot) {
3462         n->p.origin_radial = n->p.pos - n->pos;
3463     } else if (n->n.knot == knot) {
3464         n->n.origin_radial = n->n.pos - n->pos;
3465     } else {
3466         g_assert_not_reached();
3467     }
3469     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3472 /**
3473  * Node handle ungrabbed callback.
3474  */
3475 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3477    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3479     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3480     if (n->p.knot == knot) {
3481         n->p.origin_radial.a = 0;
3482         sp_knot_set_position(knot, &n->p.pos, state);
3483     } else if (n->n.knot == knot) {
3484         n->n.origin_radial.a = 0;
3485         sp_knot_set_position(knot, &n->n.pos, state);
3486     } else {
3487         g_assert_not_reached();
3488     }
3490     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3493 /**
3494  * Node handle "request" signal callback.
3495  */
3496 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3498     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3500     Inkscape::NodePath::NodeSide *me, *opposite;
3501     gint which;
3502     if (n->p.knot == knot) {
3503         me = &n->p;
3504         opposite = &n->n;
3505         which = -1;
3506     } else if (n->n.knot == knot) {
3507         me = &n->n;
3508         opposite = &n->p;
3509         which = 1;
3510     } else {
3511         me = opposite = NULL;
3512         which = 0;
3513         g_assert_not_reached();
3514     }
3516     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3518     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3520     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3521         /* We are smooth node adjacent with line */
3522         NR::Point const delta = *p - n->pos;
3523         NR::Coord const len = NR::L2(delta);
3524         Inkscape::NodePath::Node *othernode = opposite->other;
3525         NR::Point const ndelta = n->pos - othernode->pos;
3526         NR::Coord const linelen = NR::L2(ndelta);
3527         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3528             NR::Coord const scal = dot(delta, ndelta) / linelen;
3529             (*p) = n->pos + (scal / linelen) * ndelta;
3530         }
3531         *p = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), n->subpath->nodepath->item).getPoint();
3532     } else {
3533         *p = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, n->subpath->nodepath->item).getPoint();
3534     }
3536     sp_node_adjust_handle(n, -which);
3538     return FALSE;
3541 /**
3542  * Node handle moved callback.
3543  */
3544 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3546    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3548    Inkscape::NodePath::NodeSide *me;
3549    Inkscape::NodePath::NodeSide *other;
3550     if (n->p.knot == knot) {
3551         me = &n->p;
3552         other = &n->n;
3553     } else if (n->n.knot == knot) {
3554         me = &n->n;
3555         other = &n->p;
3556     } else {
3557         me = NULL;
3558         other = NULL;
3559         g_assert_not_reached();
3560     }
3562     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3563     Radial rme(me->pos - n->pos);
3564     Radial rother(other->pos - n->pos);
3565     Radial rnew(*p - n->pos);
3567     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3568         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3569         /* 0 interpreted as "no snapping". */
3571         // The closest PI/snaps angle, starting from zero.
3572         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3573         if (me->origin_radial.a == HUGE_VAL) {
3574             // ortho doesn't exist: original handle was zero length.
3575             rnew.a = a_snapped;
3576         } else {
3577             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3578              * its opposite and perpendiculars). */
3579             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3581             // Snap to the closest.
3582             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3583                        ? a_snapped
3584                        : a_ortho );
3585         }
3586     }
3588     if (state & GDK_MOD1_MASK) {
3589         // lock handle length
3590         rnew.r = me->origin_radial.r;
3591     }
3593     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3594         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3595         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3596         rother.a += rnew.a - rme.a;
3597         other->pos = NR::Point(rother) + n->pos;
3598         if (other->knot) {
3599             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3600             sp_knot_moveto(other->knot, &other->pos);
3601         }
3602     }
3604     me->pos = NR::Point(rnew) + n->pos;
3605     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3607     // move knot, but without emitting the signal:
3608     // we cannot emit a "moved" signal because we're now processing it
3609     sp_knot_moveto(me->knot, &(me->pos));
3611     update_object(n->subpath->nodepath);
3613     /* status text */
3614     SPDesktop *desktop = n->subpath->nodepath->desktop;
3615     if (!desktop) return;
3616     SPEventContext *ec = desktop->event_context;
3617     if (!ec) return;
3618     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3619     if (!mc) return;
3621     double degrees = 180 / M_PI * rnew.a;
3622     if (degrees > 180) degrees -= 360;
3623     if (degrees < -180) degrees += 360;
3624     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3625         degrees = angle_to_compass (degrees);
3627     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3629     mc->setF(Inkscape::NORMAL_MESSAGE,
3630          _("<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);
3632     g_string_free(length, TRUE);
3635 /**
3636  * Node handle event callback.
3637  */
3638 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3640     gboolean ret = FALSE;
3641     switch (event->type) {
3642         case GDK_KEY_PRESS:
3643             switch (get_group0_keyval (&event->key)) {
3644                 case GDK_space:
3645                     if (event->key.state & GDK_BUTTON1_MASK) {
3646                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3647                         stamp_repr(nodepath);
3648                         ret = TRUE;
3649                     }
3650                     break;
3651                 default:
3652                     break;
3653             }
3654             break;
3655         case GDK_ENTER_NOTIFY:
3656             // we use an experimentally determined threshold that seems to work fine
3657             if (NR::L2(n->pos - knot->pos) < 0.75)
3658                 Inkscape::NodePath::Path::active_node = n;
3659             break;
3660         case GDK_LEAVE_NOTIFY:
3661             // we use an experimentally determined threshold that seems to work fine
3662             if (NR::L2(n->pos - knot->pos) < 0.75)
3663                 Inkscape::NodePath::Path::active_node = NULL;
3664             break;
3665         default:
3666             break;
3667     }
3669     return ret;
3672 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3673                                  Radial &rme, Radial &rother, gboolean const both)
3675     rme.a += angle;
3676     if ( both
3677          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3678          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3679     {
3680         rother.a += angle;
3681     }
3684 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3685                                         Radial &rme, Radial &rother, gboolean const both)
3687     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3689     gdouble r;
3690     if ( both
3691          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3692          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3693     {
3694         r = MAX(rme.r, rother.r);
3695     } else {
3696         r = rme.r;
3697     }
3699     gdouble const weird_angle = atan2(norm_angle, r);
3700 /* Bulia says norm_angle is just the visible distance that the
3701  * object's end must travel on the screen.  Left as 'angle' for want of
3702  * a better name.*/
3704     rme.a += weird_angle;
3705     if ( both
3706          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3707          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3708     {
3709         rother.a += weird_angle;
3710     }
3713 /**
3714  * Rotate one node.
3715  */
3716 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3718     Inkscape::NodePath::NodeSide *me, *other;
3719     bool both = false;
3721     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3722     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3724     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3725         me = &(n->p);
3726         other = &(n->n);
3727     } else if (!n->p.other) {
3728         me = &(n->n);
3729         other = &(n->p);
3730     } else {
3731         if (which > 0) { // right handle
3732             if (xn > xp) {
3733                 me = &(n->n);
3734                 other = &(n->p);
3735             } else {
3736                 me = &(n->p);
3737                 other = &(n->n);
3738             }
3739         } else if (which < 0){ // left handle
3740             if (xn <= xp) {
3741                 me = &(n->n);
3742                 other = &(n->p);
3743             } else {
3744                 me = &(n->p);
3745                 other = &(n->n);
3746             }
3747         } else { // both handles
3748             me = &(n->n);
3749             other = &(n->p);
3750             both = true;
3751         }
3752     }
3754     Radial rme(me->pos - n->pos);
3755     Radial rother(other->pos - n->pos);
3757     if (screen) {
3758         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3759     } else {
3760         node_rotate_one_internal (*n, angle, rme, rother, both);
3761     }
3763     me->pos = n->pos + NR::Point(rme);
3765     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3766         other->pos =  n->pos + NR::Point(rother);
3767     }
3769     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3770     // so here we just move all the knots without emitting move signals, for speed
3771     sp_node_update_handles(n, false);
3774 /**
3775  * Rotate selected nodes.
3776  */
3777 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3779     if (!nodepath || !nodepath->selected) return;
3781     if (g_list_length(nodepath->selected) == 1) {
3782        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3783         node_rotate_one (n, angle, which, screen);
3784     } else {
3785        // rotate as an object:
3787         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3788         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3789         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3790             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3791             box.expandTo (n->pos); // contain all selected nodes
3792         }
3794         gdouble rot;
3795         if (screen) {
3796             gdouble const zoom = nodepath->desktop->current_zoom();
3797             gdouble const zmove = angle / zoom;
3798             gdouble const r = NR::L2(box.max() - box.midpoint());
3799             rot = atan2(zmove, r);
3800         } else {
3801             rot = angle;
3802         }
3804         NR::Point rot_center;
3805         if (Inkscape::NodePath::Path::active_node == NULL)
3806             rot_center = box.midpoint();
3807         else
3808             rot_center = Inkscape::NodePath::Path::active_node->pos;
3810         NR::Matrix t =
3811             NR::Matrix (NR::translate(-rot_center)) *
3812             NR::Matrix (NR::rotate(rot)) *
3813             NR::Matrix (NR::translate(rot_center));
3815         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3816             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3817             n->pos *= t;
3818             n->n.pos *= t;
3819             n->p.pos *= t;
3820             sp_node_update_handles(n, false);
3821         }
3822     }
3824     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3827 /**
3828  * Scale one node.
3829  */
3830 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3832     bool both = false;
3833     Inkscape::NodePath::NodeSide *me, *other;
3835     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3836     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3838     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3839         me = &(n->p);
3840         other = &(n->n);
3841         n->code = NR_CURVETO;
3842     } else if (!n->p.other) {
3843         me = &(n->n);
3844         other = &(n->p);
3845         if (n->n.other)
3846             n->n.other->code = NR_CURVETO;
3847     } else {
3848         if (which > 0) { // right handle
3849             if (xn > xp) {
3850                 me = &(n->n);
3851                 other = &(n->p);
3852                 if (n->n.other)
3853                     n->n.other->code = NR_CURVETO;
3854             } else {
3855                 me = &(n->p);
3856                 other = &(n->n);
3857                 n->code = NR_CURVETO;
3858             }
3859         } else if (which < 0){ // left handle
3860             if (xn <= xp) {
3861                 me = &(n->n);
3862                 other = &(n->p);
3863                 if (n->n.other)
3864                     n->n.other->code = NR_CURVETO;
3865             } else {
3866                 me = &(n->p);
3867                 other = &(n->n);
3868                 n->code = NR_CURVETO;
3869             }
3870         } else { // both handles
3871             me = &(n->n);
3872             other = &(n->p);
3873             both = true;
3874             n->code = NR_CURVETO;
3875             if (n->n.other)
3876                 n->n.other->code = NR_CURVETO;
3877         }
3878     }
3880     Radial rme(me->pos - n->pos);
3881     Radial rother(other->pos - n->pos);
3883     rme.r += grow;
3884     if (rme.r < 0) rme.r = 0;
3885     if (rme.a == HUGE_VAL) {
3886         if (me->other) { // if direction is unknown, initialize it towards the next node
3887             Radial rme_next(me->other->pos - n->pos);
3888             rme.a = rme_next.a;
3889         } else { // if there's no next, initialize to 0
3890             rme.a = 0;
3891         }
3892     }
3893     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3894         rother.r += grow;
3895         if (rother.r < 0) rother.r = 0;
3896         if (rother.a == HUGE_VAL) {
3897             rother.a = rme.a + M_PI;
3898         }
3899     }
3901     me->pos = n->pos + NR::Point(rme);
3903     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3904         other->pos = n->pos + NR::Point(rother);
3905     }
3907     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3908     // so here we just move all the knots without emitting move signals, for speed
3909     sp_node_update_handles(n, false);
3912 /**
3913  * Scale selected nodes.
3914  */
3915 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3917     if (!nodepath || !nodepath->selected) return;
3919     if (g_list_length(nodepath->selected) == 1) {
3920         // scale handles of the single selected node
3921         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3922         node_scale_one (n, grow, which);
3923     } else {
3924         // scale nodes as an "object":
3926         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3927         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3928         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3929             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3930             box.expandTo (n->pos); // contain all selected nodes
3931         }
3933         double scale = (box.maxExtent() + grow)/box.maxExtent();
3935         NR::Point scale_center;
3936         if (Inkscape::NodePath::Path::active_node == NULL)
3937             scale_center = box.midpoint();
3938         else
3939             scale_center = Inkscape::NodePath::Path::active_node->pos;
3941         NR::Matrix t =
3942             NR::Matrix (NR::translate(-scale_center)) *
3943             NR::Matrix (NR::scale(scale, scale)) *
3944             NR::Matrix (NR::translate(scale_center));
3946         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3947             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3948             n->pos *= t;
3949             n->n.pos *= t;
3950             n->p.pos *= t;
3951             sp_node_update_handles(n, false);
3952         }
3953     }
3955     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3958 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3960     if (!nodepath) return;
3961     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3964 /**
3965  * Flip selected nodes horizontally/vertically.
3966  */
3967 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
3969     if (!nodepath || !nodepath->selected) return;
3971     if (g_list_length(nodepath->selected) == 1 && !center) {
3972         // flip handles of the single selected node
3973         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3974         double temp = n->p.pos[axis];
3975         n->p.pos[axis] = n->n.pos[axis];
3976         n->n.pos[axis] = temp;
3977         sp_node_update_handles(n, false);
3978     } else {
3979         // scale nodes as an "object":
3981         NR::Rect box = sp_node_selected_bbox (nodepath);
3982         if (!center) {
3983             center = box.midpoint();
3984         }
3985         NR::Matrix t =
3986             NR::Matrix (NR::translate(- *center)) *
3987             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3988             NR::Matrix (NR::translate(*center));
3990         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3991             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3992             n->pos *= t;
3993             n->n.pos *= t;
3994             n->p.pos *= t;
3995             sp_node_update_handles(n, false);
3996         }
3997     }
3999     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4002 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4004     g_assert (nodepath->selected);
4006     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4007     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4008     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4009         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4010         box.expandTo (n->pos); // contain all selected nodes
4011     }
4012     return box;
4015 //-----------------------------------------------
4016 /**
4017  * Return new subpath under given nodepath.
4018  */
4019 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4021     g_assert(nodepath);
4022     g_assert(nodepath->desktop);
4024    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4026     s->nodepath = nodepath;
4027     s->closed = FALSE;
4028     s->nodes = NULL;
4029     s->first = NULL;
4030     s->last = NULL;
4032     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4033     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4034     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4036     return s;
4039 /**
4040  * Destroy nodes in subpath, then subpath itself.
4041  */
4042 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4044     g_assert(subpath);
4045     g_assert(subpath->nodepath);
4046     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4048     while (subpath->nodes) {
4049         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4050     }
4052     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4054     g_free(subpath);
4057 /**
4058  * Link head to tail in subpath.
4059  */
4060 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4062     g_assert(!sp->closed);
4063     g_assert(sp->last != sp->first);
4064     g_assert(sp->first->code == NR_MOVETO);
4066     sp->closed = TRUE;
4068     //Link the head to the tail
4069     sp->first->p.other = sp->last;
4070     sp->last->n.other  = sp->first;
4071     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4072     sp->first          = sp->last;
4074     //Remove the extra end node
4075     sp_nodepath_node_destroy(sp->last->n.other);
4078 /**
4079  * Open closed (loopy) subpath at node.
4080  */
4081 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4083     g_assert(sp->closed);
4084     g_assert(n->subpath == sp);
4085     g_assert(sp->first == sp->last);
4087     /* We create new startpoint, current node will become last one */
4089    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4090                                                 &n->pos, &n->pos, &n->n.pos);
4093     sp->closed        = FALSE;
4095     //Unlink to make a head and tail
4096     sp->first         = new_path;
4097     sp->last          = n;
4098     n->n.other        = NULL;
4099     new_path->p.other = NULL;
4102 /**
4103  * Return new node in subpath with given properties.
4104  * \param pos Position of node.
4105  * \param ppos Handle position in previous direction
4106  * \param npos Handle position in previous direction
4107  */
4108 Inkscape::NodePath::Node *
4109 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)
4111     g_assert(sp);
4112     g_assert(sp->nodepath);
4113     g_assert(sp->nodepath->desktop);
4115     if (nodechunk == NULL)
4116         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4118     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4120     n->subpath  = sp;
4122     if (type != Inkscape::NodePath::NODE_NONE) {
4123         // use the type from sodipodi:nodetypes
4124         n->type = type;
4125     } else {
4126         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4127             // points are (almost) collinear
4128             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4129                 // endnode, or a node with a retracted handle
4130                 n->type = Inkscape::NodePath::NODE_CUSP;
4131             } else {
4132                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4133             }
4134         } else {
4135             n->type = Inkscape::NodePath::NODE_CUSP;
4136         }
4137     }
4139     n->code     = code;
4140     n->selected = FALSE;
4141     n->pos      = *pos;
4142     n->p.pos    = *ppos;
4143     n->n.pos    = *npos;
4145     n->dragging_out = NULL;
4147     Inkscape::NodePath::Node *prev;
4148     if (next) {
4149         //g_assert(g_list_find(sp->nodes, next));
4150         prev = next->p.other;
4151     } else {
4152         prev = sp->last;
4153     }
4155     if (prev)
4156         prev->n.other = n;
4157     else
4158         sp->first = n;
4160     if (next)
4161         next->p.other = n;
4162     else
4163         sp->last = n;
4165     n->p.other = prev;
4166     n->n.other = next;
4168     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"));
4169     sp_knot_set_position(n->knot, pos, 0);
4171     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4172     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4173     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4174     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4175     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4176     sp_knot_update_ctrl(n->knot);
4178     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4179     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4180     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4181     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4182     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4183     sp_knot_show(n->knot);
4185     // We only create handle knots and lines on demand
4186     n->p.knot = NULL;
4187     n->p.line = NULL;
4188     n->n.knot = NULL;
4189     n->n.line = NULL;
4191     sp->nodes = g_list_prepend(sp->nodes, n);
4193     return n;
4196 /**
4197  * Destroy node and its knots, link neighbors in subpath.
4198  */
4199 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4201     g_assert(node);
4202     g_assert(node->subpath);
4203     g_assert(SP_IS_KNOT(node->knot));
4205    Inkscape::NodePath::SubPath *sp = node->subpath;
4207     if (node->selected) { // first, deselect
4208         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4209         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4210     }
4212     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4214     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4215     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4216     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4217     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4218     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4219     g_object_unref(G_OBJECT(node->knot));
4221     if (node->p.knot) {
4222         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4223         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4224         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4225         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4226         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4227         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4228         g_object_unref(G_OBJECT(node->p.knot));
4229         node->p.knot = NULL;
4230     }
4232     if (node->n.knot) {
4233         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4234         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4235         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4236         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4237         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4238         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4239         g_object_unref(G_OBJECT(node->n.knot));
4240         node->n.knot = NULL;
4241     }
4243     if (node->p.line)
4244         gtk_object_destroy(GTK_OBJECT(node->p.line));
4245     if (node->n.line)
4246         gtk_object_destroy(GTK_OBJECT(node->n.line));
4248     if (sp->nodes) { // there are others nodes on the subpath
4249         if (sp->closed) {
4250             if (sp->first == node) {
4251                 g_assert(sp->last == node);
4252                 sp->first = node->n.other;
4253                 sp->last = sp->first;
4254             }
4255             node->p.other->n.other = node->n.other;
4256             node->n.other->p.other = node->p.other;
4257         } else {
4258             if (sp->first == node) {
4259                 sp->first = node->n.other;
4260                 sp->first->code = NR_MOVETO;
4261             }
4262             if (sp->last == node) sp->last = node->p.other;
4263             if (node->p.other) node->p.other->n.other = node->n.other;
4264             if (node->n.other) node->n.other->p.other = node->p.other;
4265         }
4266     } else { // this was the last node on subpath
4267         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4268     }
4270     g_mem_chunk_free(nodechunk, node);
4273 /**
4274  * Returns one of the node's two sides.
4275  * \param which Indicates which side.
4276  * \return Pointer to previous node side if which==-1, next if which==1.
4277  */
4278 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4280     g_assert(node);
4282     switch (which) {
4283         case -1:
4284             return &node->p;
4285         case 1:
4286             return &node->n;
4287         default:
4288             break;
4289     }
4291     g_assert_not_reached();
4293     return NULL;
4296 /**
4297  * Return the other side of the node, given one of its sides.
4298  */
4299 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4301     g_assert(node);
4303     if (me == &node->p) return &node->n;
4304     if (me == &node->n) return &node->p;
4306     g_assert_not_reached();
4308     return NULL;
4311 /**
4312  * Return NRPathcode on the given side of the node.
4313  */
4314 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4316     g_assert(node);
4318     if (me == &node->p) {
4319         if (node->p.other) return (NRPathcode)node->code;
4320         return NR_MOVETO;
4321     }
4323     if (me == &node->n) {
4324         if (node->n.other) return (NRPathcode)node->n.other->code;
4325         return NR_MOVETO;
4326     }
4328     g_assert_not_reached();
4330     return NR_END;
4333 /**
4334  * Return node with the given index
4335  */
4336 Inkscape::NodePath::Node *
4337 sp_nodepath_get_node_by_index(int index)
4339     Inkscape::NodePath::Node *e = NULL;
4341     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4342     if (!nodepath) {
4343         return e;
4344     }
4346     //find segment
4347     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4349         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4350         int n = g_list_length(sp->nodes);
4351         if (sp->closed) {
4352             n++;
4353         }
4355         //if the piece belongs to this subpath grab it
4356         //otherwise move onto the next subpath
4357         if (index < n) {
4358             e = sp->first;
4359             for (int i = 0; i < index; ++i) {
4360                 e = e->n.other;
4361             }
4362             break;
4363         } else {
4364             if (sp->closed) {
4365                 index -= (n+1);
4366             } else {
4367                 index -= n;
4368             }
4369         }
4370     }
4372     return e;
4375 /**
4376  * Returns plain text meaning of node type.
4377  */
4378 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4380     unsigned retracted = 0;
4381     bool endnode = false;
4383     for (int which = -1; which <= 1; which += 2) {
4384         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4385         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4386             retracted ++;
4387         if (!side->other)
4388             endnode = true;
4389     }
4391     if (retracted == 0) {
4392         if (endnode) {
4393                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4394                 return _("end node");
4395         } else {
4396             switch (node->type) {
4397                 case Inkscape::NodePath::NODE_CUSP:
4398                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4399                     return _("cusp");
4400                 case Inkscape::NodePath::NODE_SMOOTH:
4401                     // TRANSLATORS: "smooth" is an adjective here
4402                     return _("smooth");
4403                 case Inkscape::NodePath::NODE_SYMM:
4404                     return _("symmetric");
4405             }
4406         }
4407     } else if (retracted == 1) {
4408         if (endnode) {
4409             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4410             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4411         } else {
4412             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4413         }
4414     } else {
4415         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4416     }
4418     return NULL;
4421 /**
4422  * Handles content of statusbar as long as node tool is active.
4423  */
4424 void
4425 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4427     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");
4428     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4430     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4431     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4432     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4433     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4435     SPDesktop *desktop = NULL;
4436     if (nodepath) {
4437         desktop = nodepath->desktop;
4438     } else {
4439         desktop = SP_ACTIVE_DESKTOP;
4440     }
4442     SPEventContext *ec = desktop->event_context;
4443     if (!ec) return;
4444     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4445     if (!mc) return;
4447     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4449     if (selected_nodes == 0) {
4450         Inkscape::Selection *sel = desktop->selection;
4451         if (!sel || sel->isEmpty()) {
4452             mc->setF(Inkscape::NORMAL_MESSAGE,
4453                      _("Select a single object to edit its nodes or handles."));
4454         } else {
4455             if (nodepath) {
4456             mc->setF(Inkscape::NORMAL_MESSAGE,
4457                      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.",
4458                               "<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.",
4459                               total_nodes),
4460                      total_nodes);
4461             } else {
4462                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4463                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4464                 } else {
4465                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4466                 }
4467             }
4468         }
4469     } else if (nodepath && selected_nodes == 1) {
4470         mc->setF(Inkscape::NORMAL_MESSAGE,
4471                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4472                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4473                           total_nodes),
4474                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4475     } else {
4476         if (selected_subpaths > 1) {
4477             mc->setF(Inkscape::NORMAL_MESSAGE,
4478                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4479                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4480                               total_nodes),
4481                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4482         } else {
4483             mc->setF(Inkscape::NORMAL_MESSAGE,
4484                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4485                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4486                               total_nodes),
4487                      selected_nodes, total_nodes, when_selected);
4488         }
4489     }
4492 /*
4493  * returns a *copy* of the curve of that object.
4494  */
4495 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4496     if (!object)
4497         return NULL;
4499     SPCurve *curve = NULL;
4500     if (SP_IS_PATH(object)) {
4501         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4502         curve = sp_curve_copy(curve_new);
4503     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4504         const gchar *svgd = object->repr->attribute(key);
4505         if (svgd) {
4506             NArtBpath *bpath = sp_svg_read_path(svgd);
4507             SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
4508             if (curve_new) {
4509                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4510             } else {
4511                 g_free(bpath);
4512             }
4513         }
4514     }
4516     return curve;
4519 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4520     if (!np || !np->object || !curve)
4521         return;
4523     if (SP_IS_PATH(np->object)) {
4524         if (SP_SHAPE(np->object)->path_effect_href) {
4525             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4526         } else {
4527             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4528         }
4529     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4530         // FIXME: this writing to string and then reading from string is bound to be slow.
4531         // create a method to convert from curve directly to 2geom...
4532         gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
4533         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4534         g_free(svgpath);
4536         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4537     }
4540 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4541     np->show_helperpath = show;
4544 /* this function does not work yet */
4545 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4546     np->straight_path = true;
4547     np->show_handles = false;
4548     g_message("add code to make the path straight.");
4549     // do sp_nodepath_convert_node_type on all nodes?
4550     // search for this text !!!   "Make selected segments lines"
4554 /*
4555   Local Variables:
4556   mode:c++
4557   c-file-style:"stroustrup"
4558   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4559   indent-tabs-mode:nil
4560   fill-column:99
4561   End:
4562 */
4563 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :