Code

angled guidelines: create angled line when dragging from edge of rulers
[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_notepath(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_notepath(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     g_return_val_if_fail(nodepath->selected, NR::Nothing());
1396     // determine coordinate of first selected node
1397     GList *nsel = nodepath->selected;
1398     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1399     NR::Coord coord = n->pos[axis];
1400     bool coincide = true;
1402     // compare it to the coordinates of all the other selected nodes
1403     for (GList *l = nsel->next; l != NULL; l = l->next) {
1404         n = (Inkscape::NodePath::Node *) l->data;
1405         if (n->pos[axis] != coord) {
1406             coincide = false;
1407         }
1408     }
1409     if (coincide) {
1410         return coord;
1411     } else {
1412         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1413         // currently we return the coordinate of the bounding box midpoint because I don't know how
1414         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1415         return bbox.midpoint()[axis];
1416     }
1419 /** If they don't yet exist, creates knot and line for the given side of the node */
1420 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1422     if (!side->knot) {
1423         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"));
1425         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1426         side->knot->setSize (7);
1427         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1428         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1429         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1430         sp_knot_update_ctrl(side->knot);
1432         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1433         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1434         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1435         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1436         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1437         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1438     }
1440     if (!side->line) {
1441         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1442                                         SP_TYPE_CTRLLINE, NULL);
1443     }
1446 /**
1447  * Ensure the given handle of the node is visible/invisible, update its screen position
1448  */
1449 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1451     g_assert(node != NULL);
1453    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1454     NRPathcode code = sp_node_path_code_from_side(node, side);
1456     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1458     if (show_handle) {
1459         if (!side->knot) { // No handle knot at all
1460             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1461             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1462             side->knot->pos = side->pos;
1463             if (side->knot->item)
1464                 SP_CTRL(side->knot->item)->moveto(side->pos);
1465             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1466             sp_knot_show(side->knot);
1467         } else {
1468             if (side->knot->pos != side->pos) { // only if it's really moved
1469                 if (fire_move_signals) {
1470                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1471                 } else {
1472                     sp_knot_moveto(side->knot, &side->pos);
1473                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1474                 }
1475             }
1476             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1477                 sp_knot_show(side->knot);
1478             }
1479         }
1480         sp_canvas_item_show(side->line);
1481     } else {
1482         if (side->knot) {
1483             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1484                 sp_knot_hide(side->knot);
1485             }
1486         }
1487         if (side->line) {
1488             sp_canvas_item_hide(side->line);
1489         }
1490     }
1493 /**
1494  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1495  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1496  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1497  * updated; otherwise, just move the knots silently (used in batch moves).
1498  */
1499 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1501     g_assert(node != NULL);
1503     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1504         sp_knot_show(node->knot);
1505     }
1507     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1508         if (fire_move_signals)
1509             sp_knot_set_position(node->knot, &node->pos, 0);
1510         else
1511             sp_knot_moveto(node->knot, &node->pos);
1512     }
1514     gboolean show_handles = node->selected;
1515     if (node->p.other != NULL) {
1516         if (node->p.other->selected) show_handles = TRUE;
1517     }
1518     if (node->n.other != NULL) {
1519         if (node->n.other->selected) show_handles = TRUE;
1520     }
1522     if (node->subpath->nodepath->show_handles == false)
1523         show_handles = FALSE;
1525     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1526     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1529 /**
1530  * Call sp_node_update_handles() for all nodes on subpath.
1531  */
1532 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1534     g_assert(subpath != NULL);
1536     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1537         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1538     }
1541 /**
1542  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1543  */
1544 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1546     g_assert(nodepath != NULL);
1548     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1549         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1550     }
1553 void
1554 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1556     if (nodepath == NULL) return;
1558     nodepath->show_handles = show;
1559     sp_nodepath_update_handles(nodepath);
1562 /**
1563  * Adds all selected nodes in nodepath to list.
1564  */
1565 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1567     StlConv<Node *>::list(l, selected);
1568 /// \todo this adds a copying, rework when the selection becomes a stl list
1571 /**
1572  * Align selected nodes on the specified axis.
1573  */
1574 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1576     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1577         return;
1578     }
1580     if ( !nodepath->selected->next ) { // only one node selected
1581         return;
1582     }
1583    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1584     NR::Point dest(pNode->pos);
1585     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1586         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1587         if (pNode) {
1588             dest[axis] = pNode->pos[axis];
1589             sp_node_moveto(pNode, dest);
1590         }
1591     }
1593     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1596 /// Helper struct.
1597 struct NodeSort
1599    Inkscape::NodePath::Node *_node;
1600     NR::Coord _coord;
1601     /// \todo use vectorof pointers instead of calling copy ctor
1602     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1603         _node(node), _coord(node->pos[axis])
1604     {}
1606 };
1608 static bool operator<(NodeSort const &a, NodeSort const &b)
1610     return (a._coord < b._coord);
1613 /**
1614  * Distribute selected nodes on the specified axis.
1615  */
1616 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1618     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1619         return;
1620     }
1622     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1623         return;
1624     }
1626    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1627     std::vector<NodeSort> sorted;
1628     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1629         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1630         if (pNode) {
1631             NodeSort n(pNode, axis);
1632             sorted.push_back(n);
1633             //dest[axis] = pNode->pos[axis];
1634             //sp_node_moveto(pNode, dest);
1635         }
1636     }
1637     std::sort(sorted.begin(), sorted.end());
1638     unsigned int len = sorted.size();
1639     //overall bboxes span
1640     float dist = (sorted.back()._coord -
1641                   sorted.front()._coord);
1642     //new distance between each bbox
1643     float step = (dist) / (len - 1);
1644     float pos = sorted.front()._coord;
1645     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1646           it < sorted.end();
1647           it ++ )
1648     {
1649         NR::Point dest((*it)._node->pos);
1650         dest[axis] = pos;
1651         sp_node_moveto((*it)._node, dest);
1652         pos += step;
1653     }
1655     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1659 /**
1660  * Call sp_nodepath_line_add_node() for all selected segments.
1661  */
1662 void
1663 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1665     if (!nodepath) {
1666         return;
1667     }
1669     GList *nl = NULL;
1671     int n_added = 0;
1673     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1674        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1675         g_assert(t->selected);
1676         if (t->p.other && t->p.other->selected) {
1677             nl = g_list_prepend(nl, t);
1678         }
1679     }
1681     while (nl) {
1682        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1683        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1684        sp_nodepath_node_select(n, TRUE, FALSE);
1685        n_added ++;
1686        nl = g_list_remove(nl, t);
1687     }
1689     /** \todo fixme: adjust ? */
1690     sp_nodepath_update_handles(nodepath);
1692     if (n_added > 1) {
1693         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1694     } else if (n_added > 0) {
1695         sp_nodepath_update_repr(nodepath, _("Add node"));
1696     }
1698     sp_nodepath_update_statusbar(nodepath);
1701 /**
1702  * Select segment nearest to point
1703  */
1704 void
1705 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1707     if (!nodepath) {
1708         return;
1709     }
1711     sp_nodepath_ensure_livarot_path(nodepath);
1712     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1713     if (!maybe_position) {
1714         return;
1715     }
1716     Path::cut_position position = *maybe_position;
1718     //find segment to segment
1719     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1721     //fixme: this can return NULL, so check before proceeding.
1722     g_return_if_fail(e != NULL);
1724     gboolean force = FALSE;
1725     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1726         force = TRUE;
1727     }
1728     sp_nodepath_node_select(e, (gboolean) toggle, force);
1729     if (e->p.other)
1730         sp_nodepath_node_select(e->p.other, TRUE, force);
1732     sp_nodepath_update_handles(nodepath);
1734     sp_nodepath_update_statusbar(nodepath);
1737 /**
1738  * Add a node nearest to point
1739  */
1740 void
1741 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1743     if (!nodepath) {
1744         return;
1745     }
1747     sp_nodepath_ensure_livarot_path(nodepath);
1748     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1749     if (!maybe_position) {
1750         return;
1751     }
1752     Path::cut_position position = *maybe_position;
1754     //find segment to split
1755     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1757     //don't know why but t seems to flip for lines
1758     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1759         position.t = 1.0 - position.t;
1760     }
1761     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1762     sp_nodepath_node_select(n, FALSE, TRUE);
1764     /* fixme: adjust ? */
1765     sp_nodepath_update_handles(nodepath);
1767     sp_nodepath_update_repr(nodepath, _("Add node"));
1769     sp_nodepath_update_statusbar(nodepath);
1772 /*
1773  * Adjusts a segment so that t moves by a certain delta for dragging
1774  * converts lines to curves
1775  *
1776  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1777  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1778  */
1779 void
1780 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1782     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1784     //fixme: e and e->p can be NULL, so check for those before proceeding
1785     g_return_if_fail(e != NULL);
1786     g_return_if_fail(&e->p != NULL);
1788     /* feel good is an arbitrary parameter that distributes the delta between handles
1789      * if t of the drag point is less than 1/6 distance form the endpoint only
1790      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1791      */
1792     double feel_good;
1793     if (t <= 1.0 / 6.0)
1794         feel_good = 0;
1795     else if (t <= 0.5)
1796         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1797     else if (t <= 5.0 / 6.0)
1798         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1799     else
1800         feel_good = 1;
1802     //if we're dragging a line convert it to a curve
1803     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1804         sp_nodepath_set_line_type(e, NR_CURVETO);
1805     }
1807     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1808     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1809     e->p.other->n.pos += offsetcoord0;
1810     e->p.pos += offsetcoord1;
1812     // adjust handles of adjacent nodes where necessary
1813     sp_node_adjust_handle(e,1);
1814     sp_node_adjust_handle(e->p.other,-1);
1816     sp_nodepath_update_handles(e->subpath->nodepath);
1818     update_object(e->subpath->nodepath);
1820     sp_nodepath_update_statusbar(e->subpath->nodepath);
1824 /**
1825  * Call sp_nodepath_break() for all selected segments.
1826  */
1827 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1829     if (!nodepath) return;
1831     GList *temp = NULL;
1832     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1833        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1834        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1835         if (nn == NULL) continue; // no break, no new node
1836         temp = g_list_prepend(temp, nn);
1837     }
1839     if (temp) {
1840         sp_nodepath_deselect(nodepath);
1841     }
1842     for (GList *l = temp; l != NULL; l = l->next) {
1843         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1844     }
1846     sp_nodepath_update_handles(nodepath);
1848     sp_nodepath_update_repr(nodepath, _("Break path"));
1851 /**
1852  * Duplicate the selected node(s).
1853  */
1854 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1856     if (!nodepath) {
1857         return;
1858     }
1860     GList *temp = NULL;
1861     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1862        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1863        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1864         if (nn == NULL) continue; // could not duplicate
1865         temp = g_list_prepend(temp, nn);
1866     }
1868     if (temp) {
1869         sp_nodepath_deselect(nodepath);
1870     }
1871     for (GList *l = temp; l != NULL; l = l->next) {
1872         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1873     }
1875     sp_nodepath_update_handles(nodepath);
1877     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1880 /**
1881  *  Join two nodes by merging them into one.
1882  */
1883 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
1885     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1887     if (g_list_length(nodepath->selected) != 2) {
1888         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1889         return;
1890     }
1892    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1893    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1895     g_assert(a != b);
1896     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
1897         // someone tried to join an orphan node (i.e. a single-node subpath).
1898         // this is not worth an error message, just fail silently.
1899         return;
1900     }
1902     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1903         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1904         return;
1905     }
1907     /* a and b are endpoints */
1909     NR::Point c;
1910     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1911         c = a->pos;
1912     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1913         c = b->pos;
1914     } else {
1915         c = (a->pos + b->pos) / 2;
1916     }
1918     if (a->subpath == b->subpath) {
1919        Inkscape::NodePath::SubPath *sp = a->subpath;
1920         sp_nodepath_subpath_close(sp);
1921         sp_node_moveto (sp->first, c);
1923         sp_nodepath_update_handles(sp->nodepath);
1924         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1925         return;
1926     }
1928     /* a and b are separate subpaths */
1929    Inkscape::NodePath::SubPath *sa = a->subpath;
1930    Inkscape::NodePath::SubPath *sb = b->subpath;
1931     NR::Point p;
1932    Inkscape::NodePath::Node *n;
1933     NRPathcode code;
1934     if (a == sa->first) {
1935         p = sa->first->n.pos;
1936         code = (NRPathcode)sa->first->n.other->code;
1937        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1938         n = sa->last;
1939         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1940         n = n->p.other;
1941         while (n) {
1942             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1943             n = n->p.other;
1944             if (n == sa->first) n = NULL;
1945         }
1946         sp_nodepath_subpath_destroy(sa);
1947         sa = t;
1948     } else if (a == sa->last) {
1949         p = sa->last->p.pos;
1950         code = (NRPathcode)sa->last->code;
1951         sp_nodepath_node_destroy(sa->last);
1952     } else {
1953         code = NR_END;
1954         g_assert_not_reached();
1955     }
1957     if (b == sb->first) {
1958         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1959         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1960             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1961         }
1962     } else if (b == sb->last) {
1963         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1964         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1965             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1966         }
1967     } else {
1968         g_assert_not_reached();
1969     }
1970     /* and now destroy sb */
1972     sp_nodepath_subpath_destroy(sb);
1974     sp_nodepath_update_handles(sa->nodepath);
1976     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1978     sp_nodepath_update_statusbar(nodepath);
1981 /**
1982  *  Join two nodes by adding a segment between them.
1983  */
1984 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
1986     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1988     if (g_list_length(nodepath->selected) != 2) {
1989         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1990         return;
1991     }
1993    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1994    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1996     g_assert(a != b);
1997     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
1998         // someone tried to join an orphan node (i.e. a single-node subpath).
1999         // this is not worth an error message, just fail silently.
2000         return;
2001     }
2003     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2004         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2005         return;
2006     }
2008     if (a->subpath == b->subpath) {
2009        Inkscape::NodePath::SubPath *sp = a->subpath;
2011         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2012         sp->closed = TRUE;
2014         sp->first->p.other = sp->last;
2015         sp->last->n.other  = sp->first;
2017         sp_node_handle_mirror_p_to_n(sp->last);
2018         sp_node_handle_mirror_n_to_p(sp->first);
2020         sp->first->code = sp->last->code;
2021         sp->first       = sp->last;
2023         sp_nodepath_update_handles(sp->nodepath);
2025         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2027         return;
2028     }
2030     /* a and b are separate subpaths */
2031    Inkscape::NodePath::SubPath *sa = a->subpath;
2032    Inkscape::NodePath::SubPath *sb = b->subpath;
2034    Inkscape::NodePath::Node *n;
2035     NR::Point p;
2036     NRPathcode code;
2037     if (a == sa->first) {
2038         code = (NRPathcode) sa->first->n.other->code;
2039        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2040         n = sa->last;
2041         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2042         for (n = n->p.other; n != NULL; n = n->p.other) {
2043             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2044         }
2045         sp_nodepath_subpath_destroy(sa);
2046         sa = t;
2047     } else if (a == sa->last) {
2048         code = (NRPathcode)sa->last->code;
2049     } else {
2050         code = NR_END;
2051         g_assert_not_reached();
2052     }
2054     if (b == sb->first) {
2055         n = sb->first;
2056         sp_node_handle_mirror_p_to_n(sa->last);
2057         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2058         sp_node_handle_mirror_n_to_p(sa->last);
2059         for (n = n->n.other; n != NULL; n = n->n.other) {
2060             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2061         }
2062     } else if (b == sb->last) {
2063         n = sb->last;
2064         sp_node_handle_mirror_p_to_n(sa->last);
2065         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2066         sp_node_handle_mirror_n_to_p(sa->last);
2067         for (n = n->p.other; n != NULL; n = n->p.other) {
2068             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2069         }
2070     } else {
2071         g_assert_not_reached();
2072     }
2073     /* and now destroy sb */
2075     sp_nodepath_subpath_destroy(sb);
2077     sp_nodepath_update_handles(sa->nodepath);
2079     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2082 /**
2083  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2084  */
2085 void sp_node_delete_preserve(GList *nodes_to_delete)
2087     GSList *nodepaths = NULL;
2089     while (nodes_to_delete) {
2090         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2091         Inkscape::NodePath::SubPath *sp = node->subpath;
2092         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2093         Inkscape::NodePath::Node *sample_cursor = NULL;
2094         Inkscape::NodePath::Node *sample_end = NULL;
2095         Inkscape::NodePath::Node *delete_cursor = node;
2096         bool just_delete = false;
2098         //find the start of this contiguous selection
2099         //move left to the first node that is not selected
2100         //or the start of the non-closed path
2101         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2102             delete_cursor = curr;
2103         }
2105         //just delete at the beginning of an open path
2106         if (!delete_cursor->p.other) {
2107             sample_cursor = delete_cursor;
2108             just_delete = true;
2109         } else {
2110             sample_cursor = delete_cursor->p.other;
2111         }
2113         //calculate points for each segment
2114         int rate = 5;
2115         float period = 1.0 / rate;
2116         std::vector<NR::Point> data;
2117         if (!just_delete) {
2118             data.push_back(sample_cursor->pos);
2119             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2120                 //just delete at the end of an open path
2121                 if (!sp->closed && curr == sp->last) {
2122                     just_delete = true;
2123                     break;
2124                 }
2126                 //sample points on the contiguous selected segment
2127                 NR::Point *bez;
2128                 bez = new NR::Point [4];
2129                 bez[0] = curr->pos;
2130                 bez[1] = curr->n.pos;
2131                 bez[2] = curr->n.other->p.pos;
2132                 bez[3] = curr->n.other->pos;
2133                 for (int i=1; i<rate; i++) {
2134                     gdouble t = i * period;
2135                     NR::Point p = bezier_pt(3, bez, t);
2136                     data.push_back(p);
2137                 }
2138                 data.push_back(curr->n.other->pos);
2140                 sample_end = curr->n.other;
2141                 //break if we've come full circle or hit the end of the selection
2142                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2143                     break;
2144                 }
2145             }
2146         }
2148         if (!just_delete) {
2149             //calculate the best fitting single segment and adjust the endpoints
2150             NR::Point *adata;
2151             adata = new NR::Point [data.size()];
2152             copy(data.begin(), data.end(), adata);
2154             NR::Point *bez;
2155             bez = new NR::Point [4];
2156             //would decreasing error create a better fitting approximation?
2157             gdouble error = 1.0;
2158             gint ret;
2159             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2161             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2162             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2163             //the resulting nodes behave as expected.
2164             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2165             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2167             //adjust endpoints
2168             sample_cursor->n.pos = bez[1];
2169             sample_end->p.pos = bez[2];
2170         }
2172         //destroy this contiguous selection
2173         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2174             Inkscape::NodePath::Node *temp = delete_cursor;
2175             if (delete_cursor->n.other == delete_cursor) {
2176                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2177                 delete_cursor = NULL;
2178             } else {
2179                 delete_cursor = delete_cursor->n.other;
2180             }
2181             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2182             sp_nodepath_node_destroy(temp);
2183         }
2185         sp_nodepath_update_handles(nodepath);
2187         if (!g_slist_find(nodepaths, nodepath))
2188             nodepaths = g_slist_prepend (nodepaths, nodepath);
2189     }
2191     for (GSList *i = nodepaths; i; i = i->next) {
2192         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2193         // different nodepaths will give us one undo event per nodepath
2194         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2196         // if the entire nodepath is removed, delete the selected object.
2197         if (nodepath->subpaths == NULL ||
2198             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2199             //at least 2
2200             sp_nodepath_get_node_count(nodepath) < 2) {
2201             SPDocument *document = sp_desktop_document (nodepath->desktop);
2202             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2203             //delete this nodepath's object, not the entire selection! (though at this time, this
2204             //does not matter)
2205             sp_selection_delete();
2206             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2207                               _("Delete nodes"));
2208         } else {
2209             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2210             sp_nodepath_update_statusbar(nodepath);
2211         }
2212     }
2214     g_slist_free (nodepaths);
2217 /**
2218  * Delete one or more selected nodes.
2219  */
2220 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2222     if (!nodepath) return;
2223     if (!nodepath->selected) return;
2225     /** \todo fixme: do it the right way */
2226     while (nodepath->selected) {
2227        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2228         sp_nodepath_node_destroy(node);
2229     }
2232     //clean up the nodepath (such as for trivial subpaths)
2233     sp_nodepath_cleanup(nodepath);
2235     sp_nodepath_update_handles(nodepath);
2237     // if the entire nodepath is removed, delete the selected object.
2238     if (nodepath->subpaths == NULL ||
2239         sp_nodepath_get_node_count(nodepath) < 2) {
2240         SPDocument *document = sp_desktop_document (nodepath->desktop);
2241         sp_selection_delete();
2242         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2243                           _("Delete nodes"));
2244         return;
2245     }
2247     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2249     sp_nodepath_update_statusbar(nodepath);
2252 /**
2253  * Delete one or more segments between two selected nodes.
2254  * This is the code for 'split'.
2255  */
2256 void
2257 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2259    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2260    Inkscape::NodePath::Node *curr, *next;     //Iterators
2262     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2264     if (g_list_length(nodepath->selected) != 2) {
2265         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2266                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2267         return;
2268     }
2270     //Selected nodes, not inclusive
2271    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2272    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2274     if ( ( a==b)                       ||  //same node
2275          (a->subpath  != b->subpath )  ||  //not the same path
2276          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2277          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2278     {
2279         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2280                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2281         return;
2282     }
2284     //###########################################
2285     //# BEGIN EDITS
2286     //###########################################
2287     //##################################
2288     //# CLOSED PATH
2289     //##################################
2290     if (a->subpath->closed) {
2293         gboolean reversed = FALSE;
2295         //Since we can go in a circle, we need to find the shorter distance.
2296         //  a->b or b->a
2297         start = end = NULL;
2298         int distance    = 0;
2299         int minDistance = 0;
2300         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2301             if (curr==b) {
2302                 //printf("a to b:%d\n", distance);
2303                 start = a;//go from a to b
2304                 end   = b;
2305                 minDistance = distance;
2306                 //printf("A to B :\n");
2307                 break;
2308             }
2309             distance++;
2310         }
2312         //try again, the other direction
2313         distance = 0;
2314         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2315             if (curr==a) {
2316                 //printf("b to a:%d\n", distance);
2317                 if (distance < minDistance) {
2318                     start    = b;  //we go from b to a
2319                     end      = a;
2320                     reversed = TRUE;
2321                     //printf("B to A\n");
2322                 }
2323                 break;
2324             }
2325             distance++;
2326         }
2329         //Copy everything from 'end' to 'start' to a new subpath
2330        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2331         for (curr=end ; curr ; curr=curr->n.other) {
2332             NRPathcode code = (NRPathcode) curr->code;
2333             if (curr == end)
2334                 code = NR_MOVETO;
2335             sp_nodepath_node_new(t, NULL,
2336                                  (Inkscape::NodePath::NodeType)curr->type, code,
2337                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2338             if (curr == start)
2339                 break;
2340         }
2341         sp_nodepath_subpath_destroy(a->subpath);
2344     }
2348     //##################################
2349     //# OPEN PATH
2350     //##################################
2351     else {
2353         //We need to get the direction of the list between A and B
2354         //Can we walk from a to b?
2355         start = end = NULL;
2356         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2357             if (curr==b) {
2358                 start = a;  //did it!  we go from a to b
2359                 end   = b;
2360                 //printf("A to B\n");
2361                 break;
2362             }
2363         }
2364         if (!start) {//didn't work?  let's try the other direction
2365             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2366                 if (curr==a) {
2367                     start = b;  //did it!  we go from b to a
2368                     end   = a;
2369                     //printf("B to A\n");
2370                     break;
2371                 }
2372             }
2373         }
2374         if (!start) {
2375             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2376                                                      _("Cannot find path between nodes."));
2377             return;
2378         }
2382         //Copy everything after 'end' to a new subpath
2383        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2384         for (curr=end ; curr ; curr=curr->n.other) {
2385             NRPathcode code = (NRPathcode) curr->code;
2386             if (curr == end)
2387                 code = NR_MOVETO;
2388             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2389                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2390         }
2392         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2393         for (curr = start->n.other ; curr  ; curr=next) {
2394             next = curr->n.other;
2395             sp_nodepath_node_destroy(curr);
2396         }
2398     }
2399     //###########################################
2400     //# END EDITS
2401     //###########################################
2403     //clean up the nodepath (such as for trivial subpaths)
2404     sp_nodepath_cleanup(nodepath);
2406     sp_nodepath_update_handles(nodepath);
2408     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2410     sp_nodepath_update_statusbar(nodepath);
2413 /**
2414  * Call sp_nodepath_set_line() for all selected segments.
2415  */
2416 void
2417 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2419     if (nodepath == NULL) return;
2421     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2422        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2423         g_assert(n->selected);
2424         if (n->p.other && n->p.other->selected) {
2425             sp_nodepath_set_line_type(n, code);
2426         }
2427     }
2429     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2432 /**
2433  * Call sp_nodepath_convert_node_type() for all selected nodes.
2434  */
2435 void
2436 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2438     if (nodepath == NULL) return;
2440     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2442     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2443         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2444     }
2446     sp_nodepath_update_repr(nodepath, _("Change node type"));
2449 /**
2450  * Change select status of node, update its own and neighbour handles.
2451  */
2452 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2454     node->selected = selected;
2456     if (selected) {
2457         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2458         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2459         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2460         sp_knot_update_ctrl(node->knot);
2461     } else {
2462         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2463         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2464         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2465         sp_knot_update_ctrl(node->knot);
2466     }
2468     sp_node_update_handles(node);
2469     if (node->n.other) sp_node_update_handles(node->n.other);
2470     if (node->p.other) sp_node_update_handles(node->p.other);
2473 /**
2474 \brief Select a node
2475 \param node     The node to select
2476 \param incremental   If true, add to selection, otherwise deselect others
2477 \param override   If true, always select this node, otherwise toggle selected status
2478 */
2479 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2481     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2483     if (incremental) {
2484         if (override) {
2485             if (!g_list_find(nodepath->selected, node)) {
2486                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2487             }
2488             sp_node_set_selected(node, TRUE);
2489         } else { // toggle
2490             if (node->selected) {
2491                 g_assert(g_list_find(nodepath->selected, node));
2492                 nodepath->selected = g_list_remove(nodepath->selected, node);
2493             } else {
2494                 g_assert(!g_list_find(nodepath->selected, node));
2495                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2496             }
2497             sp_node_set_selected(node, !node->selected);
2498         }
2499     } else {
2500         sp_nodepath_deselect(nodepath);
2501         nodepath->selected = g_list_prepend(nodepath->selected, node);
2502         sp_node_set_selected(node, TRUE);
2503     }
2505     sp_nodepath_update_statusbar(nodepath);
2509 /**
2510 \brief Deselect all nodes in the nodepath
2511 */
2512 void
2513 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2515     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2517     while (nodepath->selected) {
2518         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2519         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2520     }
2521     sp_nodepath_update_statusbar(nodepath);
2524 /**
2525 \brief Select or invert selection of all nodes in the nodepath
2526 */
2527 void
2528 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2530     if (!nodepath) return;
2532     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2533        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2534         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2535            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2536            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2537         }
2538     }
2541 /**
2542  * If nothing selected, does the same as sp_nodepath_select_all();
2543  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2544  * (i.e., similar to "select all in layer", with the "selected" subpaths
2545  * being treated as "layers" in the path).
2546  */
2547 void
2548 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2550     if (!nodepath) return;
2552     if (g_list_length (nodepath->selected) == 0) {
2553         sp_nodepath_select_all (nodepath, invert);
2554         return;
2555     }
2557     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2558     GSList *subpaths = NULL;
2560     for (GList *l = copy; l != NULL; l = l->next) {
2561         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2562         Inkscape::NodePath::SubPath *subpath = n->subpath;
2563         if (!g_slist_find (subpaths, subpath))
2564             subpaths = g_slist_prepend (subpaths, subpath);
2565     }
2567     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2568         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2569         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2570             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2571             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2572         }
2573     }
2575     g_slist_free (subpaths);
2576     g_list_free (copy);
2579 /**
2580  * \brief Select the node after the last selected; if none is selected,
2581  * select the first within path.
2582  */
2583 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2585     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2587    Inkscape::NodePath::Node *last = NULL;
2588     if (nodepath->selected) {
2589         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2590            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2591             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2592             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2593                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2594                 if (node->selected) {
2595                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2596                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2597                             if (spl->next) { // there's a next subpath
2598                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2599                                 last = subpath_next->first;
2600                             } else if (spl->prev) { // there's a previous subpath
2601                                 last = NULL; // to be set later to the first node of first subpath
2602                             } else {
2603                                 last = node->n.other;
2604                             }
2605                         } else {
2606                             last = node->n.other;
2607                         }
2608                     } else {
2609                         if (node->n.other) {
2610                             last = node->n.other;
2611                         } else {
2612                             if (spl->next) { // there's a next subpath
2613                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2614                                 last = subpath_next->first;
2615                             } else if (spl->prev) { // there's a previous subpath
2616                                 last = NULL; // to be set later to the first node of first subpath
2617                             } else {
2618                                 last = (Inkscape::NodePath::Node *) subpath->first;
2619                             }
2620                         }
2621                     }
2622                 }
2623             }
2624         }
2625         sp_nodepath_deselect(nodepath);
2626     }
2628     if (last) { // there's at least one more node after selected
2629         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2630     } else { // no more nodes, select the first one in first subpath
2631        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2632         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2633     }
2636 /**
2637  * \brief Select the node before the first selected; if none is selected,
2638  * select the last within path
2639  */
2640 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2642     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2644    Inkscape::NodePath::Node *last = NULL;
2645     if (nodepath->selected) {
2646         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2647            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2648             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2649                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2650                 if (node->selected) {
2651                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2652                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2653                             if (spl->prev) { // there's a prev subpath
2654                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2655                                 last = subpath_prev->last;
2656                             } else if (spl->next) { // there's a next subpath
2657                                 last = NULL; // to be set later to the last node of last subpath
2658                             } else {
2659                                 last = node->p.other;
2660                             }
2661                         } else {
2662                             last = node->p.other;
2663                         }
2664                     } else {
2665                         if (node->p.other) {
2666                             last = node->p.other;
2667                         } else {
2668                             if (spl->prev) { // there's a prev subpath
2669                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2670                                 last = subpath_prev->last;
2671                             } else if (spl->next) { // there's a next subpath
2672                                 last = NULL; // to be set later to the last node of last subpath
2673                             } else {
2674                                 last = (Inkscape::NodePath::Node *) subpath->last;
2675                             }
2676                         }
2677                     }
2678                 }
2679             }
2680         }
2681         sp_nodepath_deselect(nodepath);
2682     }
2684     if (last) { // there's at least one more node before selected
2685         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2686     } else { // no more nodes, select the last one in last subpath
2687         GList *spl = g_list_last(nodepath->subpaths);
2688        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2689         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2690     }
2693 /**
2694  * \brief Select all nodes that are within the rectangle.
2695  */
2696 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2698     if (!incremental) {
2699         sp_nodepath_deselect(nodepath);
2700     }
2702     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2703        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2704         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2705            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2707             if (b.contains(node->pos)) {
2708                 sp_nodepath_node_select(node, TRUE, TRUE);
2709             }
2710         }
2711     }
2715 void
2716 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2718     g_assert (n);
2719     g_assert (nodepath);
2720     g_assert (n->subpath->nodepath == nodepath);
2722     if (g_list_length (nodepath->selected) == 0) {
2723         if (grow > 0) {
2724             sp_nodepath_node_select(n, TRUE, TRUE);
2725         }
2726         return;
2727     }
2729     if (g_list_length (nodepath->selected) == 1) {
2730         if (grow < 0) {
2731             sp_nodepath_deselect (nodepath);
2732             return;
2733         }
2734     }
2736         double n_sel_range = 0, p_sel_range = 0;
2737             Inkscape::NodePath::Node *farthest_n_node = n;
2738             Inkscape::NodePath::Node *farthest_p_node = n;
2740         // Calculate ranges
2741         {
2742             double n_range = 0, p_range = 0;
2743             bool n_going = true, p_going = true;
2744             Inkscape::NodePath::Node *n_node = n;
2745             Inkscape::NodePath::Node *p_node = n;
2746             do {
2747                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2748                 if (n_node && n_going)
2749                     n_node = n_node->n.other;
2750                 if (n_node == NULL) {
2751                     n_going = false;
2752                 } else {
2753                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2754                     if (n_node->selected) {
2755                         n_sel_range = n_range;
2756                         farthest_n_node = n_node;
2757                     }
2758                     if (n_node == p_node) {
2759                         n_going = false;
2760                         p_going = false;
2761                     }
2762                 }
2763                 if (p_node && p_going)
2764                     p_node = p_node->p.other;
2765                 if (p_node == NULL) {
2766                     p_going = false;
2767                 } else {
2768                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2769                     if (p_node->selected) {
2770                         p_sel_range = p_range;
2771                         farthest_p_node = p_node;
2772                     }
2773                     if (p_node == n_node) {
2774                         n_going = false;
2775                         p_going = false;
2776                     }
2777                 }
2778             } while (n_going || p_going);
2779         }
2781     if (grow > 0) {
2782         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2783                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2784         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2785                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2786         }
2787     } else {
2788         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2789                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2790         } else if (farthest_p_node && farthest_p_node->selected) {
2791                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2792         }
2793     }
2796 void
2797 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2799     g_assert (n);
2800     g_assert (nodepath);
2801     g_assert (n->subpath->nodepath == nodepath);
2803     if (g_list_length (nodepath->selected) == 0) {
2804         if (grow > 0) {
2805             sp_nodepath_node_select(n, TRUE, TRUE);
2806         }
2807         return;
2808     }
2810     if (g_list_length (nodepath->selected) == 1) {
2811         if (grow < 0) {
2812             sp_nodepath_deselect (nodepath);
2813             return;
2814         }
2815     }
2817     Inkscape::NodePath::Node *farthest_selected = NULL;
2818     double farthest_dist = 0;
2820     Inkscape::NodePath::Node *closest_unselected = NULL;
2821     double closest_dist = NR_HUGE;
2823     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2824        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2825         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2826            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2827            if (node == n)
2828                continue;
2829            if (node->selected) {
2830                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2831                    farthest_dist = NR::L2(node->pos - n->pos);
2832                    farthest_selected = node;
2833                }
2834            } else {
2835                if (NR::L2(node->pos - n->pos) < closest_dist) {
2836                    closest_dist = NR::L2(node->pos - n->pos);
2837                    closest_unselected = node;
2838                }
2839            }
2840         }
2841     }
2843     if (grow > 0) {
2844         if (closest_unselected) {
2845             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2846         }
2847     } else {
2848         if (farthest_selected) {
2849             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2850         }
2851     }
2855 /**
2856 \brief  Saves all nodes' and handles' current positions in their origin members
2857 */
2858 void
2859 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2861     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2862        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2863         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2864            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2865            n->origin = n->pos;
2866            n->p.origin = n->p.pos;
2867            n->n.origin = n->n.pos;
2868         }
2869     }
2872 /**
2873 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2874 */
2875 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2877     if (!nodepath->selected) {
2878         return NULL;
2879     }
2881     GList *r = NULL;
2882     guint i = 0;
2883     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2884        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2885         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2886            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2887             i++;
2888             if (node->selected) {
2889                 r = g_list_append(r, GINT_TO_POINTER(i));
2890             }
2891         }
2892     }
2893     return r;
2896 /**
2897 \brief  Restores selection by selecting nodes whose positions are in the list
2898 */
2899 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2901     sp_nodepath_deselect(nodepath);
2903     guint i = 0;
2904     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2905        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2906         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2907            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2908             i++;
2909             if (g_list_find(r, GINT_TO_POINTER(i))) {
2910                 sp_nodepath_node_select(node, TRUE, TRUE);
2911             }
2912         }
2913     }
2917 /**
2918 \brief Adjusts handle according to node type and line code.
2919 */
2920 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2922     double len, otherlen, linelen;
2924     g_assert(node);
2926    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2927    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2929     /** \todo fixme: */
2930     if (me->other == NULL) return;
2931     if (other->other == NULL) return;
2933     /* I have line */
2935     NRPathcode mecode, ocode;
2936     if (which_adjust == 1) {
2937         mecode = (NRPathcode)me->other->code;
2938         ocode = (NRPathcode)node->code;
2939     } else {
2940         mecode = (NRPathcode)node->code;
2941         ocode = (NRPathcode)other->other->code;
2942     }
2944     if (mecode == NR_LINETO) return;
2946     /* I am curve */
2948     if (other->other == NULL) return;
2950     /* Other has line */
2952     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2954     NR::Point delta;
2955     if (ocode == NR_LINETO) {
2956         /* other is lineto, we are either smooth or symm */
2957        Inkscape::NodePath::Node *othernode = other->other;
2958         len = NR::L2(me->pos - node->pos);
2959         delta = node->pos - othernode->pos;
2960         linelen = NR::L2(delta);
2961         if (linelen < 1e-18)
2962             return;
2963         me->pos = node->pos + (len / linelen)*delta;
2964         return;
2965     }
2967     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2969         me->pos = 2 * node->pos - other->pos;
2970         return;
2971     }
2973     /* We are smooth */
2975     len = NR::L2(me->pos - node->pos);
2976     delta = other->pos - node->pos;
2977     otherlen = NR::L2(delta);
2978     if (otherlen < 1e-18) return;
2980     me->pos = node->pos - (len / otherlen) * delta;
2983 /**
2984  \brief Adjusts both handles according to node type and line code
2985  */
2986 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2988     g_assert(node);
2990     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2992     /* we are either smooth or symm */
2994     if (node->p.other == NULL) return;
2996     if (node->n.other == NULL) return;
2998     if (node->code == NR_LINETO) {
2999         if (node->n.other->code == NR_LINETO) return;
3000         sp_node_adjust_handle(node, 1);
3001         return;
3002     }
3004     if (node->n.other->code == NR_LINETO) {
3005         if (node->code == NR_LINETO) return;
3006         sp_node_adjust_handle(node, -1);
3007         return;
3008     }
3010     /* both are curves */
3011     NR::Point const delta( node->n.pos - node->p.pos );
3013     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3014         node->p.pos = node->pos - delta / 2;
3015         node->n.pos = node->pos + delta / 2;
3016         return;
3017     }
3019     /* We are smooth */
3020     double plen = NR::L2(node->p.pos - node->pos);
3021     if (plen < 1e-18) return;
3022     double nlen = NR::L2(node->n.pos - node->pos);
3023     if (nlen < 1e-18) return;
3024     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3025     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3028 /**
3029  * Node event callback.
3030  */
3031 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3033     gboolean ret = FALSE;
3034     switch (event->type) {
3035         case GDK_ENTER_NOTIFY:
3036             Inkscape::NodePath::Path::active_node = n;
3037             break;
3038         case GDK_LEAVE_NOTIFY:
3039             Inkscape::NodePath::Path::active_node = NULL;
3040             break;
3041         case GDK_SCROLL:
3042             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3043                 switch (event->scroll.direction) {
3044                     case GDK_SCROLL_UP:
3045                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3046                         break;
3047                     case GDK_SCROLL_DOWN:
3048                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3049                         break;
3050                     default:
3051                         break;
3052                 }
3053                 ret = TRUE;
3054             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3055                 switch (event->scroll.direction) {
3056                     case GDK_SCROLL_UP:
3057                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3058                         break;
3059                     case GDK_SCROLL_DOWN:
3060                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3061                         break;
3062                     default:
3063                         break;
3064                 }
3065                 ret = TRUE;
3066             }
3067             break;
3068         case GDK_KEY_PRESS:
3069             switch (get_group0_keyval (&event->key)) {
3070                 case GDK_space:
3071                     if (event->key.state & GDK_BUTTON1_MASK) {
3072                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3073                         stamp_repr(nodepath);
3074                         ret = TRUE;
3075                     }
3076                     break;
3077                 case GDK_Page_Up:
3078                     if (event->key.state & GDK_CONTROL_MASK) {
3079                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3080                     } else {
3081                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3082                     }
3083                     break;
3084                 case GDK_Page_Down:
3085                     if (event->key.state & GDK_CONTROL_MASK) {
3086                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3087                     } else {
3088                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3089                     }
3090                     break;
3091                 default:
3092                     break;
3093             }
3094             break;
3095         default:
3096             break;
3097     }
3099     return ret;
3102 /**
3103  * Handle keypress on node; directly called.
3104  */
3105 gboolean node_key(GdkEvent *event)
3107     Inkscape::NodePath::Path *np;
3109     // there is no way to verify nodes so set active_node to nil when deleting!!
3110     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3112     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3113         gint ret = FALSE;
3114         switch (get_group0_keyval (&event->key)) {
3115             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3116             case GDK_BackSpace:
3117                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3118                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3119                 sp_nodepath_update_repr(np, _("Delete node"));
3120                 Inkscape::NodePath::Path::active_node = NULL;
3121                 ret = TRUE;
3122                 break;
3123             case GDK_c:
3124                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3125                 ret = TRUE;
3126                 break;
3127             case GDK_s:
3128                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3129                 ret = TRUE;
3130                 break;
3131             case GDK_y:
3132                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3133                 ret = TRUE;
3134                 break;
3135             case GDK_b:
3136                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3137                 ret = TRUE;
3138                 break;
3139         }
3140         return ret;
3141     }
3142     return FALSE;
3145 /**
3146  * Mouseclick on node callback.
3147  */
3148 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3150    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3152     if (state & GDK_CONTROL_MASK) {
3153         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3155         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3156             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3157                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3158             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3159                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3160             } else {
3161                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3162             }
3163             sp_nodepath_update_repr(nodepath, _("Change node type"));
3164             sp_nodepath_update_statusbar(nodepath);
3166         } else { //ctrl+alt+click: delete node
3167             GList *node_to_delete = NULL;
3168             node_to_delete = g_list_append(node_to_delete, n);
3169             sp_node_delete_preserve(node_to_delete);
3170         }
3172     } else {
3173         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3174     }
3177 /**
3178  * Mouse grabbed node callback.
3179  */
3180 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3182    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3184     if (!n->selected) {
3185         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3186     }
3188     n->is_dragging = true;
3189     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3191     sp_nodepath_remember_origins (n->subpath->nodepath);
3194 /**
3195  * Mouse ungrabbed node callback.
3196  */
3197 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3199    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3201    n->dragging_out = NULL;
3202    n->is_dragging = false;
3203    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3205    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3208 /**
3209  * The point on a line, given by its angle, closest to the given point.
3210  * \param p  A point.
3211  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3212  * \param closest  Pointer to the point struct where the result is stored.
3213  * \todo FIXME: use dot product perhaps?
3214  */
3215 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3217     if (a == HUGE_VAL) { // vertical
3218         *closest = NR::Point(0, (*p)[NR::Y]);
3219     } else {
3220         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3221         (*closest)[NR::Y] = a * (*closest)[NR::X];
3222     }
3225 /**
3226  * Distance from the point to a line given by its angle.
3227  * \param p  A point.
3228  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3229  */
3230 static double point_line_distance(NR::Point *p, double a)
3232     NR::Point c;
3233     point_line_closest(p, a, &c);
3234     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]));
3237 /**
3238  * Callback for node "request" signal.
3239  * \todo fixme: This goes to "moved" event? (lauris)
3240  */
3241 static gboolean
3242 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3244     double yn, xn, yp, xp;
3245     double an, ap, na, pa;
3246     double d_an, d_ap, d_na, d_pa;
3247     gboolean collinear = FALSE;
3248     NR::Point c;
3249     NR::Point pr;
3251    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3253    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3254     if ( (!n->subpath->nodepath->straight_path) &&
3255          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3256            || n->dragging_out ) )
3257     {
3258        NR::Point mouse = (*p);
3260        if (!n->dragging_out) {
3261            // This is the first drag-out event; find out which handle to drag out
3262            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3263            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3265            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3266                return FALSE;
3268            Inkscape::NodePath::NodeSide *opposite;
3269            if (appr_p > appr_n) { // closer to p
3270                n->dragging_out = &n->p;
3271                opposite = &n->n;
3272                n->code = NR_CURVETO;
3273            } else if (appr_p < appr_n) { // closer to n
3274                n->dragging_out = &n->n;
3275                opposite = &n->p;
3276                n->n.other->code = NR_CURVETO;
3277            } else { // p and n nodes are the same
3278                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3279                    n->dragging_out = &n->p;
3280                    opposite = &n->n;
3281                    n->code = NR_CURVETO;
3282                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3283                    n->dragging_out = &n->n;
3284                    opposite = &n->p;
3285                    n->n.other->code = NR_CURVETO;
3286                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3287                    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);
3288                    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);
3289                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3290                        n->dragging_out = &n->n;
3291                        opposite = &n->p;
3292                        n->n.other->code = NR_CURVETO;
3293                    } else { // closer to other's n handle
3294                        n->dragging_out = &n->p;
3295                        opposite = &n->n;
3296                        n->code = NR_CURVETO;
3297                    }
3298                }
3299            }
3301            // if there's another handle, make sure the one we drag out starts parallel to it
3302            if (opposite->pos != n->pos) {
3303                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3304            }
3306            // knots might not be created yet!
3307            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3308            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3309        }
3311        // pass this on to the handle-moved callback
3312        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3313        sp_node_update_handles(n);
3314        return TRUE;
3315    }
3317     if (state & GDK_CONTROL_MASK) { // constrained motion
3319         // calculate relative distances of handles
3320         // n handle:
3321         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3322         xn = n->n.pos[NR::X] - n->pos[NR::X];
3323         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3324         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3325             if (n->n.other) { // if there is the next point
3326                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3327                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3328                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3329             }
3330         }
3331         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3332         if (yn < 0) { xn = -xn; yn = -yn; }
3334         // p handle:
3335         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3336         xp = n->p.pos[NR::X] - n->pos[NR::X];
3337         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3338         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3339             if (n->p.other) {
3340                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3341                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3342                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3343             }
3344         }
3345         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3346         if (yp < 0) { xp = -xp; yp = -yp; }
3348         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3349             // sliding on handles, only if at least one of the handles is non-vertical
3350             // (otherwise it's the same as ctrl+drag anyway)
3352             // calculate angles of the handles
3353             if (xn == 0) {
3354                 if (yn == 0) { // no handle, consider it the continuation of the other one
3355                     an = 0;
3356                     collinear = TRUE;
3357                 }
3358                 else an = 0; // vertical; set the angle to horizontal
3359             } else an = yn/xn;
3361             if (xp == 0) {
3362                 if (yp == 0) { // no handle, consider it the continuation of the other one
3363                     ap = an;
3364                 }
3365                 else ap = 0; // vertical; set the angle to horizontal
3366             } else  ap = yp/xp;
3368             if (collinear) an = ap;
3370             // angles of the perpendiculars; HUGE_VAL means vertical
3371             if (an == 0) na = HUGE_VAL; else na = -1/an;
3372             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3374             // mouse point relative to the node's original pos
3375             pr = (*p) - n->origin;
3377             // distances to the four lines (two handles and two perpendiculars)
3378             d_an = point_line_distance(&pr, an);
3379             d_na = point_line_distance(&pr, na);
3380             d_ap = point_line_distance(&pr, ap);
3381             d_pa = point_line_distance(&pr, pa);
3383             // find out which line is the closest, save its closest point in c
3384             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3385                 point_line_closest(&pr, an, &c);
3386             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3387                 point_line_closest(&pr, ap, &c);
3388             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3389                 point_line_closest(&pr, na, &c);
3390             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3391                 point_line_closest(&pr, pa, &c);
3392             }
3394             // move the node to the closest point
3395             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3396                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3397                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3399         } else {  // constraining to hor/vert
3401             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3402                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3403             } else { // snap to vert
3404                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3405             }
3406         }
3407     } else { // move freely
3408         if (n->is_dragging) {
3409             if (state & GDK_MOD1_MASK) { // sculpt
3410                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3411             } else {
3412                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3413                                             (*p)[NR::X] - n->pos[NR::X],
3414                                             (*p)[NR::Y] - n->pos[NR::Y],
3415                                             (state & GDK_SHIFT_MASK) == 0);
3416             }
3417         }
3418     }
3420     n->subpath->nodepath->desktop->scroll_to_point(p);
3422     return TRUE;
3425 /**
3426  * Node handle clicked callback.
3427  */
3428 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3430    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3432     if (state & GDK_CONTROL_MASK) { // "delete" handle
3433         if (n->p.knot == knot) {
3434             n->p.pos = n->pos;
3435         } else if (n->n.knot == knot) {
3436             n->n.pos = n->pos;
3437         }
3438         sp_node_update_handles(n);
3439         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3440         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3441         sp_nodepath_update_statusbar(nodepath);
3443     } else { // just select or add to selection, depending in Shift
3444         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3445     }
3448 /**
3449  * Node handle grabbed callback.
3450  */
3451 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3453    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3455     if (!n->selected) {
3456         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3457     }
3459     // remember the origin point of the handle
3460     if (n->p.knot == knot) {
3461         n->p.origin_radial = n->p.pos - n->pos;
3462     } else if (n->n.knot == knot) {
3463         n->n.origin_radial = n->n.pos - n->pos;
3464     } else {
3465         g_assert_not_reached();
3466     }
3468     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3471 /**
3472  * Node handle ungrabbed callback.
3473  */
3474 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3476    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3478     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3479     if (n->p.knot == knot) {
3480         n->p.origin_radial.a = 0;
3481         sp_knot_set_position(knot, &n->p.pos, state);
3482     } else if (n->n.knot == knot) {
3483         n->n.origin_radial.a = 0;
3484         sp_knot_set_position(knot, &n->n.pos, state);
3485     } else {
3486         g_assert_not_reached();
3487     }
3489     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3492 /**
3493  * Node handle "request" signal callback.
3494  */
3495 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3497     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3499     Inkscape::NodePath::NodeSide *me, *opposite;
3500     gint which;
3501     if (n->p.knot == knot) {
3502         me = &n->p;
3503         opposite = &n->n;
3504         which = -1;
3505     } else if (n->n.knot == knot) {
3506         me = &n->n;
3507         opposite = &n->p;
3508         which = 1;
3509     } else {
3510         me = opposite = NULL;
3511         which = 0;
3512         g_assert_not_reached();
3513     }
3515     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3517     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3519     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3520         /* We are smooth node adjacent with line */
3521         NR::Point const delta = *p - n->pos;
3522         NR::Coord const len = NR::L2(delta);
3523         Inkscape::NodePath::Node *othernode = opposite->other;
3524         NR::Point const ndelta = n->pos - othernode->pos;
3525         NR::Coord const linelen = NR::L2(ndelta);
3526         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3527             NR::Coord const scal = dot(delta, ndelta) / linelen;
3528             (*p) = n->pos + (scal / linelen) * ndelta;
3529         }
3530         *p = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), n->subpath->nodepath->item).getPoint();
3531     } else {
3532         *p = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, n->subpath->nodepath->item).getPoint();
3533     }
3535     sp_node_adjust_handle(n, -which);
3537     return FALSE;
3540 /**
3541  * Node handle moved callback.
3542  */
3543 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3545    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3547    Inkscape::NodePath::NodeSide *me;
3548    Inkscape::NodePath::NodeSide *other;
3549     if (n->p.knot == knot) {
3550         me = &n->p;
3551         other = &n->n;
3552     } else if (n->n.knot == knot) {
3553         me = &n->n;
3554         other = &n->p;
3555     } else {
3556         me = NULL;
3557         other = NULL;
3558         g_assert_not_reached();
3559     }
3561     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3562     Radial rme(me->pos - n->pos);
3563     Radial rother(other->pos - n->pos);
3564     Radial rnew(*p - n->pos);
3566     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3567         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3568         /* 0 interpreted as "no snapping". */
3570         // The closest PI/snaps angle, starting from zero.
3571         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3572         if (me->origin_radial.a == HUGE_VAL) {
3573             // ortho doesn't exist: original handle was zero length.
3574             rnew.a = a_snapped;
3575         } else {
3576             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3577              * its opposite and perpendiculars). */
3578             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3580             // Snap to the closest.
3581             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3582                        ? a_snapped
3583                        : a_ortho );
3584         }
3585     }
3587     if (state & GDK_MOD1_MASK) {
3588         // lock handle length
3589         rnew.r = me->origin_radial.r;
3590     }
3592     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3593         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3594         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3595         rother.a += rnew.a - rme.a;
3596         other->pos = NR::Point(rother) + n->pos;
3597         if (other->knot) {
3598             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3599             sp_knot_moveto(other->knot, &other->pos);
3600         }
3601     }
3603     me->pos = NR::Point(rnew) + n->pos;
3604     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3606     // move knot, but without emitting the signal:
3607     // we cannot emit a "moved" signal because we're now processing it
3608     sp_knot_moveto(me->knot, &(me->pos));
3610     update_object(n->subpath->nodepath);
3612     /* status text */
3613     SPDesktop *desktop = n->subpath->nodepath->desktop;
3614     if (!desktop) return;
3615     SPEventContext *ec = desktop->event_context;
3616     if (!ec) return;
3617     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3618     if (!mc) return;
3620     double degrees = 180 / M_PI * rnew.a;
3621     if (degrees > 180) degrees -= 360;
3622     if (degrees < -180) degrees += 360;
3623     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3624         degrees = angle_to_compass (degrees);
3626     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3628     mc->setF(Inkscape::NORMAL_MESSAGE,
3629          _("<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);
3631     g_string_free(length, TRUE);
3634 /**
3635  * Node handle event callback.
3636  */
3637 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3639     gboolean ret = FALSE;
3640     switch (event->type) {
3641         case GDK_KEY_PRESS:
3642             switch (get_group0_keyval (&event->key)) {
3643                 case GDK_space:
3644                     if (event->key.state & GDK_BUTTON1_MASK) {
3645                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3646                         stamp_repr(nodepath);
3647                         ret = TRUE;
3648                     }
3649                     break;
3650                 default:
3651                     break;
3652             }
3653             break;
3654         case GDK_ENTER_NOTIFY:
3655             // we use an experimentally determined threshold that seems to work fine
3656             if (NR::L2(n->pos - knot->pos) < 0.75)
3657                 Inkscape::NodePath::Path::active_node = n;
3658             break;
3659         case GDK_LEAVE_NOTIFY:
3660             // we use an experimentally determined threshold that seems to work fine
3661             if (NR::L2(n->pos - knot->pos) < 0.75)
3662                 Inkscape::NodePath::Path::active_node = NULL;
3663             break;
3664         default:
3665             break;
3666     }
3668     return ret;
3671 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3672                                  Radial &rme, Radial &rother, gboolean const both)
3674     rme.a += angle;
3675     if ( both
3676          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3677          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3678     {
3679         rother.a += angle;
3680     }
3683 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3684                                         Radial &rme, Radial &rother, gboolean const both)
3686     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3688     gdouble r;
3689     if ( both
3690          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3691          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3692     {
3693         r = MAX(rme.r, rother.r);
3694     } else {
3695         r = rme.r;
3696     }
3698     gdouble const weird_angle = atan2(norm_angle, r);
3699 /* Bulia says norm_angle is just the visible distance that the
3700  * object's end must travel on the screen.  Left as 'angle' for want of
3701  * a better name.*/
3703     rme.a += weird_angle;
3704     if ( both
3705          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3706          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3707     {
3708         rother.a += weird_angle;
3709     }
3712 /**
3713  * Rotate one node.
3714  */
3715 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3717     Inkscape::NodePath::NodeSide *me, *other;
3718     bool both = false;
3720     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3721     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3723     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3724         me = &(n->p);
3725         other = &(n->n);
3726     } else if (!n->p.other) {
3727         me = &(n->n);
3728         other = &(n->p);
3729     } else {
3730         if (which > 0) { // right handle
3731             if (xn > xp) {
3732                 me = &(n->n);
3733                 other = &(n->p);
3734             } else {
3735                 me = &(n->p);
3736                 other = &(n->n);
3737             }
3738         } else if (which < 0){ // left handle
3739             if (xn <= xp) {
3740                 me = &(n->n);
3741                 other = &(n->p);
3742             } else {
3743                 me = &(n->p);
3744                 other = &(n->n);
3745             }
3746         } else { // both handles
3747             me = &(n->n);
3748             other = &(n->p);
3749             both = true;
3750         }
3751     }
3753     Radial rme(me->pos - n->pos);
3754     Radial rother(other->pos - n->pos);
3756     if (screen) {
3757         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3758     } else {
3759         node_rotate_one_internal (*n, angle, rme, rother, both);
3760     }
3762     me->pos = n->pos + NR::Point(rme);
3764     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3765         other->pos =  n->pos + NR::Point(rother);
3766     }
3768     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3769     // so here we just move all the knots without emitting move signals, for speed
3770     sp_node_update_handles(n, false);
3773 /**
3774  * Rotate selected nodes.
3775  */
3776 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3778     if (!nodepath || !nodepath->selected) return;
3780     if (g_list_length(nodepath->selected) == 1) {
3781        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3782         node_rotate_one (n, angle, which, screen);
3783     } else {
3784        // rotate as an object:
3786         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3787         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3788         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3789             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3790             box.expandTo (n->pos); // contain all selected nodes
3791         }
3793         gdouble rot;
3794         if (screen) {
3795             gdouble const zoom = nodepath->desktop->current_zoom();
3796             gdouble const zmove = angle / zoom;
3797             gdouble const r = NR::L2(box.max() - box.midpoint());
3798             rot = atan2(zmove, r);
3799         } else {
3800             rot = angle;
3801         }
3803         NR::Point rot_center;
3804         if (Inkscape::NodePath::Path::active_node == NULL)
3805             rot_center = box.midpoint();
3806         else
3807             rot_center = Inkscape::NodePath::Path::active_node->pos;
3809         NR::Matrix t =
3810             NR::Matrix (NR::translate(-rot_center)) *
3811             NR::Matrix (NR::rotate(rot)) *
3812             NR::Matrix (NR::translate(rot_center));
3814         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3815             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3816             n->pos *= t;
3817             n->n.pos *= t;
3818             n->p.pos *= t;
3819             sp_node_update_handles(n, false);
3820         }
3821     }
3823     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3826 /**
3827  * Scale one node.
3828  */
3829 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3831     bool both = false;
3832     Inkscape::NodePath::NodeSide *me, *other;
3834     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3835     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3837     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3838         me = &(n->p);
3839         other = &(n->n);
3840         n->code = NR_CURVETO;
3841     } else if (!n->p.other) {
3842         me = &(n->n);
3843         other = &(n->p);
3844         if (n->n.other)
3845             n->n.other->code = NR_CURVETO;
3846     } else {
3847         if (which > 0) { // right handle
3848             if (xn > xp) {
3849                 me = &(n->n);
3850                 other = &(n->p);
3851                 if (n->n.other)
3852                     n->n.other->code = NR_CURVETO;
3853             } else {
3854                 me = &(n->p);
3855                 other = &(n->n);
3856                 n->code = NR_CURVETO;
3857             }
3858         } else if (which < 0){ // left handle
3859             if (xn <= xp) {
3860                 me = &(n->n);
3861                 other = &(n->p);
3862                 if (n->n.other)
3863                     n->n.other->code = NR_CURVETO;
3864             } else {
3865                 me = &(n->p);
3866                 other = &(n->n);
3867                 n->code = NR_CURVETO;
3868             }
3869         } else { // both handles
3870             me = &(n->n);
3871             other = &(n->p);
3872             both = true;
3873             n->code = NR_CURVETO;
3874             if (n->n.other)
3875                 n->n.other->code = NR_CURVETO;
3876         }
3877     }
3879     Radial rme(me->pos - n->pos);
3880     Radial rother(other->pos - n->pos);
3882     rme.r += grow;
3883     if (rme.r < 0) rme.r = 0;
3884     if (rme.a == HUGE_VAL) {
3885         if (me->other) { // if direction is unknown, initialize it towards the next node
3886             Radial rme_next(me->other->pos - n->pos);
3887             rme.a = rme_next.a;
3888         } else { // if there's no next, initialize to 0
3889             rme.a = 0;
3890         }
3891     }
3892     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3893         rother.r += grow;
3894         if (rother.r < 0) rother.r = 0;
3895         if (rother.a == HUGE_VAL) {
3896             rother.a = rme.a + M_PI;
3897         }
3898     }
3900     me->pos = n->pos + NR::Point(rme);
3902     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3903         other->pos = n->pos + NR::Point(rother);
3904     }
3906     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3907     // so here we just move all the knots without emitting move signals, for speed
3908     sp_node_update_handles(n, false);
3911 /**
3912  * Scale selected nodes.
3913  */
3914 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3916     if (!nodepath || !nodepath->selected) return;
3918     if (g_list_length(nodepath->selected) == 1) {
3919         // scale handles of the single selected node
3920         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3921         node_scale_one (n, grow, which);
3922     } else {
3923         // scale nodes as an "object":
3925         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3926         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3927         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3928             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3929             box.expandTo (n->pos); // contain all selected nodes
3930         }
3932         double scale = (box.maxExtent() + grow)/box.maxExtent();
3934         NR::Point scale_center;
3935         if (Inkscape::NodePath::Path::active_node == NULL)
3936             scale_center = box.midpoint();
3937         else
3938             scale_center = Inkscape::NodePath::Path::active_node->pos;
3940         NR::Matrix t =
3941             NR::Matrix (NR::translate(-scale_center)) *
3942             NR::Matrix (NR::scale(scale, scale)) *
3943             NR::Matrix (NR::translate(scale_center));
3945         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3946             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3947             n->pos *= t;
3948             n->n.pos *= t;
3949             n->p.pos *= t;
3950             sp_node_update_handles(n, false);
3951         }
3952     }
3954     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3957 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3959     if (!nodepath) return;
3960     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3963 /**
3964  * Flip selected nodes horizontally/vertically.
3965  */
3966 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
3968     if (!nodepath || !nodepath->selected) return;
3970     if (g_list_length(nodepath->selected) == 1 && !center) {
3971         // flip handles of the single selected node
3972         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3973         double temp = n->p.pos[axis];
3974         n->p.pos[axis] = n->n.pos[axis];
3975         n->n.pos[axis] = temp;
3976         sp_node_update_handles(n, false);
3977     } else {
3978         // scale nodes as an "object":
3980         NR::Rect box = sp_node_selected_bbox (nodepath);
3981         if (!center) {
3982             center = box.midpoint();
3983         }
3984         NR::Matrix t =
3985             NR::Matrix (NR::translate(- *center)) *
3986             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3987             NR::Matrix (NR::translate(*center));
3989         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3990             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3991             n->pos *= t;
3992             n->n.pos *= t;
3993             n->p.pos *= t;
3994             sp_node_update_handles(n, false);
3995         }
3996     }
3998     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4001 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4003     g_assert (nodepath->selected);
4005     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4006     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4007     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4008         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4009         box.expandTo (n->pos); // contain all selected nodes
4010     }
4011     return box;
4014 //-----------------------------------------------
4015 /**
4016  * Return new subpath under given nodepath.
4017  */
4018 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4020     g_assert(nodepath);
4021     g_assert(nodepath->desktop);
4023    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4025     s->nodepath = nodepath;
4026     s->closed = FALSE;
4027     s->nodes = NULL;
4028     s->first = NULL;
4029     s->last = NULL;
4031     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4032     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4033     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4035     return s;
4038 /**
4039  * Destroy nodes in subpath, then subpath itself.
4040  */
4041 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4043     g_assert(subpath);
4044     g_assert(subpath->nodepath);
4045     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4047     while (subpath->nodes) {
4048         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4049     }
4051     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4053     g_free(subpath);
4056 /**
4057  * Link head to tail in subpath.
4058  */
4059 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4061     g_assert(!sp->closed);
4062     g_assert(sp->last != sp->first);
4063     g_assert(sp->first->code == NR_MOVETO);
4065     sp->closed = TRUE;
4067     //Link the head to the tail
4068     sp->first->p.other = sp->last;
4069     sp->last->n.other  = sp->first;
4070     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4071     sp->first          = sp->last;
4073     //Remove the extra end node
4074     sp_nodepath_node_destroy(sp->last->n.other);
4077 /**
4078  * Open closed (loopy) subpath at node.
4079  */
4080 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4082     g_assert(sp->closed);
4083     g_assert(n->subpath == sp);
4084     g_assert(sp->first == sp->last);
4086     /* We create new startpoint, current node will become last one */
4088    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4089                                                 &n->pos, &n->pos, &n->n.pos);
4092     sp->closed        = FALSE;
4094     //Unlink to make a head and tail
4095     sp->first         = new_path;
4096     sp->last          = n;
4097     n->n.other        = NULL;
4098     new_path->p.other = NULL;
4101 /**
4102  * Return new node in subpath with given properties.
4103  * \param pos Position of node.
4104  * \param ppos Handle position in previous direction
4105  * \param npos Handle position in previous direction
4106  */
4107 Inkscape::NodePath::Node *
4108 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)
4110     g_assert(sp);
4111     g_assert(sp->nodepath);
4112     g_assert(sp->nodepath->desktop);
4114     if (nodechunk == NULL)
4115         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4117     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4119     n->subpath  = sp;
4121     if (type != Inkscape::NodePath::NODE_NONE) {
4122         // use the type from sodipodi:nodetypes
4123         n->type = type;
4124     } else {
4125         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4126             // points are (almost) collinear
4127             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4128                 // endnode, or a node with a retracted handle
4129                 n->type = Inkscape::NodePath::NODE_CUSP;
4130             } else {
4131                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4132             }
4133         } else {
4134             n->type = Inkscape::NodePath::NODE_CUSP;
4135         }
4136     }
4138     n->code     = code;
4139     n->selected = FALSE;
4140     n->pos      = *pos;
4141     n->p.pos    = *ppos;
4142     n->n.pos    = *npos;
4144     n->dragging_out = NULL;
4146     Inkscape::NodePath::Node *prev;
4147     if (next) {
4148         //g_assert(g_list_find(sp->nodes, next));
4149         prev = next->p.other;
4150     } else {
4151         prev = sp->last;
4152     }
4154     if (prev)
4155         prev->n.other = n;
4156     else
4157         sp->first = n;
4159     if (next)
4160         next->p.other = n;
4161     else
4162         sp->last = n;
4164     n->p.other = prev;
4165     n->n.other = next;
4167     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"));
4168     sp_knot_set_position(n->knot, pos, 0);
4170     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4171     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4172     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4173     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4174     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4175     sp_knot_update_ctrl(n->knot);
4177     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4178     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4179     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4180     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4181     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4182     sp_knot_show(n->knot);
4184     // We only create handle knots and lines on demand
4185     n->p.knot = NULL;
4186     n->p.line = NULL;
4187     n->n.knot = NULL;
4188     n->n.line = NULL;
4190     sp->nodes = g_list_prepend(sp->nodes, n);
4192     return n;
4195 /**
4196  * Destroy node and its knots, link neighbors in subpath.
4197  */
4198 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4200     g_assert(node);
4201     g_assert(node->subpath);
4202     g_assert(SP_IS_KNOT(node->knot));
4204    Inkscape::NodePath::SubPath *sp = node->subpath;
4206     if (node->selected) { // first, deselect
4207         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4208         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4209     }
4211     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4213     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4214     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4215     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4216     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4217     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4218     g_object_unref(G_OBJECT(node->knot));
4220     if (node->p.knot) {
4221         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4222         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4223         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4224         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4225         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4226         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4227         g_object_unref(G_OBJECT(node->p.knot));
4228         node->p.knot = NULL;
4229     }
4231     if (node->n.knot) {
4232         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4233         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4234         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4235         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4236         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4237         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4238         g_object_unref(G_OBJECT(node->n.knot));
4239         node->n.knot = NULL;
4240     }
4242     if (node->p.line)
4243         gtk_object_destroy(GTK_OBJECT(node->p.line));
4244     if (node->n.line)
4245         gtk_object_destroy(GTK_OBJECT(node->n.line));
4247     if (sp->nodes) { // there are others nodes on the subpath
4248         if (sp->closed) {
4249             if (sp->first == node) {
4250                 g_assert(sp->last == node);
4251                 sp->first = node->n.other;
4252                 sp->last = sp->first;
4253             }
4254             node->p.other->n.other = node->n.other;
4255             node->n.other->p.other = node->p.other;
4256         } else {
4257             if (sp->first == node) {
4258                 sp->first = node->n.other;
4259                 sp->first->code = NR_MOVETO;
4260             }
4261             if (sp->last == node) sp->last = node->p.other;
4262             if (node->p.other) node->p.other->n.other = node->n.other;
4263             if (node->n.other) node->n.other->p.other = node->p.other;
4264         }
4265     } else { // this was the last node on subpath
4266         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4267     }
4269     g_mem_chunk_free(nodechunk, node);
4272 /**
4273  * Returns one of the node's two sides.
4274  * \param which Indicates which side.
4275  * \return Pointer to previous node side if which==-1, next if which==1.
4276  */
4277 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4279     g_assert(node);
4281     switch (which) {
4282         case -1:
4283             return &node->p;
4284         case 1:
4285             return &node->n;
4286         default:
4287             break;
4288     }
4290     g_assert_not_reached();
4292     return NULL;
4295 /**
4296  * Return the other side of the node, given one of its sides.
4297  */
4298 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4300     g_assert(node);
4302     if (me == &node->p) return &node->n;
4303     if (me == &node->n) return &node->p;
4305     g_assert_not_reached();
4307     return NULL;
4310 /**
4311  * Return NRPathcode on the given side of the node.
4312  */
4313 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4315     g_assert(node);
4317     if (me == &node->p) {
4318         if (node->p.other) return (NRPathcode)node->code;
4319         return NR_MOVETO;
4320     }
4322     if (me == &node->n) {
4323         if (node->n.other) return (NRPathcode)node->n.other->code;
4324         return NR_MOVETO;
4325     }
4327     g_assert_not_reached();
4329     return NR_END;
4332 /**
4333  * Return node with the given index
4334  */
4335 Inkscape::NodePath::Node *
4336 sp_nodepath_get_node_by_index(int index)
4338     Inkscape::NodePath::Node *e = NULL;
4340     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4341     if (!nodepath) {
4342         return e;
4343     }
4345     //find segment
4346     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4348         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4349         int n = g_list_length(sp->nodes);
4350         if (sp->closed) {
4351             n++;
4352         }
4354         //if the piece belongs to this subpath grab it
4355         //otherwise move onto the next subpath
4356         if (index < n) {
4357             e = sp->first;
4358             for (int i = 0; i < index; ++i) {
4359                 e = e->n.other;
4360             }
4361             break;
4362         } else {
4363             if (sp->closed) {
4364                 index -= (n+1);
4365             } else {
4366                 index -= n;
4367             }
4368         }
4369     }
4371     return e;
4374 /**
4375  * Returns plain text meaning of node type.
4376  */
4377 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4379     unsigned retracted = 0;
4380     bool endnode = false;
4382     for (int which = -1; which <= 1; which += 2) {
4383         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4384         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4385             retracted ++;
4386         if (!side->other)
4387             endnode = true;
4388     }
4390     if (retracted == 0) {
4391         if (endnode) {
4392                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4393                 return _("end node");
4394         } else {
4395             switch (node->type) {
4396                 case Inkscape::NodePath::NODE_CUSP:
4397                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4398                     return _("cusp");
4399                 case Inkscape::NodePath::NODE_SMOOTH:
4400                     // TRANSLATORS: "smooth" is an adjective here
4401                     return _("smooth");
4402                 case Inkscape::NodePath::NODE_SYMM:
4403                     return _("symmetric");
4404             }
4405         }
4406     } else if (retracted == 1) {
4407         if (endnode) {
4408             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4409             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4410         } else {
4411             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4412         }
4413     } else {
4414         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4415     }
4417     return NULL;
4420 /**
4421  * Handles content of statusbar as long as node tool is active.
4422  */
4423 void
4424 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4426     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");
4427     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4429     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4430     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4431     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4432     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4434     SPDesktop *desktop = NULL;
4435     if (nodepath) {
4436         desktop = nodepath->desktop;
4437     } else {
4438         desktop = SP_ACTIVE_DESKTOP;
4439     }
4441     SPEventContext *ec = desktop->event_context;
4442     if (!ec) return;
4443     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4444     if (!mc) return;
4446     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4448     if (selected_nodes == 0) {
4449         Inkscape::Selection *sel = desktop->selection;
4450         if (!sel || sel->isEmpty()) {
4451             mc->setF(Inkscape::NORMAL_MESSAGE,
4452                      _("Select a single object to edit its nodes or handles."));
4453         } else {
4454             if (nodepath) {
4455             mc->setF(Inkscape::NORMAL_MESSAGE,
4456                      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.",
4457                               "<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.",
4458                               total_nodes),
4459                      total_nodes);
4460             } else {
4461                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4462                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4463                 } else {
4464                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4465                 }
4466             }
4467         }
4468     } else if (nodepath && selected_nodes == 1) {
4469         mc->setF(Inkscape::NORMAL_MESSAGE,
4470                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4471                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4472                           total_nodes),
4473                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4474     } else {
4475         if (selected_subpaths > 1) {
4476             mc->setF(Inkscape::NORMAL_MESSAGE,
4477                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4478                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4479                               total_nodes),
4480                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4481         } else {
4482             mc->setF(Inkscape::NORMAL_MESSAGE,
4483                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4484                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4485                               total_nodes),
4486                      selected_nodes, total_nodes, when_selected);
4487         }
4488     }
4491 /*
4492  * returns a *copy* of the curve of that object.
4493  */
4494 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4495     if (!object)
4496         return NULL;
4498     SPCurve *curve = NULL;
4499     if (SP_IS_PATH(object)) {
4500         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4501         curve = sp_curve_copy(curve_new);
4502     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4503         const gchar *svgd = object->repr->attribute(key);
4504         if (svgd) {
4505             NArtBpath *bpath = sp_svg_read_path(svgd);
4506             SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
4507             if (curve_new) {
4508                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4509             } else {
4510                 g_free(bpath);
4511             }
4512         }
4513     }
4515     return curve;
4518 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4519     if (!np || !np->object || !curve)
4520         return;
4522     if (SP_IS_PATH(np->object)) {
4523         if (SP_SHAPE(np->object)->path_effect_href) {
4524             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4525         } else {
4526             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4527         }
4528     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4529         // FIXME: this writing to string and then reading from string is bound to be slow.
4530         // create a method to convert from curve directly to 2geom...
4531         gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
4532         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4533         g_free(svgpath);
4535         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4536     }
4539 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4540     np->show_helperpath = show;
4543 /* this function does not work yet */
4544 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4545     np->straight_path = true;
4546     np->show_handles = false;
4547     g_message("add code to make the path straight.");
4548     // do sp_nodepath_convert_node_type on all nodes?
4549     // search for this text !!!   "Make selected segments lines"
4553 /*
4554   Local Variables:
4555   mode:c++
4556   c-file-style:"stroustrup"
4557   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4558   indent-tabs-mode:nil
4559   fill-column:99
4560   End:
4561 */
4562 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :