Code

924d57595be1993cd3452c8e60cf5d6f5fe82d7e
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include <glibmm/i18n.h>
23 #include "libnr/n-art-bpath.h"
24 #include "libnr/nr-path.h"
25 #include "helper/units.h"
26 #include "knot.h"
27 #include "inkscape.h"
28 #include "document.h"
29 #include "sp-namedview.h"
30 #include "desktop.h"
31 #include "desktop-handles.h"
32 #include "snap.h"
33 #include "message-stack.h"
34 #include "message-context.h"
35 #include "node-context.h"
36 #include "shape-editor.h"
37 #include "selection-chemistry.h"
38 #include "selection.h"
39 #include "xml/repr.h"
40 #include "prefs-utils.h"
41 #include "sp-metrics.h"
42 #include "sp-path.h"
43 #include "libnr/nr-matrix-ops.h"
44 #include "splivarot.h"
45 #include "svg/svg.h"
46 #include "verbs.h"
47 #include "display/bezier-utils.h"
48 #include <vector>
49 #include <algorithm>
50 #include <cstring>
51 #include <string>
52 #include "live_effects/lpeobject.h"
53 #include "live_effects/parameter/parameter.h"
54 #include "util/mathfns.h"
55 #include "display/snap-indicator.h"
57 class NR::Matrix;
59 /// \todo
60 /// evil evil evil. FIXME: conflict of two different Path classes!
61 /// There is a conflict in the namespace between two classes named Path.
62 /// #include "sp-flowtext.h"
63 /// #include "sp-flowregion.h"
65 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
66 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
67 GType sp_flowregion_get_type (void);
68 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
69 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
70 GType sp_flowtext_get_type (void);
71 // end evil workaround
73 #include "helper/stlport.h"
76 /// \todo fixme: Implement these via preferences */
78 #define NODE_FILL          0xbfbfbf00
79 #define NODE_STROKE        0x000000ff
80 #define NODE_FILL_HI       0xff000000
81 #define NODE_STROKE_HI     0x000000ff
82 #define NODE_FILL_SEL      0x0000ffff
83 #define NODE_STROKE_SEL    0x000000ff
84 #define NODE_FILL_SEL_HI   0xff000000
85 #define NODE_STROKE_SEL_HI 0x000000ff
86 #define KNOT_FILL          0xffffffff
87 #define KNOT_STROKE        0x000000ff
88 #define KNOT_FILL_HI       0xff000000
89 #define KNOT_STROKE_HI     0x000000ff
91 static GMemChunk *nodechunk = NULL;
93 /* Creation from object */
95 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
96 static gchar *parse_nodetypes(gchar const *types, gint length);
98 /* Object updating */
100 static void stamp_repr(Inkscape::NodePath::Path *np);
101 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
102 static gchar *create_typestr(Inkscape::NodePath::Path *np);
104 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
106 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
108 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
110 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
112 /* Adjust handle placement, if the node or the other handle is moved */
113 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
114 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
116 /* Node event callbacks */
117 static void node_clicked(SPKnot *knot, guint state, gpointer data);
118 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
119 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
120 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
122 /* Handle event callbacks */
123 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
124 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
125 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
126 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
127 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
128 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
130 /* Constructors and destructors */
132 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
133 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
134 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
135 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
136 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
137                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
138 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
140 /* Helpers */
142 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
143 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
144 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
146 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
147 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
149 // active_node indicates mouseover node
150 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
152 /**
153  * \brief Creates new nodepath from item
154  */
155 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
157     Inkscape::XML::Node *repr = object->repr;
159     /** \todo
160      * FIXME: remove this. We don't want to edit paths inside flowtext.
161      * Instead we will build our flowtext with cloned paths, so that the
162      * real paths are outside the flowtext and thus editable as usual.
163      */
164     if (SP_IS_FLOWTEXT(object)) {
165         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
166             if SP_IS_FLOWREGION(child) {
167                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
168                 if (grandchild && SP_IS_PATH(grandchild)) {
169                     object = SP_ITEM(grandchild);
170                     break;
171                 }
172             }
173         }
174     }
176     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
178     if (curve == NULL)
179         return NULL;
181     NArtBpath *bpath = sp_curve_first_bpath(curve);
182     gint length = curve->end;
183     if (length == 0) {
184         sp_curve_unref(curve);
185         return NULL; // prevent crash for one-node paths
186     }
188     //Create new nodepath
189     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
190     if (!np) {
191         sp_curve_unref(curve);
192         return NULL;
193     }
195     // Set defaults
196     np->desktop     = desktop;
197     np->object      = object;
198     np->subpaths    = NULL;
199     np->selected    = NULL;
200     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
201     np->livarot_path = NULL;
202     np->local_change = 0;
203     np->show_handles = show_handles;
204     np->helper_path = NULL;
205     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
206     np->helperpath_width = 1.0;
207     np->curve = sp_curve_copy(curve);
208     np->show_helperpath = prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1;
209     np->straight_path = false;
210     if (IS_LIVEPATHEFFECT(object) && item) {
211         np->item = item;
212     } else {
213         np->item = SP_ITEM(object);
214     }
216     // we need to update item's transform from the repr here,
217     // because they may be out of sync when we respond
218     // to a change in repr by regenerating nodepath     --bb
219     sp_object_read_attr(SP_OBJECT(np->item), "transform");
221     np->i2d  = sp_item_i2d_affine(np->item);
222     np->d2i  = np->i2d.inverse();
224     np->repr = repr;
225     if (repr_key_in) { // apparantly the object is an LPEObject
226         np->repr_key = g_strdup(repr_key_in);
227         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
228         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
229         if (lpeparam) {
230             lpeparam->param_setup_nodepath(np);
231         }
232     } else {
233         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
234         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
235             np->repr_key = g_strdup("inkscape:original-d");
237             LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(SP_LPE_ITEM(np->object));
238             if (lpeobj && lpeobj->lpe) {
239                 lpeobj->lpe->setup_nodepath(np);
240             }
241         } else {
242             np->repr_key = g_strdup("d");
243         }
244     }
246     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
247     gchar *typestr = parse_nodetypes(nodetypes, length);
249     // create the subpath(s) from the bpath
250     NArtBpath *b = bpath;
251     while (b->code != NR_END) {
252         b = subpath_from_bpath(np, b, typestr + (b - bpath));
253     }
255     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
256     np->subpaths = g_list_reverse(np->subpaths);
258     g_free(typestr);
259     sp_curve_unref(curve);
261     // create the livarot representation from the same item
262     sp_nodepath_ensure_livarot_path(np);
264     // Draw helper curve
265     if (np->show_helperpath) {
266         SPCurve *helper_curve = sp_curve_copy(np->curve);
267         sp_curve_transform(helper_curve, np->i2d );
268         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
269         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);
270         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
271         sp_canvas_item_show(np->helper_path);
272         sp_curve_unref(helper_curve);
273     }
275     return np;
278 /**
279  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
280  */
281 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
283     if (!np)  //soft fail, like delete
284         return;
286     while (np->subpaths) {
287         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
288     }
290     //Inform the ShapeEditor that made me, if any, that I am gone.
291     if (np->shape_editor)
292         np->shape_editor->nodepath_destroyed();
294     g_assert(!np->selected);
296     if (np->livarot_path) {
297         delete np->livarot_path;
298         np->livarot_path = NULL;
299     }
301     if (np->helper_path) {
302         GtkObject *temp = np->helper_path;
303         np->helper_path = NULL;
304         gtk_object_destroy(temp);
305     }
306     if (np->curve) {
307         sp_curve_unref(np->curve);
308         np->curve = NULL;
309     }
311     if (np->repr_key) {
312         g_free(np->repr_key);
313         np->repr_key = NULL;
314     }
315     if (np->repr_nodetypes_key) {
316         g_free(np->repr_nodetypes_key);
317         np->repr_nodetypes_key = NULL;
318     }
320     np->desktop = NULL;
322     g_free(np);
326 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
328     if (np && np->livarot_path == NULL) {
329         SPCurve *curve = create_curve(np);
330         NArtBpath *bpath = SP_CURVE_BPATH(curve);
331         np->livarot_path = bpath_to_Path(bpath);
333         if (np->livarot_path)
334             np->livarot_path->ConvertWithBackData(0.01);
336         sp_curve_unref(curve);
337     }
341 /**
342  *  Return the node count of a given NodeSubPath.
343  */
344 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
346     if (!subpath)
347         return 0;
348     gint nodeCount = g_list_length(subpath->nodes);
349     return nodeCount;
352 /**
353  *  Return the node count of a given NodePath.
354  */
355 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
357     if (!np)
358         return 0;
359     gint nodeCount = 0;
360     for (GList *item = np->subpaths ; item ; item=item->next) {
361        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
362         nodeCount += g_list_length(subpath->nodes);
363     }
364     return nodeCount;
367 /**
368  *  Return the subpath count of a given NodePath.
369  */
370 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
372     if (!np)
373         return 0;
374     return g_list_length (np->subpaths);
377 /**
378  *  Return the selected node count of a given NodePath.
379  */
380 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
382     if (!np)
383         return 0;
384     return g_list_length (np->selected);
387 /**
388  *  Return the number of subpaths where nodes are selected in a given NodePath.
389  */
390 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
392     if (!np)
393         return 0;
394     if (!np->selected)
395         return 0;
396     if (!np->selected->next)
397         return 1;
398     gint count = 0;
399     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
400         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
401         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
402             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
403             if (node->selected) {
404                 count ++;
405                 break;
406             }
407         }
408     }
409     return count;
412 /**
413  * Clean up a nodepath after editing.
414  *
415  * Currently we are deleting trivial subpaths.
416  */
417 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
419     GList *badSubPaths = NULL;
421     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
422     for (GList *l = nodepath->subpaths; l ; l=l->next) {
423        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
424        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
425             badSubPaths = g_list_append(badSubPaths, sp);
426     }
428     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
429     //also removes the subpath from nodepath->subpaths
430     for (GList *l = badSubPaths; l ; l=l->next) {
431        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
432         sp_nodepath_subpath_destroy(sp);
433     }
435     g_list_free(badSubPaths);
438 /**
439  * Create new nodepath from b, make it subpath of np.
440  * \param t The node type.
441  * \todo Fixme: t should be a proper type, rather than gchar
442  */
443 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
445     NR::Point ppos, pos, npos;
447     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
449     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
450     bool const closed = (b->code == NR_MOVETO);
452     pos = NR::Point(b->x3, b->y3) * np->i2d;
453     if (b[1].code == NR_CURVETO) {
454         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
455     } else {
456         npos = pos;
457     }
458     Inkscape::NodePath::Node *n;
459     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
460     g_assert(sp->first == n);
461     g_assert(sp->last  == n);
463     b++;
464     t++;
465     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
466         pos = NR::Point(b->x3, b->y3) * np->i2d;
467         if (b->code == NR_CURVETO) {
468             ppos = NR::Point(b->x2, b->y2) * np->i2d;
469         } else {
470             ppos = pos;
471         }
472         if (b[1].code == NR_CURVETO) {
473             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
474         } else {
475             npos = pos;
476         }
477         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
478         b++;
479         t++;
480     }
482     if (closed) sp_nodepath_subpath_close(sp);
484     return b;
487 /**
488  * Convert from sodipodi:nodetypes to new style type string.
489  */
490 static gchar *parse_nodetypes(gchar const *types, gint length)
492     g_assert(length > 0);
494     gchar *typestr = g_new(gchar, length + 1);
496     gint pos = 0;
498     if (types) {
499         for (gint i = 0; types[i] && ( i < length ); i++) {
500             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
501             if (types[i] != '\0') {
502                 switch (types[i]) {
503                     case 's':
504                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
505                         break;
506                     case 'z':
507                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
508                         break;
509                     case 'c':
510                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
511                         break;
512                     default:
513                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
514                         break;
515                 }
516             }
517         }
518     }
520     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
522     return typestr;
525 /**
526  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
527  * updated but repr is not (for speed). Used during curve and node drag.
528  */
529 static void update_object(Inkscape::NodePath::Path *np)
531     g_assert(np);
533     sp_curve_unref(np->curve);
534     np->curve = create_curve(np);
536     sp_nodepath_set_curve(np, np->curve);
538     if (np->show_helperpath) {
539         SPCurve * helper_curve = sp_curve_copy(np->curve);
540         sp_curve_transform(helper_curve, np->i2d );
541         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
542         sp_curve_unref(helper_curve);
543     }
546 /**
547  * Update XML path node with data from path object.
548  */
549 static void update_repr_internal(Inkscape::NodePath::Path *np)
551     g_assert(np);
553     Inkscape::XML::Node *repr = np->object->repr;
555     sp_curve_unref(np->curve);
556     np->curve = create_curve(np);
558     gchar *typestr = create_typestr(np);
559     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
561     // determine if path has an effect applied and write to correct "d" attribute.
562     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
563         np->local_change++;
564         repr->setAttribute(np->repr_key, svgpath);
565     }
567     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
568         np->local_change++;
569         repr->setAttribute(np->repr_nodetypes_key, typestr);
570     }
572     g_free(svgpath);
573     g_free(typestr);
575     if (np->show_helperpath) {
576         SPCurve * helper_curve = sp_curve_copy(np->curve);
577         sp_curve_transform(helper_curve, np->i2d );
578         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
579         sp_curve_unref(helper_curve);
580     }
581  }
583 /**
584  * Update XML path node with data from path object, commit changes forever.
585  */
586 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
588     //fixme: np can be NULL, so check before proceeding
589     g_return_if_fail(np != NULL);
591     if (np->livarot_path) {
592         delete np->livarot_path;
593         np->livarot_path = NULL;
594     }
596     update_repr_internal(np);
597     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
599     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
600                      annotation);
603 /**
604  * Update XML path node with data from path object, commit changes with undo.
605  */
606 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
608     if (np->livarot_path) {
609         delete np->livarot_path;
610         np->livarot_path = NULL;
611     }
613     update_repr_internal(np);
614     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
615                            annotation);
618 /**
619  * Make duplicate of path, replace corresponding XML node in tree, commit.
620  */
621 static void stamp_repr(Inkscape::NodePath::Path *np)
623     g_assert(np);
625     Inkscape::XML::Node *old_repr = np->object->repr;
626     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
628     // remember the position of the item
629     gint pos = old_repr->position();
630     // remember parent
631     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
633     SPCurve *curve = create_curve(np);
634     gchar *typestr = create_typestr(np);
636     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
638     new_repr->setAttribute(np->repr_key, svgpath);
639     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
641     // add the new repr to the parent
642     parent->appendChild(new_repr);
643     // move to the saved position
644     new_repr->setPosition(pos > 0 ? pos : 0);
646     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
647                      _("Stamp"));
649     Inkscape::GC::release(new_repr);
650     g_free(svgpath);
651     g_free(typestr);
652     sp_curve_unref(curve);
655 /**
656  * Create curve from path.
657  */
658 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
660     SPCurve *curve = sp_curve_new();
662     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
663        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
664         sp_curve_moveto(curve,
665                         sp->first->pos * np->d2i);
666        Inkscape::NodePath::Node *n = sp->first->n.other;
667         while (n) {
668             NR::Point const end_pt = n->pos * np->d2i;
669             switch (n->code) {
670                 case NR_LINETO:
671                     sp_curve_lineto(curve, end_pt);
672                     break;
673                 case NR_CURVETO:
674                     sp_curve_curveto(curve,
675                                      n->p.other->n.pos * np->d2i,
676                                      n->p.pos * np->d2i,
677                                      end_pt);
678                     break;
679                 default:
680                     g_assert_not_reached();
681                     break;
682             }
683             if (n != sp->last) {
684                 n = n->n.other;
685             } else {
686                 n = NULL;
687             }
688         }
689         if (sp->closed) {
690             sp_curve_closepath(curve);
691         }
692     }
694     return curve;
697 /**
698  * Convert path type string to sodipodi:nodetypes style.
699  */
700 static gchar *create_typestr(Inkscape::NodePath::Path *np)
702     gchar *typestr = g_new(gchar, 32);
703     gint len = 32;
704     gint pos = 0;
706     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
707        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
709         if (pos >= len) {
710             typestr = g_renew(gchar, typestr, len + 32);
711             len += 32;
712         }
714         typestr[pos++] = 'c';
716        Inkscape::NodePath::Node *n;
717         n = sp->first->n.other;
718         while (n) {
719             gchar code;
721             switch (n->type) {
722                 case Inkscape::NodePath::NODE_CUSP:
723                     code = 'c';
724                     break;
725                 case Inkscape::NodePath::NODE_SMOOTH:
726                     code = 's';
727                     break;
728                 case Inkscape::NodePath::NODE_SYMM:
729                     code = 'z';
730                     break;
731                 default:
732                     g_assert_not_reached();
733                     code = '\0';
734                     break;
735             }
737             if (pos >= len) {
738                 typestr = g_renew(gchar, typestr, len + 32);
739                 len += 32;
740             }
742             typestr[pos++] = code;
744             if (n != sp->last) {
745                 n = n->n.other;
746             } else {
747                 n = NULL;
748             }
749         }
750     }
752     if (pos >= len) {
753         typestr = g_renew(gchar, typestr, len + 1);
754         len += 1;
755     }
757     typestr[pos++] = '\0';
759     return typestr;
762 /**
763  * Returns current path in context. // later eliminate this function at all!
764  */
765 static Inkscape::NodePath::Path *sp_nodepath_current()
767     if (!SP_ACTIVE_DESKTOP) {
768         return NULL;
769     }
771     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
773     if (!SP_IS_NODE_CONTEXT(event_context)) {
774         return NULL;
775     }
777     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
782 /**
783  \brief Fills node and handle positions for three nodes, splitting line
784   marked by end at distance t.
785  */
786 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
788     g_assert(new_path != NULL);
789     g_assert(end      != NULL);
791     g_assert(end->p.other == new_path);
792    Inkscape::NodePath::Node *start = new_path->p.other;
793     g_assert(start);
795     if (end->code == NR_LINETO) {
796         new_path->type =Inkscape::NodePath::NODE_CUSP;
797         new_path->code = NR_LINETO;
798         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
799     } else {
800         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
801         new_path->code = NR_CURVETO;
802         gdouble s      = 1 - t;
803         for (int dim = 0; dim < 2; dim++) {
804             NR::Coord const f000 = start->pos[dim];
805             NR::Coord const f001 = start->n.pos[dim];
806             NR::Coord const f011 = end->p.pos[dim];
807             NR::Coord const f111 = end->pos[dim];
808             NR::Coord const f00t = s * f000 + t * f001;
809             NR::Coord const f01t = s * f001 + t * f011;
810             NR::Coord const f11t = s * f011 + t * f111;
811             NR::Coord const f0tt = s * f00t + t * f01t;
812             NR::Coord const f1tt = s * f01t + t * f11t;
813             NR::Coord const fttt = s * f0tt + t * f1tt;
814             start->n.pos[dim]    = f00t;
815             new_path->p.pos[dim] = f0tt;
816             new_path->pos[dim]   = fttt;
817             new_path->n.pos[dim] = f1tt;
818             end->p.pos[dim]      = f11t;
819         }
820     }
823 /**
824  * Adds new node on direct line between two nodes, activates handles of all
825  * three nodes.
826  */
827 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
829     g_assert(end);
830     g_assert(end->subpath);
831     g_assert(g_list_find(end->subpath->nodes, end));
833    Inkscape::NodePath::Node *start = end->p.other;
834     g_assert( start->n.other == end );
835    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
836                                                end,
837                                                (NRPathcode)end->code == NR_LINETO?
838                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
839                                                (NRPathcode)end->code,
840                                                &start->pos, &start->pos, &start->n.pos);
841     sp_nodepath_line_midpoint(newnode, end, t);
843     sp_node_adjust_handles(start);
844     sp_node_update_handles(start);
845     sp_node_update_handles(newnode);
846     sp_node_adjust_handles(end);
847     sp_node_update_handles(end);
849     return newnode;
852 /**
853 \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
854 */
855 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
857     g_assert(node);
858     g_assert(node->subpath);
859     g_assert(g_list_find(node->subpath->nodes, node));
861    Inkscape::NodePath::SubPath *sp = node->subpath;
862     Inkscape::NodePath::Path *np    = sp->nodepath;
864     if (sp->closed) {
865         sp_nodepath_subpath_open(sp, node);
866         return sp->first;
867     } else {
868         // no break for end nodes
869         if (node == sp->first) return NULL;
870         if (node == sp->last ) return NULL;
872         // create a new subpath
873        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
875         // duplicate the break node as start of the new subpath
876        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
878         while (node->n.other) { // copy the remaining nodes into the new subpath
879            Inkscape::NodePath::Node *n  = node->n.other;
880            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);
881             if (n->selected) {
882                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
883             }
884             sp_nodepath_node_destroy(n); // remove the point on the original subpath
885         }
887         return newnode;
888     }
891 /**
892  * Duplicate node and connect to neighbours.
893  */
894 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
896     g_assert(node);
897     g_assert(node->subpath);
898     g_assert(g_list_find(node->subpath->nodes, node));
900    Inkscape::NodePath::SubPath *sp = node->subpath;
902     NRPathcode code = (NRPathcode) node->code;
903     if (code == NR_MOVETO) { // if node is the endnode,
904         node->code = NR_LINETO; // new one is inserted before it, so change that to line
905     }
907     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
909     if (!node->n.other || !node->p.other) // if node is an endnode, select it
910         return node;
911     else
912         return newnode; // otherwise select the newly created node
915 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
917     node->p.pos = (node->pos + (node->pos - node->n.pos));
920 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
922     node->n.pos = (node->pos + (node->pos - node->p.pos));
925 /**
926  * Change line type at node, with side effects on neighbours.
927  */
928 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
930     g_assert(end);
931     g_assert(end->subpath);
932     g_assert(end->p.other);
934     if (end->code == static_cast< guint > ( code ) )
935         return;
937    Inkscape::NodePath::Node *start = end->p.other;
939     end->code = code;
941     if (code == NR_LINETO) {
942         if (start->code == NR_LINETO) {
943             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
944         }
945         if (end->n.other) {
946             if (end->n.other->code == NR_LINETO) {
947                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
948             }
949         }
950     } else {
951         NR::Point delta = end->pos - start->pos;
952         start->n.pos = start->pos + delta / 3;
953         end->p.pos = end->pos - delta / 3;
954         sp_node_adjust_handle(start, 1);
955         sp_node_adjust_handle(end, -1);
956     }
958     sp_node_update_handles(start);
959     sp_node_update_handles(end);
962 /**
963  * Change node type, and its handles accordingly.
964  */
965 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
967     g_assert(node);
968     g_assert(node->subpath);
970     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
971         return node;
973     if ((node->p.other != NULL) && (node->n.other != NULL)) {
974         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
975             type =Inkscape::NodePath::NODE_CUSP;
976         }
977     }
979     node->type = type;
981     if (node->type == Inkscape::NodePath::NODE_CUSP) {
982         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
983         node->knot->setSize (node->selected? 11 : 9);
984         sp_knot_update_ctrl(node->knot);
985     } else {
986         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
987         node->knot->setSize (node->selected? 9 : 7);
988         sp_knot_update_ctrl(node->knot);
989     }
991     // if one of handles is mouseovered, preserve its position
992     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
993         sp_node_adjust_handle(node, 1);
994     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
995         sp_node_adjust_handle(node, -1);
996     } else {
997         sp_node_adjust_handles(node);
998     }
1000     sp_node_update_handles(node);
1002     sp_nodepath_update_statusbar(node->subpath->nodepath);
1004     return node;
1007 /**
1008  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
1009  * adjacent segments from lines to curves.
1010 */
1011 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1013     bool p_line = (node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos);
1014     bool n_line = (node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos);
1016     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1017         if (p_line && n_line) {
1018             // only if both adjacent segments are lines,
1019             // convert both to curves:
1021             node->code = NR_CURVETO;
1022             node->n.other->code = NR_CURVETO;
1024             NR::Point leg_prev = node->pos - node->p.other->pos;
1025             NR::Point leg_next = node->pos - node->n.other->pos;
1027             double norm_leg_prev = L2(leg_prev);
1028             double norm_leg_next = L2(leg_next);
1030             // delta has length 1 and is orthogonal to bisecting line
1031             NR::Point delta;
1032             if (norm_leg_next > 0.0) {
1033                 delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1034                 (&delta)->normalize();
1035             }
1037             if (type == Inkscape::NodePath::NODE_SYMM) {
1038                 double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1039                 node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1040                 node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1041             } else {
1042                 // length of handle is proportional to distance to adjacent node
1043                 node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1044                 node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1045             }
1047             sp_node_update_handles(node);
1048         }
1049     }
1051     sp_nodepath_set_node_type (node, type);
1054 /**
1055  * Move node to point, and adjust its and neighbouring handles.
1056  */
1057 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1059     NR::Point delta = p - node->pos;
1060     node->pos = p;
1062     node->p.pos += delta;
1063     node->n.pos += delta;
1065     Inkscape::NodePath::Node *node_p = NULL;
1066     Inkscape::NodePath::Node *node_n = NULL;
1068     if (node->p.other) {
1069         if (node->code == NR_LINETO) {
1070             sp_node_adjust_handle(node, 1);
1071             sp_node_adjust_handle(node->p.other, -1);
1072             node_p = node->p.other;
1073         }
1074     }
1075     if (node->n.other) {
1076         if (node->n.other->code == NR_LINETO) {
1077             sp_node_adjust_handle(node, -1);
1078             sp_node_adjust_handle(node->n.other, 1);
1079             node_n = node->n.other;
1080         }
1081     }
1083     // this function is only called from batch movers that will update display at the end
1084     // themselves, so here we just move all the knots without emitting move signals, for speed
1085     sp_node_update_handles(node, false);
1086     if (node_n) {
1087         sp_node_update_handles(node_n, false);
1088     }
1089     if (node_p) {
1090         sp_node_update_handles(node_p, false);
1091     }
1094 /**
1095  * Call sp_node_moveto() for node selection and handle possible snapping.
1096  */
1097 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1098                                             bool const snap = true)
1100     NR::Coord best = NR_HUGE;
1101     NR::Point delta(dx, dy);
1102     NR::Point best_pt = delta;
1103     NR::Point best_abs(NR_HUGE, NR_HUGE);
1105     
1106     if (snap) {    
1107         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1108          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1109          * must provide that information. */
1110           
1111         // Build a list of the unselected nodes to which the snapper should snap 
1112         std::vector<NR::Point> unselected_nodes;
1113         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1114             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1115             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1116                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1117                 if (!node->selected) {
1118                     unselected_nodes.push_back(node->pos);
1119                 }    
1120             }
1121         }        
1122         
1123         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1124         
1125         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1126             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1127             Inkscape::SnappedPoint s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);            
1128             if (s.getDistance() < best) {
1129                 best = s.getDistance();
1130                 best_abs = s.getPoint();
1131                 best_pt = best_abs - n->pos;
1132             }
1133         }
1134                         
1135         if (best_abs[NR::X] < NR_HUGE) {
1136             nodepath->desktop->snapindicator->set_new_snappoint(best_abs.to_2geom());
1137         }
1138     }
1140     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1141         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1142         sp_node_moveto(n, n->pos + best_pt);
1143     }
1145     // do not update repr here so that node dragging is acceptably fast
1146     update_object(nodepath);
1149 /**
1150 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1151 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1152 near x = 0.
1153  */
1154 double
1155 sculpt_profile (double x, double alpha, guint profile)
1157     if (x >= 1)
1158         return 0;
1159     if (x <= 0)
1160         return 1;
1162     switch (profile) {
1163         case SCULPT_PROFILE_LINEAR:
1164         return 1 - x;
1165         case SCULPT_PROFILE_BELL:
1166         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1167         case SCULPT_PROFILE_ELLIPTIC:
1168         return sqrt(1 - x*x);
1169     }
1171     return 1;
1174 double
1175 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1177     // extremely primitive for now, don't have time to look for the real one
1178     double lower = NR::L2(b - a);
1179     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1180     return (lower + upper)/2;
1183 void
1184 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1186     n->pos = n->origin + delta;
1187     n->n.pos = n->n.origin + delta_n;
1188     n->p.pos = n->p.origin + delta_p;
1189     sp_node_adjust_handles(n);
1190     sp_node_update_handles(n, false);
1193 /**
1194  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1195  * on how far they are from the dragged node n.
1196  */
1197 static void
1198 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1200     g_assert (n);
1201     g_assert (nodepath);
1202     g_assert (n->subpath->nodepath == nodepath);
1204     double pressure = n->knot->pressure;
1205     if (pressure == 0)
1206         pressure = 0.5; // default
1207     pressure = CLAMP (pressure, 0.2, 0.8);
1209     // map pressure to alpha = 1/5 ... 5
1210     double alpha = 1 - 2 * fabs(pressure - 0.5);
1211     if (pressure > 0.5)
1212         alpha = 1/alpha;
1214     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1216     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1217         // Only one subpath has selected nodes:
1218         // use linear mode, where the distance from n to node being dragged is calculated along the path
1220         double n_sel_range = 0, p_sel_range = 0;
1221         guint n_nodes = 0, p_nodes = 0;
1222         guint n_sel_nodes = 0, p_sel_nodes = 0;
1224         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1225         {
1226             double n_range = 0, p_range = 0;
1227             bool n_going = true, p_going = true;
1228             Inkscape::NodePath::Node *n_node = n;
1229             Inkscape::NodePath::Node *p_node = n;
1230             do {
1231                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1232                 if (n_node && n_going)
1233                     n_node = n_node->n.other;
1234                 if (n_node == NULL) {
1235                     n_going = false;
1236                 } else {
1237                     n_nodes ++;
1238                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1239                     if (n_node->selected) {
1240                         n_sel_nodes ++;
1241                         n_sel_range = n_range;
1242                     }
1243                     if (n_node == p_node) {
1244                         n_going = false;
1245                         p_going = false;
1246                     }
1247                 }
1248                 if (p_node && p_going)
1249                     p_node = p_node->p.other;
1250                 if (p_node == NULL) {
1251                     p_going = false;
1252                 } else {
1253                     p_nodes ++;
1254                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1255                     if (p_node->selected) {
1256                         p_sel_nodes ++;
1257                         p_sel_range = p_range;
1258                     }
1259                     if (p_node == n_node) {
1260                         n_going = false;
1261                         p_going = false;
1262                     }
1263                 }
1264             } while (n_going || p_going);
1265         }
1267         // Second pass: actually move nodes in this subpath
1268         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1269         {
1270             double n_range = 0, p_range = 0;
1271             bool n_going = true, p_going = true;
1272             Inkscape::NodePath::Node *n_node = n;
1273             Inkscape::NodePath::Node *p_node = n;
1274             do {
1275                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1276                 if (n_node && n_going)
1277                     n_node = n_node->n.other;
1278                 if (n_node == NULL) {
1279                     n_going = false;
1280                 } else {
1281                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1282                     if (n_node->selected) {
1283                         sp_nodepath_move_node_and_handles (n_node,
1284                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1285                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1286                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1287                     }
1288                     if (n_node == p_node) {
1289                         n_going = false;
1290                         p_going = false;
1291                     }
1292                 }
1293                 if (p_node && p_going)
1294                     p_node = p_node->p.other;
1295                 if (p_node == NULL) {
1296                     p_going = false;
1297                 } else {
1298                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1299                     if (p_node->selected) {
1300                         sp_nodepath_move_node_and_handles (p_node,
1301                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1302                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1303                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1304                     }
1305                     if (p_node == n_node) {
1306                         n_going = false;
1307                         p_going = false;
1308                     }
1309                 }
1310             } while (n_going || p_going);
1311         }
1313     } else {
1314         // Multiple subpaths have selected nodes:
1315         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1316         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1317         // fix the pear-like shape when sculpting e.g. a ring
1319         // First pass: calculate range
1320         gdouble direct_range = 0;
1321         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1322             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1323             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1324                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1325                 if (node->selected) {
1326                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1327                 }
1328             }
1329         }
1331         // Second pass: actually move nodes
1332         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1333             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1334             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1335                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1336                 if (node->selected) {
1337                     if (direct_range > 1e-6) {
1338                         sp_nodepath_move_node_and_handles (node,
1339                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1340                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1341                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1342                     } else {
1343                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1344                     }
1346                 }
1347             }
1348         }
1349     }
1351     // do not update repr here so that node dragging is acceptably fast
1352     update_object(nodepath);
1356 /**
1357  * Move node selection to point, adjust its and neighbouring handles,
1358  * handle possible snapping, and commit the change with possible undo.
1359  */
1360 void
1361 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1363     if (!nodepath) return;
1365     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1367     if (dx == 0) {
1368         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1369     } else if (dy == 0) {
1370         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1371     } else {
1372         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1373     }
1376 /**
1377  * Move node selection off screen and commit the change.
1378  */
1379 void
1380 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1382     // borrowed from sp_selection_move_screen in selection-chemistry.c
1383     // we find out the current zoom factor and divide deltas by it
1384     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1386     gdouble zoom = desktop->current_zoom();
1387     gdouble zdx = dx / zoom;
1388     gdouble zdy = dy / zoom;
1390     if (!nodepath) return;
1392     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1394     if (dx == 0) {
1395         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1396     } else if (dy == 0) {
1397         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1398     } else {
1399         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1400     }
1403 /**
1404  * Move selected nodes to the absolute position given
1405  */
1406 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1408     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1409         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1410         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1411         sp_node_moveto(n, npos);
1412     }
1414     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1417 /**
1418  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1419  */
1420 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1422     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1423     g_return_val_if_fail(nodepath->selected, no_coord);
1425     // determine coordinate of first selected node
1426     GList *nsel = nodepath->selected;
1427     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1428     NR::Coord coord = n->pos[axis];
1429     bool coincide = true;
1431     // compare it to the coordinates of all the other selected nodes
1432     for (GList *l = nsel->next; l != NULL; l = l->next) {
1433         n = (Inkscape::NodePath::Node *) l->data;
1434         if (n->pos[axis] != coord) {
1435             coincide = false;
1436         }
1437     }
1438     if (coincide) {
1439         return coord;
1440     } else {
1441         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1442         // currently we return the coordinate of the bounding box midpoint because I don't know how
1443         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1444         return bbox.midpoint()[axis];
1445     }
1448 /** If they don't yet exist, creates knot and line for the given side of the node */
1449 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1451     if (!side->knot) {
1452         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"));
1454         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1455         side->knot->setSize (7);
1456         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1457         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1458         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1459         sp_knot_update_ctrl(side->knot);
1461         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1462         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1463         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1464         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1465         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1466         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1467     }
1469     if (!side->line) {
1470         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1471                                         SP_TYPE_CTRLLINE, NULL);
1472     }
1475 /**
1476  * Ensure the given handle of the node is visible/invisible, update its screen position
1477  */
1478 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1480     g_assert(node != NULL);
1482    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1483     NRPathcode code = sp_node_path_code_from_side(node, side);
1485     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1487     if (show_handle) {
1488         if (!side->knot) { // No handle knot at all
1489             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1490             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1491             side->knot->pos = side->pos;
1492             if (side->knot->item)
1493                 SP_CTRL(side->knot->item)->moveto(side->pos);
1494             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1495             sp_knot_show(side->knot);
1496         } else {
1497             if (side->knot->pos != side->pos) { // only if it's really moved
1498                 if (fire_move_signals) {
1499                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1500                 } else {
1501                     sp_knot_moveto(side->knot, &side->pos);
1502                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1503                 }
1504             }
1505             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1506                 sp_knot_show(side->knot);
1507             }
1508         }
1509         sp_canvas_item_show(side->line);
1510     } else {
1511         if (side->knot) {
1512             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1513                 sp_knot_hide(side->knot);
1514             }
1515         }
1516         if (side->line) {
1517             sp_canvas_item_hide(side->line);
1518         }
1519     }
1522 /**
1523  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1524  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1525  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1526  * updated; otherwise, just move the knots silently (used in batch moves).
1527  */
1528 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1530     g_assert(node != NULL);
1532     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1533         sp_knot_show(node->knot);
1534     }
1536     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1537         if (fire_move_signals)
1538             sp_knot_set_position(node->knot, &node->pos, 0);
1539         else
1540             sp_knot_moveto(node->knot, &node->pos);
1541     }
1543     gboolean show_handles = node->selected;
1544     if (node->p.other != NULL) {
1545         if (node->p.other->selected) show_handles = TRUE;
1546     }
1547     if (node->n.other != NULL) {
1548         if (node->n.other->selected) show_handles = TRUE;
1549     }
1551     if (node->subpath->nodepath->show_handles == false)
1552         show_handles = FALSE;
1554     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1555     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1558 /**
1559  * Call sp_node_update_handles() for all nodes on subpath.
1560  */
1561 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1563     g_assert(subpath != NULL);
1565     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1566         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1567     }
1570 /**
1571  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1572  */
1573 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1575     g_assert(nodepath != NULL);
1577     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1578         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1579     }
1582 void
1583 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1585     if (nodepath == NULL) return;
1587     nodepath->show_handles = show;
1588     sp_nodepath_update_handles(nodepath);
1591 /**
1592  * Adds all selected nodes in nodepath to list.
1593  */
1594 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1596     StlConv<Node *>::list(l, selected);
1597 /// \todo this adds a copying, rework when the selection becomes a stl list
1600 /**
1601  * Align selected nodes on the specified axis.
1602  */
1603 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1605     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1606         return;
1607     }
1609     if ( !nodepath->selected->next ) { // only one node selected
1610         return;
1611     }
1612    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1613     NR::Point dest(pNode->pos);
1614     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1615         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1616         if (pNode) {
1617             dest[axis] = pNode->pos[axis];
1618             sp_node_moveto(pNode, dest);
1619         }
1620     }
1622     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1625 /// Helper struct.
1626 struct NodeSort
1628    Inkscape::NodePath::Node *_node;
1629     NR::Coord _coord;
1630     /// \todo use vectorof pointers instead of calling copy ctor
1631     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1632         _node(node), _coord(node->pos[axis])
1633     {}
1635 };
1637 static bool operator<(NodeSort const &a, NodeSort const &b)
1639     return (a._coord < b._coord);
1642 /**
1643  * Distribute selected nodes on the specified axis.
1644  */
1645 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1647     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1648         return;
1649     }
1651     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1652         return;
1653     }
1655    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1656     std::vector<NodeSort> sorted;
1657     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1658         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1659         if (pNode) {
1660             NodeSort n(pNode, axis);
1661             sorted.push_back(n);
1662             //dest[axis] = pNode->pos[axis];
1663             //sp_node_moveto(pNode, dest);
1664         }
1665     }
1666     std::sort(sorted.begin(), sorted.end());
1667     unsigned int len = sorted.size();
1668     //overall bboxes span
1669     float dist = (sorted.back()._coord -
1670                   sorted.front()._coord);
1671     //new distance between each bbox
1672     float step = (dist) / (len - 1);
1673     float pos = sorted.front()._coord;
1674     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1675           it < sorted.end();
1676           it ++ )
1677     {
1678         NR::Point dest((*it)._node->pos);
1679         dest[axis] = pos;
1680         sp_node_moveto((*it)._node, dest);
1681         pos += step;
1682     }
1684     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1688 /**
1689  * Call sp_nodepath_line_add_node() for all selected segments.
1690  */
1691 void
1692 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1694     if (!nodepath) {
1695         return;
1696     }
1698     GList *nl = NULL;
1700     int n_added = 0;
1702     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1703        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1704         g_assert(t->selected);
1705         if (t->p.other && t->p.other->selected) {
1706             nl = g_list_prepend(nl, t);
1707         }
1708     }
1710     while (nl) {
1711        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1712        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1713        sp_nodepath_node_select(n, TRUE, FALSE);
1714        n_added ++;
1715        nl = g_list_remove(nl, t);
1716     }
1718     /** \todo fixme: adjust ? */
1719     sp_nodepath_update_handles(nodepath);
1721     if (n_added > 1) {
1722         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1723     } else if (n_added > 0) {
1724         sp_nodepath_update_repr(nodepath, _("Add node"));
1725     }
1727     sp_nodepath_update_statusbar(nodepath);
1730 /**
1731  * Select segment nearest to point
1732  */
1733 void
1734 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1736     if (!nodepath) {
1737         return;
1738     }
1740     sp_nodepath_ensure_livarot_path(nodepath);
1741     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1742     if (!maybe_position) {
1743         return;
1744     }
1745     Path::cut_position position = *maybe_position;
1747     //find segment to segment
1748     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1750     //fixme: this can return NULL, so check before proceeding.
1751     g_return_if_fail(e != NULL);
1753     gboolean force = FALSE;
1754     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1755         force = TRUE;
1756     }
1757     sp_nodepath_node_select(e, (gboolean) toggle, force);
1758     if (e->p.other)
1759         sp_nodepath_node_select(e->p.other, TRUE, force);
1761     sp_nodepath_update_handles(nodepath);
1763     sp_nodepath_update_statusbar(nodepath);
1766 /**
1767  * Add a node nearest to point
1768  */
1769 void
1770 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1772     if (!nodepath) {
1773         return;
1774     }
1776     sp_nodepath_ensure_livarot_path(nodepath);
1777     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1778     if (!maybe_position) {
1779         return;
1780     }
1781     Path::cut_position position = *maybe_position;
1783     //find segment to split
1784     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1786     //don't know why but t seems to flip for lines
1787     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1788         position.t = 1.0 - position.t;
1789     }
1790     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1791     sp_nodepath_node_select(n, FALSE, TRUE);
1793     /* fixme: adjust ? */
1794     sp_nodepath_update_handles(nodepath);
1796     sp_nodepath_update_repr(nodepath, _("Add node"));
1798     sp_nodepath_update_statusbar(nodepath);
1801 /*
1802  * Adjusts a segment so that t moves by a certain delta for dragging
1803  * converts lines to curves
1804  *
1805  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1806  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1807  */
1808 void
1809 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1811     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1813     //fixme: e and e->p can be NULL, so check for those before proceeding
1814     g_return_if_fail(e != NULL);
1815     g_return_if_fail(&e->p != NULL);
1817     /* feel good is an arbitrary parameter that distributes the delta between handles
1818      * if t of the drag point is less than 1/6 distance form the endpoint only
1819      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1820      */
1821     double feel_good;
1822     if (t <= 1.0 / 6.0)
1823         feel_good = 0;
1824     else if (t <= 0.5)
1825         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1826     else if (t <= 5.0 / 6.0)
1827         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1828     else
1829         feel_good = 1;
1831     //if we're dragging a line convert it to a curve
1832     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1833         sp_nodepath_set_line_type(e, NR_CURVETO);
1834     }
1836     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1837     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1838     e->p.other->n.pos += offsetcoord0;
1839     e->p.pos += offsetcoord1;
1841     // adjust handles of adjacent nodes where necessary
1842     sp_node_adjust_handle(e,1);
1843     sp_node_adjust_handle(e->p.other,-1);
1845     sp_nodepath_update_handles(e->subpath->nodepath);
1847     update_object(e->subpath->nodepath);
1849     sp_nodepath_update_statusbar(e->subpath->nodepath);
1853 /**
1854  * Call sp_nodepath_break() for all selected segments.
1855  */
1856 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1858     if (!nodepath) return;
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_break(n);
1864         if (nn == NULL) continue; // no break, no new node
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, _("Break path"));
1880 /**
1881  * Duplicate the selected node(s).
1882  */
1883 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1885     if (!nodepath) {
1886         return;
1887     }
1889     GList *temp = NULL;
1890     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1891        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1892        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1893         if (nn == NULL) continue; // could not duplicate
1894         temp = g_list_prepend(temp, nn);
1895     }
1897     if (temp) {
1898         sp_nodepath_deselect(nodepath);
1899     }
1900     for (GList *l = temp; l != NULL; l = l->next) {
1901         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1902     }
1904     sp_nodepath_update_handles(nodepath);
1906     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1909 /**
1910  *  Internal function to join two nodes by merging them into one.
1911  */
1912 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
1914     /* a and b are endpoints */
1916     NR::Point c;
1917     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1918         c = a->pos;
1919     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1920         c = b->pos;
1921     } else {
1922         c = (a->pos + b->pos) / 2;
1923     }
1925     if (a->subpath == b->subpath) {
1926        Inkscape::NodePath::SubPath *sp = a->subpath;
1927         sp_nodepath_subpath_close(sp);
1928         sp_node_moveto (sp->first, c);
1930         sp_nodepath_update_handles(sp->nodepath);
1931         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1932         return;
1933     }
1935     /* a and b are separate subpaths */
1936     Inkscape::NodePath::SubPath *sa = a->subpath;
1937     Inkscape::NodePath::SubPath *sb = b->subpath;
1938     NR::Point p;
1939     Inkscape::NodePath::Node *n;
1940     NRPathcode code;
1941     if (a == sa->first) {
1942         p = sa->first->n.pos;
1943         code = (NRPathcode)sa->first->n.other->code;
1944        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1945         n = sa->last;
1946         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1947         n = n->p.other;
1948         while (n) {
1949             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1950             n = n->p.other;
1951             if (n == sa->first) n = NULL;
1952         }
1953         sp_nodepath_subpath_destroy(sa);
1954         sa = t;
1955     } else if (a == sa->last) {
1956         p = sa->last->p.pos;
1957         code = (NRPathcode)sa->last->code;
1958         sp_nodepath_node_destroy(sa->last);
1959     } else {
1960         code = NR_END;
1961         g_assert_not_reached();
1962     }
1964     if (b == sb->first) {
1965         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1966         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1967             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1968         }
1969     } else if (b == sb->last) {
1970         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1971         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1972             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1973         }
1974     } else {
1975         g_assert_not_reached();
1976     }
1977     /* and now destroy sb */
1979     sp_nodepath_subpath_destroy(sb);
1981     sp_nodepath_update_handles(sa->nodepath);
1983     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1985     sp_nodepath_update_statusbar(nodepath);
1988 /**
1989  *  Internal function to join two nodes by adding a segment between them.
1990  */
1991 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
1993     if (a->subpath == b->subpath) {
1994        Inkscape::NodePath::SubPath *sp = a->subpath;
1996         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1997         sp->closed = TRUE;
1999         sp->first->p.other = sp->last;
2000         sp->last->n.other  = sp->first;
2002         sp_node_handle_mirror_p_to_n(sp->last);
2003         sp_node_handle_mirror_n_to_p(sp->first);
2005         sp->first->code = sp->last->code;
2006         sp->first       = sp->last;
2008         sp_nodepath_update_handles(sp->nodepath);
2010         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2012         return;
2013     }
2015     /* a and b are separate subpaths */
2016     Inkscape::NodePath::SubPath *sa = a->subpath;
2017     Inkscape::NodePath::SubPath *sb = b->subpath;
2019     Inkscape::NodePath::Node *n;
2020     NR::Point p;
2021     NRPathcode code;
2022     if (a == sa->first) {
2023         code = (NRPathcode) sa->first->n.other->code;
2024        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2025         n = sa->last;
2026         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2027         for (n = n->p.other; n != NULL; n = n->p.other) {
2028             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2029         }
2030         sp_nodepath_subpath_destroy(sa);
2031         sa = t;
2032     } else if (a == sa->last) {
2033         code = (NRPathcode)sa->last->code;
2034     } else {
2035         code = NR_END;
2036         g_assert_not_reached();
2037     }
2039     if (b == sb->first) {
2040         n = sb->first;
2041         sp_node_handle_mirror_p_to_n(sa->last);
2042         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2043         sp_node_handle_mirror_n_to_p(sa->last);
2044         for (n = n->n.other; n != NULL; n = n->n.other) {
2045             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2046         }
2047     } else if (b == sb->last) {
2048         n = sb->last;
2049         sp_node_handle_mirror_p_to_n(sa->last);
2050         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2051         sp_node_handle_mirror_n_to_p(sa->last);
2052         for (n = n->p.other; n != NULL; n = n->p.other) {
2053             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2054         }
2055     } else {
2056         g_assert_not_reached();
2057     }
2058     /* and now destroy sb */
2060     sp_nodepath_subpath_destroy(sb);
2062     sp_nodepath_update_handles(sa->nodepath);
2064     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2067 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2069 /**
2070  * Internal function to handle joining two nodes.
2071  */
2072 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2074     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2076     if (g_list_length(nodepath->selected) != 2) {
2077         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2078         return;
2079     }
2081     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2082     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2084     g_assert(a != b);
2085     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2086         // someone tried to join an orphan node (i.e. a single-node subpath).
2087         // this is not worth an error message, just fail silently.
2088         return;
2089     }
2091     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2092         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2093         return;
2094     }
2096     switch(mode) {
2097         case NODE_JOIN_ENDPOINTS:
2098             do_node_selected_join(nodepath, a, b);
2099             break;
2100         case NODE_JOIN_SEGMENT:
2101             do_node_selected_join_segment(nodepath, a, b);
2102             break;
2103     }
2106 /**
2107  *  Join two nodes by merging them into one.
2108  */
2109 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2111     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2114 /**
2115  *  Join two nodes by adding a segment between them.
2116  */
2117 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2119     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2122 /**
2123  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2124  */
2125 void sp_node_delete_preserve(GList *nodes_to_delete)
2127     GSList *nodepaths = NULL;
2129     while (nodes_to_delete) {
2130         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2131         Inkscape::NodePath::SubPath *sp = node->subpath;
2132         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2133         Inkscape::NodePath::Node *sample_cursor = NULL;
2134         Inkscape::NodePath::Node *sample_end = NULL;
2135         Inkscape::NodePath::Node *delete_cursor = node;
2136         bool just_delete = false;
2138         //find the start of this contiguous selection
2139         //move left to the first node that is not selected
2140         //or the start of the non-closed path
2141         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2142             delete_cursor = curr;
2143         }
2145         //just delete at the beginning of an open path
2146         if (!delete_cursor->p.other) {
2147             sample_cursor = delete_cursor;
2148             just_delete = true;
2149         } else {
2150             sample_cursor = delete_cursor->p.other;
2151         }
2153         //calculate points for each segment
2154         int rate = 5;
2155         float period = 1.0 / rate;
2156         std::vector<NR::Point> data;
2157         if (!just_delete) {
2158             data.push_back(sample_cursor->pos);
2159             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2160                 //just delete at the end of an open path
2161                 if (!sp->closed && curr == sp->last) {
2162                     just_delete = true;
2163                     break;
2164                 }
2166                 //sample points on the contiguous selected segment
2167                 NR::Point *bez;
2168                 bez = new NR::Point [4];
2169                 bez[0] = curr->pos;
2170                 bez[1] = curr->n.pos;
2171                 bez[2] = curr->n.other->p.pos;
2172                 bez[3] = curr->n.other->pos;
2173                 for (int i=1; i<rate; i++) {
2174                     gdouble t = i * period;
2175                     NR::Point p = bezier_pt(3, bez, t);
2176                     data.push_back(p);
2177                 }
2178                 data.push_back(curr->n.other->pos);
2180                 sample_end = curr->n.other;
2181                 //break if we've come full circle or hit the end of the selection
2182                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2183                     break;
2184                 }
2185             }
2186         }
2188         if (!just_delete) {
2189             //calculate the best fitting single segment and adjust the endpoints
2190             NR::Point *adata;
2191             adata = new NR::Point [data.size()];
2192             copy(data.begin(), data.end(), adata);
2194             NR::Point *bez;
2195             bez = new NR::Point [4];
2196             //would decreasing error create a better fitting approximation?
2197             gdouble error = 1.0;
2198             gint ret;
2199             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2201             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2202             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2203             //the resulting nodes behave as expected.
2204             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2205             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2207             //adjust endpoints
2208             sample_cursor->n.pos = bez[1];
2209             sample_end->p.pos = bez[2];
2210         }
2212         //destroy this contiguous selection
2213         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2214             Inkscape::NodePath::Node *temp = delete_cursor;
2215             if (delete_cursor->n.other == delete_cursor) {
2216                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2217                 delete_cursor = NULL;
2218             } else {
2219                 delete_cursor = delete_cursor->n.other;
2220             }
2221             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2222             sp_nodepath_node_destroy(temp);
2223         }
2225         sp_nodepath_update_handles(nodepath);
2227         if (!g_slist_find(nodepaths, nodepath))
2228             nodepaths = g_slist_prepend (nodepaths, nodepath);
2229     }
2231     for (GSList *i = nodepaths; i; i = i->next) {
2232         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2233         // different nodepaths will give us one undo event per nodepath
2234         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2236         // if the entire nodepath is removed, delete the selected object.
2237         if (nodepath->subpaths == NULL ||
2238             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2239             //at least 2
2240             sp_nodepath_get_node_count(nodepath) < 2) {
2241             SPDocument *document = sp_desktop_document (nodepath->desktop);
2242             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2243             //delete this nodepath's object, not the entire selection! (though at this time, this
2244             //does not matter)
2245             sp_selection_delete();
2246             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2247                               _("Delete nodes"));
2248         } else {
2249             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2250             sp_nodepath_update_statusbar(nodepath);
2251         }
2252     }
2254     g_slist_free (nodepaths);
2257 /**
2258  * Delete one or more selected nodes.
2259  */
2260 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2262     if (!nodepath) return;
2263     if (!nodepath->selected) return;
2265     /** \todo fixme: do it the right way */
2266     while (nodepath->selected) {
2267        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2268         sp_nodepath_node_destroy(node);
2269     }
2272     //clean up the nodepath (such as for trivial subpaths)
2273     sp_nodepath_cleanup(nodepath);
2275     sp_nodepath_update_handles(nodepath);
2277     // if the entire nodepath is removed, delete the selected object.
2278     if (nodepath->subpaths == NULL ||
2279         sp_nodepath_get_node_count(nodepath) < 2) {
2280         SPDocument *document = sp_desktop_document (nodepath->desktop);
2281         sp_selection_delete();
2282         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2283                           _("Delete nodes"));
2284         return;
2285     }
2287     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2289     sp_nodepath_update_statusbar(nodepath);
2292 /**
2293  * Delete one or more segments between two selected nodes.
2294  * This is the code for 'split'.
2295  */
2296 void
2297 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2299    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2300    Inkscape::NodePath::Node *curr, *next;     //Iterators
2302     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2304     if (g_list_length(nodepath->selected) != 2) {
2305         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2306                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2307         return;
2308     }
2310     //Selected nodes, not inclusive
2311    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2312    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2314     if ( ( a==b)                       ||  //same node
2315          (a->subpath  != b->subpath )  ||  //not the same path
2316          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2317          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2318     {
2319         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2320                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2321         return;
2322     }
2324     //###########################################
2325     //# BEGIN EDITS
2326     //###########################################
2327     //##################################
2328     //# CLOSED PATH
2329     //##################################
2330     if (a->subpath->closed) {
2333         gboolean reversed = FALSE;
2335         //Since we can go in a circle, we need to find the shorter distance.
2336         //  a->b or b->a
2337         start = end = NULL;
2338         int distance    = 0;
2339         int minDistance = 0;
2340         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2341             if (curr==b) {
2342                 //printf("a to b:%d\n", distance);
2343                 start = a;//go from a to b
2344                 end   = b;
2345                 minDistance = distance;
2346                 //printf("A to B :\n");
2347                 break;
2348             }
2349             distance++;
2350         }
2352         //try again, the other direction
2353         distance = 0;
2354         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2355             if (curr==a) {
2356                 //printf("b to a:%d\n", distance);
2357                 if (distance < minDistance) {
2358                     start    = b;  //we go from b to a
2359                     end      = a;
2360                     reversed = TRUE;
2361                     //printf("B to A\n");
2362                 }
2363                 break;
2364             }
2365             distance++;
2366         }
2369         //Copy everything from 'end' to 'start' to a new subpath
2370        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2371         for (curr=end ; curr ; curr=curr->n.other) {
2372             NRPathcode code = (NRPathcode) curr->code;
2373             if (curr == end)
2374                 code = NR_MOVETO;
2375             sp_nodepath_node_new(t, NULL,
2376                                  (Inkscape::NodePath::NodeType)curr->type, code,
2377                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2378             if (curr == start)
2379                 break;
2380         }
2381         sp_nodepath_subpath_destroy(a->subpath);
2384     }
2388     //##################################
2389     //# OPEN PATH
2390     //##################################
2391     else {
2393         //We need to get the direction of the list between A and B
2394         //Can we walk from a to b?
2395         start = end = NULL;
2396         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2397             if (curr==b) {
2398                 start = a;  //did it!  we go from a to b
2399                 end   = b;
2400                 //printf("A to B\n");
2401                 break;
2402             }
2403         }
2404         if (!start) {//didn't work?  let's try the other direction
2405             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2406                 if (curr==a) {
2407                     start = b;  //did it!  we go from b to a
2408                     end   = a;
2409                     //printf("B to A\n");
2410                     break;
2411                 }
2412             }
2413         }
2414         if (!start) {
2415             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2416                                                      _("Cannot find path between nodes."));
2417             return;
2418         }
2422         //Copy everything after 'end' to a new subpath
2423        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2424         for (curr=end ; curr ; curr=curr->n.other) {
2425             NRPathcode code = (NRPathcode) curr->code;
2426             if (curr == end)
2427                 code = NR_MOVETO;
2428             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2429                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2430         }
2432         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2433         for (curr = start->n.other ; curr  ; curr=next) {
2434             next = curr->n.other;
2435             sp_nodepath_node_destroy(curr);
2436         }
2438     }
2439     //###########################################
2440     //# END EDITS
2441     //###########################################
2443     //clean up the nodepath (such as for trivial subpaths)
2444     sp_nodepath_cleanup(nodepath);
2446     sp_nodepath_update_handles(nodepath);
2448     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2450     sp_nodepath_update_statusbar(nodepath);
2453 /**
2454  * Call sp_nodepath_set_line() for all selected segments.
2455  */
2456 void
2457 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2459     if (nodepath == NULL) return;
2461     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2462        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2463         g_assert(n->selected);
2464         if (n->p.other && n->p.other->selected) {
2465             sp_nodepath_set_line_type(n, code);
2466         }
2467     }
2469     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2472 /**
2473  * Call sp_nodepath_convert_node_type() for all selected nodes.
2474  */
2475 void
2476 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2478     if (nodepath == NULL) return;
2480     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2482     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2483         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2484     }
2486     sp_nodepath_update_repr(nodepath, _("Change node type"));
2489 /**
2490  * Change select status of node, update its own and neighbour handles.
2491  */
2492 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2494     node->selected = selected;
2496     if (selected) {
2497         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2498         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2499         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2500         sp_knot_update_ctrl(node->knot);
2501     } else {
2502         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2503         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2504         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2505         sp_knot_update_ctrl(node->knot);
2506     }
2508     sp_node_update_handles(node);
2509     if (node->n.other) sp_node_update_handles(node->n.other);
2510     if (node->p.other) sp_node_update_handles(node->p.other);
2513 /**
2514 \brief Select a node
2515 \param node     The node to select
2516 \param incremental   If true, add to selection, otherwise deselect others
2517 \param override   If true, always select this node, otherwise toggle selected status
2518 */
2519 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2521     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2523     if (incremental) {
2524         if (override) {
2525             if (!g_list_find(nodepath->selected, node)) {
2526                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2527             }
2528             sp_node_set_selected(node, TRUE);
2529         } else { // toggle
2530             if (node->selected) {
2531                 g_assert(g_list_find(nodepath->selected, node));
2532                 nodepath->selected = g_list_remove(nodepath->selected, node);
2533             } else {
2534                 g_assert(!g_list_find(nodepath->selected, node));
2535                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2536             }
2537             sp_node_set_selected(node, !node->selected);
2538         }
2539     } else {
2540         sp_nodepath_deselect(nodepath);
2541         nodepath->selected = g_list_prepend(nodepath->selected, node);
2542         sp_node_set_selected(node, TRUE);
2543     }
2545     sp_nodepath_update_statusbar(nodepath);
2549 /**
2550 \brief Deselect all nodes in the nodepath
2551 */
2552 void
2553 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2555     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2557     while (nodepath->selected) {
2558         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2559         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2560     }
2561     sp_nodepath_update_statusbar(nodepath);
2564 /**
2565 \brief Select or invert selection of all nodes in the nodepath
2566 */
2567 void
2568 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2570     if (!nodepath) return;
2572     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2573        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2574         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2575            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2576            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2577         }
2578     }
2581 /**
2582  * If nothing selected, does the same as sp_nodepath_select_all();
2583  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2584  * (i.e., similar to "select all in layer", with the "selected" subpaths
2585  * being treated as "layers" in the path).
2586  */
2587 void
2588 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2590     if (!nodepath) return;
2592     if (g_list_length (nodepath->selected) == 0) {
2593         sp_nodepath_select_all (nodepath, invert);
2594         return;
2595     }
2597     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2598     GSList *subpaths = NULL;
2600     for (GList *l = copy; l != NULL; l = l->next) {
2601         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2602         Inkscape::NodePath::SubPath *subpath = n->subpath;
2603         if (!g_slist_find (subpaths, subpath))
2604             subpaths = g_slist_prepend (subpaths, subpath);
2605     }
2607     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2608         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2609         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2610             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2611             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2612         }
2613     }
2615     g_slist_free (subpaths);
2616     g_list_free (copy);
2619 /**
2620  * \brief Select the node after the last selected; if none is selected,
2621  * select the first within path.
2622  */
2623 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2625     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2627    Inkscape::NodePath::Node *last = NULL;
2628     if (nodepath->selected) {
2629         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2630            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2631             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2632             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2633                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2634                 if (node->selected) {
2635                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2636                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2637                             if (spl->next) { // there's a next subpath
2638                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2639                                 last = subpath_next->first;
2640                             } else if (spl->prev) { // there's a previous subpath
2641                                 last = NULL; // to be set later to the first node of first subpath
2642                             } else {
2643                                 last = node->n.other;
2644                             }
2645                         } else {
2646                             last = node->n.other;
2647                         }
2648                     } else {
2649                         if (node->n.other) {
2650                             last = node->n.other;
2651                         } else {
2652                             if (spl->next) { // there's a next subpath
2653                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2654                                 last = subpath_next->first;
2655                             } else if (spl->prev) { // there's a previous subpath
2656                                 last = NULL; // to be set later to the first node of first subpath
2657                             } else {
2658                                 last = (Inkscape::NodePath::Node *) subpath->first;
2659                             }
2660                         }
2661                     }
2662                 }
2663             }
2664         }
2665         sp_nodepath_deselect(nodepath);
2666     }
2668     if (last) { // there's at least one more node after selected
2669         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2670     } else { // no more nodes, select the first one in first subpath
2671        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2672         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2673     }
2676 /**
2677  * \brief Select the node before the first selected; if none is selected,
2678  * select the last within path
2679  */
2680 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2682     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2684    Inkscape::NodePath::Node *last = NULL;
2685     if (nodepath->selected) {
2686         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2687            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2688             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2689                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2690                 if (node->selected) {
2691                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2692                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2693                             if (spl->prev) { // there's a prev subpath
2694                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2695                                 last = subpath_prev->last;
2696                             } else if (spl->next) { // there's a next subpath
2697                                 last = NULL; // to be set later to the last node of last subpath
2698                             } else {
2699                                 last = node->p.other;
2700                             }
2701                         } else {
2702                             last = node->p.other;
2703                         }
2704                     } else {
2705                         if (node->p.other) {
2706                             last = node->p.other;
2707                         } else {
2708                             if (spl->prev) { // there's a prev subpath
2709                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2710                                 last = subpath_prev->last;
2711                             } else if (spl->next) { // there's a next subpath
2712                                 last = NULL; // to be set later to the last node of last subpath
2713                             } else {
2714                                 last = (Inkscape::NodePath::Node *) subpath->last;
2715                             }
2716                         }
2717                     }
2718                 }
2719             }
2720         }
2721         sp_nodepath_deselect(nodepath);
2722     }
2724     if (last) { // there's at least one more node before selected
2725         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2726     } else { // no more nodes, select the last one in last subpath
2727         GList *spl = g_list_last(nodepath->subpaths);
2728        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2729         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2730     }
2733 /**
2734  * \brief Select all nodes that are within the rectangle.
2735  */
2736 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2738     if (!incremental) {
2739         sp_nodepath_deselect(nodepath);
2740     }
2742     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2743        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2744         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2745            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2747             if (b.contains(node->pos)) {
2748                 sp_nodepath_node_select(node, TRUE, TRUE);
2749             }
2750         }
2751     }
2755 void
2756 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2758     g_assert (n);
2759     g_assert (nodepath);
2760     g_assert (n->subpath->nodepath == nodepath);
2762     if (g_list_length (nodepath->selected) == 0) {
2763         if (grow > 0) {
2764             sp_nodepath_node_select(n, TRUE, TRUE);
2765         }
2766         return;
2767     }
2769     if (g_list_length (nodepath->selected) == 1) {
2770         if (grow < 0) {
2771             sp_nodepath_deselect (nodepath);
2772             return;
2773         }
2774     }
2776         double n_sel_range = 0, p_sel_range = 0;
2777             Inkscape::NodePath::Node *farthest_n_node = n;
2778             Inkscape::NodePath::Node *farthest_p_node = n;
2780         // Calculate ranges
2781         {
2782             double n_range = 0, p_range = 0;
2783             bool n_going = true, p_going = true;
2784             Inkscape::NodePath::Node *n_node = n;
2785             Inkscape::NodePath::Node *p_node = n;
2786             do {
2787                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2788                 if (n_node && n_going)
2789                     n_node = n_node->n.other;
2790                 if (n_node == NULL) {
2791                     n_going = false;
2792                 } else {
2793                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2794                     if (n_node->selected) {
2795                         n_sel_range = n_range;
2796                         farthest_n_node = n_node;
2797                     }
2798                     if (n_node == p_node) {
2799                         n_going = false;
2800                         p_going = false;
2801                     }
2802                 }
2803                 if (p_node && p_going)
2804                     p_node = p_node->p.other;
2805                 if (p_node == NULL) {
2806                     p_going = false;
2807                 } else {
2808                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2809                     if (p_node->selected) {
2810                         p_sel_range = p_range;
2811                         farthest_p_node = p_node;
2812                     }
2813                     if (p_node == n_node) {
2814                         n_going = false;
2815                         p_going = false;
2816                     }
2817                 }
2818             } while (n_going || p_going);
2819         }
2821     if (grow > 0) {
2822         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2823                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2824         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2825                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2826         }
2827     } else {
2828         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2829                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2830         } else if (farthest_p_node && farthest_p_node->selected) {
2831                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2832         }
2833     }
2836 void
2837 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2839     g_assert (n);
2840     g_assert (nodepath);
2841     g_assert (n->subpath->nodepath == nodepath);
2843     if (g_list_length (nodepath->selected) == 0) {
2844         if (grow > 0) {
2845             sp_nodepath_node_select(n, TRUE, TRUE);
2846         }
2847         return;
2848     }
2850     if (g_list_length (nodepath->selected) == 1) {
2851         if (grow < 0) {
2852             sp_nodepath_deselect (nodepath);
2853             return;
2854         }
2855     }
2857     Inkscape::NodePath::Node *farthest_selected = NULL;
2858     double farthest_dist = 0;
2860     Inkscape::NodePath::Node *closest_unselected = NULL;
2861     double closest_dist = NR_HUGE;
2863     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2864        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2865         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2866            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2867            if (node == n)
2868                continue;
2869            if (node->selected) {
2870                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2871                    farthest_dist = NR::L2(node->pos - n->pos);
2872                    farthest_selected = node;
2873                }
2874            } else {
2875                if (NR::L2(node->pos - n->pos) < closest_dist) {
2876                    closest_dist = NR::L2(node->pos - n->pos);
2877                    closest_unselected = node;
2878                }
2879            }
2880         }
2881     }
2883     if (grow > 0) {
2884         if (closest_unselected) {
2885             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2886         }
2887     } else {
2888         if (farthest_selected) {
2889             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2890         }
2891     }
2895 /**
2896 \brief  Saves all nodes' and handles' current positions in their origin members
2897 */
2898 void
2899 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2901     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2902        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2903         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2904            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2905            n->origin = n->pos;
2906            n->p.origin = n->p.pos;
2907            n->n.origin = n->n.pos;
2908         }
2909     }
2912 /**
2913 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2914 */
2915 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2917     if (!nodepath->selected) {
2918         return NULL;
2919     }
2921     GList *r = NULL;
2922     guint i = 0;
2923     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2924        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2925         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2926            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2927             i++;
2928             if (node->selected) {
2929                 r = g_list_append(r, GINT_TO_POINTER(i));
2930             }
2931         }
2932     }
2933     return r;
2936 /**
2937 \brief  Restores selection by selecting nodes whose positions are in the list
2938 */
2939 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2941     sp_nodepath_deselect(nodepath);
2943     guint i = 0;
2944     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2945        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2946         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2947            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2948             i++;
2949             if (g_list_find(r, GINT_TO_POINTER(i))) {
2950                 sp_nodepath_node_select(node, TRUE, TRUE);
2951             }
2952         }
2953     }
2957 /**
2958 \brief Adjusts handle according to node type and line code.
2959 */
2960 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2962     double len, otherlen, linelen;
2964     g_assert(node);
2966    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2967    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2969     /** \todo fixme: */
2970     if (me->other == NULL) return;
2971     if (other->other == NULL) return;
2973     /* I have line */
2975     NRPathcode mecode, ocode;
2976     if (which_adjust == 1) {
2977         mecode = (NRPathcode)me->other->code;
2978         ocode = (NRPathcode)node->code;
2979     } else {
2980         mecode = (NRPathcode)node->code;
2981         ocode = (NRPathcode)other->other->code;
2982     }
2984     if (mecode == NR_LINETO) return;
2986     /* I am curve */
2988     if (other->other == NULL) return;
2990     /* Other has line */
2992     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2994     NR::Point delta;
2995     if (ocode == NR_LINETO) {
2996         /* other is lineto, we are either smooth or symm */
2997        Inkscape::NodePath::Node *othernode = other->other;
2998         len = NR::L2(me->pos - node->pos);
2999         delta = node->pos - othernode->pos;
3000         linelen = NR::L2(delta);
3001         if (linelen < 1e-18)
3002             return;
3003         me->pos = node->pos + (len / linelen)*delta;
3004         return;
3005     }
3007     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3009         me->pos = 2 * node->pos - other->pos;
3010         return;
3011     }
3013     /* We are smooth */
3015     len = NR::L2(me->pos - node->pos);
3016     delta = other->pos - node->pos;
3017     otherlen = NR::L2(delta);
3018     if (otherlen < 1e-18) return;
3020     me->pos = node->pos - (len / otherlen) * delta;
3023 /**
3024  \brief Adjusts both handles according to node type and line code
3025  */
3026 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3028     g_assert(node);
3030     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3032     /* we are either smooth or symm */
3034     if (node->p.other == NULL) return;
3036     if (node->n.other == NULL) return;
3038     if (node->code == NR_LINETO) {
3039         if (node->n.other->code == NR_LINETO) return;
3040         sp_node_adjust_handle(node, 1);
3041         return;
3042     }
3044     if (node->n.other->code == NR_LINETO) {
3045         if (node->code == NR_LINETO) return;
3046         sp_node_adjust_handle(node, -1);
3047         return;
3048     }
3050     /* both are curves */
3051     NR::Point const delta( node->n.pos - node->p.pos );
3053     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3054         node->p.pos = node->pos - delta / 2;
3055         node->n.pos = node->pos + delta / 2;
3056         return;
3057     }
3059     /* We are smooth */
3060     double plen = NR::L2(node->p.pos - node->pos);
3061     if (plen < 1e-18) return;
3062     double nlen = NR::L2(node->n.pos - node->pos);
3063     if (nlen < 1e-18) return;
3064     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3065     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3068 /**
3069  * Node event callback.
3070  */
3071 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3073     gboolean ret = FALSE;
3074     switch (event->type) {
3075         case GDK_ENTER_NOTIFY:
3076             Inkscape::NodePath::Path::active_node = n;
3077             break;
3078         case GDK_LEAVE_NOTIFY:
3079             Inkscape::NodePath::Path::active_node = NULL;
3080             break;
3081         case GDK_SCROLL:
3082             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3083                 switch (event->scroll.direction) {
3084                     case GDK_SCROLL_UP:
3085                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3086                         break;
3087                     case GDK_SCROLL_DOWN:
3088                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3089                         break;
3090                     default:
3091                         break;
3092                 }
3093                 ret = TRUE;
3094             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3095                 switch (event->scroll.direction) {
3096                     case GDK_SCROLL_UP:
3097                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3098                         break;
3099                     case GDK_SCROLL_DOWN:
3100                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3101                         break;
3102                     default:
3103                         break;
3104                 }
3105                 ret = TRUE;
3106             }
3107             break;
3108         case GDK_KEY_PRESS:
3109             switch (get_group0_keyval (&event->key)) {
3110                 case GDK_space:
3111                     if (event->key.state & GDK_BUTTON1_MASK) {
3112                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3113                         stamp_repr(nodepath);
3114                         ret = TRUE;
3115                     }
3116                     break;
3117                 case GDK_Page_Up:
3118                     if (event->key.state & GDK_CONTROL_MASK) {
3119                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3120                     } else {
3121                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3122                     }
3123                     break;
3124                 case GDK_Page_Down:
3125                     if (event->key.state & GDK_CONTROL_MASK) {
3126                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3127                     } else {
3128                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3129                     }
3130                     break;
3131                 default:
3132                     break;
3133             }
3134             break;
3135         default:
3136             break;
3137     }
3139     return ret;
3142 /**
3143  * Handle keypress on node; directly called.
3144  */
3145 gboolean node_key(GdkEvent *event)
3147     Inkscape::NodePath::Path *np;
3149     // there is no way to verify nodes so set active_node to nil when deleting!!
3150     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3152     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3153         gint ret = FALSE;
3154         switch (get_group0_keyval (&event->key)) {
3155             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3156             case GDK_BackSpace:
3157                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3158                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3159                 sp_nodepath_update_repr(np, _("Delete node"));
3160                 Inkscape::NodePath::Path::active_node = NULL;
3161                 ret = TRUE;
3162                 break;
3163             case GDK_c:
3164                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3165                 ret = TRUE;
3166                 break;
3167             case GDK_s:
3168                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3169                 ret = TRUE;
3170                 break;
3171             case GDK_y:
3172                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3173                 ret = TRUE;
3174                 break;
3175             case GDK_b:
3176                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3177                 ret = TRUE;
3178                 break;
3179         }
3180         return ret;
3181     }
3182     return FALSE;
3185 /**
3186  * Mouseclick on node callback.
3187  */
3188 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3190    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3192     if (state & GDK_CONTROL_MASK) {
3193         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3195         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3196             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3197                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3198             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3199                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3200             } else {
3201                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3202             }
3203             sp_nodepath_update_repr(nodepath, _("Change node type"));
3204             sp_nodepath_update_statusbar(nodepath);
3206         } else { //ctrl+alt+click: delete node
3207             GList *node_to_delete = NULL;
3208             node_to_delete = g_list_append(node_to_delete, n);
3209             sp_node_delete_preserve(node_to_delete);
3210         }
3212     } else {
3213         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3214     }
3217 /**
3218  * Mouse grabbed node callback.
3219  */
3220 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3222    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3224     if (!n->selected) {
3225         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3226     }
3228     n->is_dragging = true;
3229     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3231     sp_nodepath_remember_origins (n->subpath->nodepath);
3234 /**
3235  * Mouse ungrabbed node callback.
3236  */
3237 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3239    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3241    n->dragging_out = NULL;
3242    n->is_dragging = false;
3243    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3245    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3248 /**
3249  * The point on a line, given by its angle, closest to the given point.
3250  * \param p  A point.
3251  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3252  * \param closest  Pointer to the point struct where the result is stored.
3253  * \todo FIXME: use dot product perhaps?
3254  */
3255 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3257     if (a == HUGE_VAL) { // vertical
3258         *closest = NR::Point(0, (*p)[NR::Y]);
3259     } else {
3260         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3261         (*closest)[NR::Y] = a * (*closest)[NR::X];
3262     }
3265 /**
3266  * Distance from the point to a line given by its angle.
3267  * \param p  A point.
3268  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3269  */
3270 static double point_line_distance(NR::Point *p, double a)
3272     NR::Point c;
3273     point_line_closest(p, a, &c);
3274     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]));
3277 /**
3278  * Callback for node "request" signal.
3279  * \todo fixme: This goes to "moved" event? (lauris)
3280  */
3281 static gboolean
3282 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3284     double yn, xn, yp, xp;
3285     double an, ap, na, pa;
3286     double d_an, d_ap, d_na, d_pa;
3287     gboolean collinear = FALSE;
3288     NR::Point c;
3289     NR::Point pr;
3291    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3293     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3295    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3296     if ( (!n->subpath->nodepath->straight_path) &&
3297          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3298            || n->dragging_out ) )
3299     {
3300        NR::Point mouse = (*p);
3302        if (!n->dragging_out) {
3303            // This is the first drag-out event; find out which handle to drag out
3304            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3305            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3307            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3308                return FALSE;
3310            Inkscape::NodePath::NodeSide *opposite;
3311            if (appr_p > appr_n) { // closer to p
3312                n->dragging_out = &n->p;
3313                opposite = &n->n;
3314                n->code = NR_CURVETO;
3315            } else if (appr_p < appr_n) { // closer to n
3316                n->dragging_out = &n->n;
3317                opposite = &n->p;
3318                n->n.other->code = NR_CURVETO;
3319            } else { // p and n nodes are the same
3320                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3321                    n->dragging_out = &n->p;
3322                    opposite = &n->n;
3323                    n->code = NR_CURVETO;
3324                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3325                    n->dragging_out = &n->n;
3326                    opposite = &n->p;
3327                    n->n.other->code = NR_CURVETO;
3328                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3329                    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);
3330                    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);
3331                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3332                        n->dragging_out = &n->n;
3333                        opposite = &n->p;
3334                        n->n.other->code = NR_CURVETO;
3335                    } else { // closer to other's n handle
3336                        n->dragging_out = &n->p;
3337                        opposite = &n->n;
3338                        n->code = NR_CURVETO;
3339                    }
3340                }
3341            }
3343            // if there's another handle, make sure the one we drag out starts parallel to it
3344            if (opposite->pos != n->pos) {
3345                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3346            }
3348            // knots might not be created yet!
3349            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3350            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3351        }
3353        // pass this on to the handle-moved callback
3354        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3355        sp_node_update_handles(n);
3356        return TRUE;
3357    }
3359     if (state & GDK_CONTROL_MASK) { // constrained motion
3361         // calculate relative distances of handles
3362         // n handle:
3363         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3364         xn = n->n.pos[NR::X] - n->pos[NR::X];
3365         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3366         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3367             if (n->n.other) { // if there is the next point
3368                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3369                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3370                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3371             }
3372         }
3373         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3374         if (yn < 0) { xn = -xn; yn = -yn; }
3376         // p handle:
3377         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3378         xp = n->p.pos[NR::X] - n->pos[NR::X];
3379         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3380         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3381             if (n->p.other) {
3382                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3383                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3384                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3385             }
3386         }
3387         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3388         if (yp < 0) { xp = -xp; yp = -yp; }
3390         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3391             // sliding on handles, only if at least one of the handles is non-vertical
3392             // (otherwise it's the same as ctrl+drag anyway)
3394             // calculate angles of the handles
3395             if (xn == 0) {
3396                 if (yn == 0) { // no handle, consider it the continuation of the other one
3397                     an = 0;
3398                     collinear = TRUE;
3399                 }
3400                 else an = 0; // vertical; set the angle to horizontal
3401             } else an = yn/xn;
3403             if (xp == 0) {
3404                 if (yp == 0) { // no handle, consider it the continuation of the other one
3405                     ap = an;
3406                 }
3407                 else ap = 0; // vertical; set the angle to horizontal
3408             } else  ap = yp/xp;
3410             if (collinear) an = ap;
3412             // angles of the perpendiculars; HUGE_VAL means vertical
3413             if (an == 0) na = HUGE_VAL; else na = -1/an;
3414             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3416             // mouse point relative to the node's original pos
3417             pr = (*p) - n->origin;
3419             // distances to the four lines (two handles and two perpendiculars)
3420             d_an = point_line_distance(&pr, an);
3421             d_na = point_line_distance(&pr, na);
3422             d_ap = point_line_distance(&pr, ap);
3423             d_pa = point_line_distance(&pr, pa);
3425             // find out which line is the closest, save its closest point in c
3426             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3427                 point_line_closest(&pr, an, &c);
3428             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3429                 point_line_closest(&pr, ap, &c);
3430             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3431                 point_line_closest(&pr, na, &c);
3432             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3433                 point_line_closest(&pr, pa, &c);
3434             }
3436             // move the node to the closest point
3437             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3438                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3439                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3441         } else {  // constraining to hor/vert
3443             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3444                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3445             } else { // snap to vert
3446                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3447             }
3448         }
3449     } else { // move freely
3450         if (n->is_dragging) {
3451             if (state & GDK_MOD1_MASK) { // sculpt
3452                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3453             } else {
3454                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3455                                             (*p)[NR::X] - n->pos[NR::X],
3456                                             (*p)[NR::Y] - n->pos[NR::Y],
3457                                             (state & GDK_SHIFT_MASK) == 0);
3458             }
3459         }
3460     }
3462     n->subpath->nodepath->desktop->scroll_to_point(p);
3464     return TRUE;
3467 /**
3468  * Node handle clicked callback.
3469  */
3470 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3472    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3474     if (state & GDK_CONTROL_MASK) { // "delete" handle
3475         if (n->p.knot == knot) {
3476             n->p.pos = n->pos;
3477         } else if (n->n.knot == knot) {
3478             n->n.pos = n->pos;
3479         }
3480         sp_node_update_handles(n);
3481         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3482         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3483         sp_nodepath_update_statusbar(nodepath);
3485     } else { // just select or add to selection, depending in Shift
3486         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3487     }
3490 /**
3491  * Node handle grabbed callback.
3492  */
3493 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3495    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3497     if (!n->selected) {
3498         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3499     }
3501     // remember the origin point of the handle
3502     if (n->p.knot == knot) {
3503         n->p.origin_radial = n->p.pos - n->pos;
3504     } else if (n->n.knot == knot) {
3505         n->n.origin_radial = n->n.pos - n->pos;
3506     } else {
3507         g_assert_not_reached();
3508     }
3510     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3513 /**
3514  * Node handle ungrabbed callback.
3515  */
3516 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3518    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3520     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3521     if (n->p.knot == knot) {
3522         n->p.origin_radial.a = 0;
3523         sp_knot_set_position(knot, &n->p.pos, state);
3524     } else if (n->n.knot == knot) {
3525         n->n.origin_radial.a = 0;
3526         sp_knot_set_position(knot, &n->n.pos, state);
3527     } else {
3528         g_assert_not_reached();
3529     }
3531     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3534 /**
3535  * Node handle "request" signal callback.
3536  */
3537 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3539     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3541     Inkscape::NodePath::NodeSide *me, *opposite;
3542     gint which;
3543     if (n->p.knot == knot) {
3544         me = &n->p;
3545         opposite = &n->n;
3546         which = -1;
3547     } else if (n->n.knot == knot) {
3548         me = &n->n;
3549         opposite = &n->p;
3550         which = 1;
3551     } else {
3552         me = opposite = NULL;
3553         which = 0;
3554         g_assert_not_reached();
3555     }
3557     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3559     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3560     Inkscape::SnappedPoint s ;
3561     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3562         /* We are smooth node adjacent with line */
3563         NR::Point const delta = *p - n->pos;
3564         NR::Coord const len = NR::L2(delta);
3565         Inkscape::NodePath::Node *othernode = opposite->other;
3566         NR::Point const ndelta = n->pos - othernode->pos;
3567         NR::Coord const linelen = NR::L2(ndelta);
3568         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3569             NR::Coord const scal = dot(delta, ndelta) / linelen;
3570             (*p) = n->pos + (scal / linelen) * ndelta;
3571         }
3572         s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), n->subpath->nodepath->item);
3573     } else {
3574         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, n->subpath->nodepath->item);
3575     }
3576     *p = s.getPoint();
3577     if (s.getDistance() < NR_HUGE) {
3578         n->subpath->nodepath->desktop->snapindicator->set_new_snappoint((*p).to_2geom());
3579     }
3581     sp_node_adjust_handle(n, -which);
3583     return FALSE;
3586 /**
3587  * Node handle moved callback.
3588  */
3589 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3591    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3593    Inkscape::NodePath::NodeSide *me;
3594    Inkscape::NodePath::NodeSide *other;
3595     if (n->p.knot == knot) {
3596         me = &n->p;
3597         other = &n->n;
3598     } else if (n->n.knot == knot) {
3599         me = &n->n;
3600         other = &n->p;
3601     } else {
3602         me = NULL;
3603         other = NULL;
3604         g_assert_not_reached();
3605     }
3607     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3608     Radial rme(me->pos - n->pos);
3609     Radial rother(other->pos - n->pos);
3610     Radial rnew(*p - n->pos);
3612     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3613         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3614         /* 0 interpreted as "no snapping". */
3616         // The closest PI/snaps angle, starting from zero.
3617         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3618         if (me->origin_radial.a == HUGE_VAL) {
3619             // ortho doesn't exist: original handle was zero length.
3620             rnew.a = a_snapped;
3621         } else {
3622             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3623              * its opposite and perpendiculars). */
3624             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3626             // Snap to the closest.
3627             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3628                        ? a_snapped
3629                        : a_ortho );
3630         }
3631     }
3633     if (state & GDK_MOD1_MASK) {
3634         // lock handle length
3635         rnew.r = me->origin_radial.r;
3636     }
3638     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3639         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3640         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3641         rother.a += rnew.a - rme.a;
3642         other->pos = NR::Point(rother) + n->pos;
3643         if (other->knot) {
3644             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3645             sp_knot_moveto(other->knot, &other->pos);
3646         }
3647     }
3649     me->pos = NR::Point(rnew) + n->pos;
3650     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3652     // move knot, but without emitting the signal:
3653     // we cannot emit a "moved" signal because we're now processing it
3654     sp_knot_moveto(me->knot, &(me->pos));
3656     update_object(n->subpath->nodepath);
3658     /* status text */
3659     SPDesktop *desktop = n->subpath->nodepath->desktop;
3660     if (!desktop) return;
3661     SPEventContext *ec = desktop->event_context;
3662     if (!ec) return;
3663     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3664     if (!mc) return;
3666     double degrees = 180 / M_PI * rnew.a;
3667     if (degrees > 180) degrees -= 360;
3668     if (degrees < -180) degrees += 360;
3669     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3670         degrees = angle_to_compass (degrees);
3672     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3674     mc->setF(Inkscape::NORMAL_MESSAGE,
3675          _("<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);
3677     g_string_free(length, TRUE);
3680 /**
3681  * Node handle event callback.
3682  */
3683 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3685     gboolean ret = FALSE;
3686     switch (event->type) {
3687         case GDK_KEY_PRESS:
3688             switch (get_group0_keyval (&event->key)) {
3689                 case GDK_space:
3690                     if (event->key.state & GDK_BUTTON1_MASK) {
3691                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3692                         stamp_repr(nodepath);
3693                         ret = TRUE;
3694                     }
3695                     break;
3696                 default:
3697                     break;
3698             }
3699             break;
3700         case GDK_ENTER_NOTIFY:
3701             // we use an experimentally determined threshold that seems to work fine
3702             if (NR::L2(n->pos - knot->pos) < 0.75)
3703                 Inkscape::NodePath::Path::active_node = n;
3704             break;
3705         case GDK_LEAVE_NOTIFY:
3706             // we use an experimentally determined threshold that seems to work fine
3707             if (NR::L2(n->pos - knot->pos) < 0.75)
3708                 Inkscape::NodePath::Path::active_node = NULL;
3709             break;
3710         default:
3711             break;
3712     }
3714     return ret;
3717 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3718                                  Radial &rme, Radial &rother, gboolean const both)
3720     rme.a += angle;
3721     if ( both
3722          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3723          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3724     {
3725         rother.a += angle;
3726     }
3729 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3730                                         Radial &rme, Radial &rother, gboolean const both)
3732     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3734     gdouble r;
3735     if ( both
3736          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3737          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3738     {
3739         r = MAX(rme.r, rother.r);
3740     } else {
3741         r = rme.r;
3742     }
3744     gdouble const weird_angle = atan2(norm_angle, r);
3745 /* Bulia says norm_angle is just the visible distance that the
3746  * object's end must travel on the screen.  Left as 'angle' for want of
3747  * a better name.*/
3749     rme.a += weird_angle;
3750     if ( both
3751          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3752          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3753     {
3754         rother.a += weird_angle;
3755     }
3758 /**
3759  * Rotate one node.
3760  */
3761 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3763     Inkscape::NodePath::NodeSide *me, *other;
3764     bool both = false;
3766     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3767     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3769     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3770         me = &(n->p);
3771         other = &(n->n);
3772     } else if (!n->p.other) {
3773         me = &(n->n);
3774         other = &(n->p);
3775     } else {
3776         if (which > 0) { // right handle
3777             if (xn > xp) {
3778                 me = &(n->n);
3779                 other = &(n->p);
3780             } else {
3781                 me = &(n->p);
3782                 other = &(n->n);
3783             }
3784         } else if (which < 0){ // left handle
3785             if (xn <= xp) {
3786                 me = &(n->n);
3787                 other = &(n->p);
3788             } else {
3789                 me = &(n->p);
3790                 other = &(n->n);
3791             }
3792         } else { // both handles
3793             me = &(n->n);
3794             other = &(n->p);
3795             both = true;
3796         }
3797     }
3799     Radial rme(me->pos - n->pos);
3800     Radial rother(other->pos - n->pos);
3802     if (screen) {
3803         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3804     } else {
3805         node_rotate_one_internal (*n, angle, rme, rother, both);
3806     }
3808     me->pos = n->pos + NR::Point(rme);
3810     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3811         other->pos =  n->pos + NR::Point(rother);
3812     }
3814     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3815     // so here we just move all the knots without emitting move signals, for speed
3816     sp_node_update_handles(n, false);
3819 /**
3820  * Rotate selected nodes.
3821  */
3822 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3824     if (!nodepath || !nodepath->selected) return;
3826     if (g_list_length(nodepath->selected) == 1) {
3827        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3828         node_rotate_one (n, angle, which, screen);
3829     } else {
3830        // rotate as an object:
3832         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3833         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3834         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3835             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3836             box.expandTo (n->pos); // contain all selected nodes
3837         }
3839         gdouble rot;
3840         if (screen) {
3841             gdouble const zoom = nodepath->desktop->current_zoom();
3842             gdouble const zmove = angle / zoom;
3843             gdouble const r = NR::L2(box.max() - box.midpoint());
3844             rot = atan2(zmove, r);
3845         } else {
3846             rot = angle;
3847         }
3849         NR::Point rot_center;
3850         if (Inkscape::NodePath::Path::active_node == NULL)
3851             rot_center = box.midpoint();
3852         else
3853             rot_center = Inkscape::NodePath::Path::active_node->pos;
3855         NR::Matrix t =
3856             NR::Matrix (NR::translate(-rot_center)) *
3857             NR::Matrix (NR::rotate(rot)) *
3858             NR::Matrix (NR::translate(rot_center));
3860         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3861             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3862             n->pos *= t;
3863             n->n.pos *= t;
3864             n->p.pos *= t;
3865             sp_node_update_handles(n, false);
3866         }
3867     }
3869     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3872 /**
3873  * Scale one node.
3874  */
3875 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3877     bool both = false;
3878     Inkscape::NodePath::NodeSide *me, *other;
3880     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3881     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3883     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3884         me = &(n->p);
3885         other = &(n->n);
3886         n->code = NR_CURVETO;
3887     } else if (!n->p.other) {
3888         me = &(n->n);
3889         other = &(n->p);
3890         if (n->n.other)
3891             n->n.other->code = NR_CURVETO;
3892     } else {
3893         if (which > 0) { // right handle
3894             if (xn > xp) {
3895                 me = &(n->n);
3896                 other = &(n->p);
3897                 if (n->n.other)
3898                     n->n.other->code = NR_CURVETO;
3899             } else {
3900                 me = &(n->p);
3901                 other = &(n->n);
3902                 n->code = NR_CURVETO;
3903             }
3904         } else if (which < 0){ // left handle
3905             if (xn <= xp) {
3906                 me = &(n->n);
3907                 other = &(n->p);
3908                 if (n->n.other)
3909                     n->n.other->code = NR_CURVETO;
3910             } else {
3911                 me = &(n->p);
3912                 other = &(n->n);
3913                 n->code = NR_CURVETO;
3914             }
3915         } else { // both handles
3916             me = &(n->n);
3917             other = &(n->p);
3918             both = true;
3919             n->code = NR_CURVETO;
3920             if (n->n.other)
3921                 n->n.other->code = NR_CURVETO;
3922         }
3923     }
3925     Radial rme(me->pos - n->pos);
3926     Radial rother(other->pos - n->pos);
3928     rme.r += grow;
3929     if (rme.r < 0) rme.r = 0;
3930     if (rme.a == HUGE_VAL) {
3931         if (me->other) { // if direction is unknown, initialize it towards the next node
3932             Radial rme_next(me->other->pos - n->pos);
3933             rme.a = rme_next.a;
3934         } else { // if there's no next, initialize to 0
3935             rme.a = 0;
3936         }
3937     }
3938     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3939         rother.r += grow;
3940         if (rother.r < 0) rother.r = 0;
3941         if (rother.a == HUGE_VAL) {
3942             rother.a = rme.a + M_PI;
3943         }
3944     }
3946     me->pos = n->pos + NR::Point(rme);
3948     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3949         other->pos = n->pos + NR::Point(rother);
3950     }
3952     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3953     // so here we just move all the knots without emitting move signals, for speed
3954     sp_node_update_handles(n, false);
3957 /**
3958  * Scale selected nodes.
3959  */
3960 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3962     if (!nodepath || !nodepath->selected) return;
3964     if (g_list_length(nodepath->selected) == 1) {
3965         // scale handles of the single selected node
3966         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3967         node_scale_one (n, grow, which);
3968     } else {
3969         // scale nodes as an "object":
3971         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3972         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3973         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3974             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3975             box.expandTo (n->pos); // contain all selected nodes
3976         }
3978         double scale = (box.maxExtent() + grow)/box.maxExtent();
3980         NR::Point scale_center;
3981         if (Inkscape::NodePath::Path::active_node == NULL)
3982             scale_center = box.midpoint();
3983         else
3984             scale_center = Inkscape::NodePath::Path::active_node->pos;
3986         NR::Matrix t =
3987             NR::Matrix (NR::translate(-scale_center)) *
3988             NR::Matrix (NR::scale(scale, scale)) *
3989             NR::Matrix (NR::translate(scale_center));
3991         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3992             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3993             n->pos *= t;
3994             n->n.pos *= t;
3995             n->p.pos *= t;
3996             sp_node_update_handles(n, false);
3997         }
3998     }
4000     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4003 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4005     if (!nodepath) return;
4006     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4009 /**
4010  * Flip selected nodes horizontally/vertically.
4011  */
4012 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4014     if (!nodepath || !nodepath->selected) return;
4016     if (g_list_length(nodepath->selected) == 1 && !center) {
4017         // flip handles of the single selected node
4018         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4019         double temp = n->p.pos[axis];
4020         n->p.pos[axis] = n->n.pos[axis];
4021         n->n.pos[axis] = temp;
4022         sp_node_update_handles(n, false);
4023     } else {
4024         // scale nodes as an "object":
4026         NR::Rect box = sp_node_selected_bbox (nodepath);
4027         if (!center) {
4028             center = box.midpoint();
4029         }
4030         NR::Matrix t =
4031             NR::Matrix (NR::translate(- *center)) *
4032             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4033             NR::Matrix (NR::translate(*center));
4035         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4036             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4037             n->pos *= t;
4038             n->n.pos *= t;
4039             n->p.pos *= t;
4040             sp_node_update_handles(n, false);
4041         }
4042     }
4044     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4047 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4049     g_assert (nodepath->selected);
4051     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4052     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4053     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4054         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4055         box.expandTo (n->pos); // contain all selected nodes
4056     }
4057     return box;
4060 //-----------------------------------------------
4061 /**
4062  * Return new subpath under given nodepath.
4063  */
4064 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4066     g_assert(nodepath);
4067     g_assert(nodepath->desktop);
4069    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4071     s->nodepath = nodepath;
4072     s->closed = FALSE;
4073     s->nodes = NULL;
4074     s->first = NULL;
4075     s->last = NULL;
4077     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4078     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4079     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4081     return s;
4084 /**
4085  * Destroy nodes in subpath, then subpath itself.
4086  */
4087 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4089     g_assert(subpath);
4090     g_assert(subpath->nodepath);
4091     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4093     while (subpath->nodes) {
4094         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4095     }
4097     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4099     g_free(subpath);
4102 /**
4103  * Link head to tail in subpath.
4104  */
4105 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4107     g_assert(!sp->closed);
4108     g_assert(sp->last != sp->first);
4109     g_assert(sp->first->code == NR_MOVETO);
4111     sp->closed = TRUE;
4113     //Link the head to the tail
4114     sp->first->p.other = sp->last;
4115     sp->last->n.other  = sp->first;
4116     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4117     sp->first          = sp->last;
4119     //Remove the extra end node
4120     sp_nodepath_node_destroy(sp->last->n.other);
4123 /**
4124  * Open closed (loopy) subpath at node.
4125  */
4126 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4128     g_assert(sp->closed);
4129     g_assert(n->subpath == sp);
4130     g_assert(sp->first == sp->last);
4132     /* We create new startpoint, current node will become last one */
4134    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4135                                                 &n->pos, &n->pos, &n->n.pos);
4138     sp->closed        = FALSE;
4140     //Unlink to make a head and tail
4141     sp->first         = new_path;
4142     sp->last          = n;
4143     n->n.other        = NULL;
4144     new_path->p.other = NULL;
4147 /**
4148  * Return new node in subpath with given properties.
4149  * \param pos Position of node.
4150  * \param ppos Handle position in previous direction
4151  * \param npos Handle position in previous direction
4152  */
4153 Inkscape::NodePath::Node *
4154 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)
4156     g_assert(sp);
4157     g_assert(sp->nodepath);
4158     g_assert(sp->nodepath->desktop);
4160     if (nodechunk == NULL)
4161         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4163     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4165     n->subpath  = sp;
4167     if (type != Inkscape::NodePath::NODE_NONE) {
4168         // use the type from sodipodi:nodetypes
4169         n->type = type;
4170     } else {
4171         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4172             // points are (almost) collinear
4173             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4174                 // endnode, or a node with a retracted handle
4175                 n->type = Inkscape::NodePath::NODE_CUSP;
4176             } else {
4177                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4178             }
4179         } else {
4180             n->type = Inkscape::NodePath::NODE_CUSP;
4181         }
4182     }
4184     n->code     = code;
4185     n->selected = FALSE;
4186     n->pos      = *pos;
4187     n->p.pos    = *ppos;
4188     n->n.pos    = *npos;
4190     n->dragging_out = NULL;
4192     Inkscape::NodePath::Node *prev;
4193     if (next) {
4194         //g_assert(g_list_find(sp->nodes, next));
4195         prev = next->p.other;
4196     } else {
4197         prev = sp->last;
4198     }
4200     if (prev)
4201         prev->n.other = n;
4202     else
4203         sp->first = n;
4205     if (next)
4206         next->p.other = n;
4207     else
4208         sp->last = n;
4210     n->p.other = prev;
4211     n->n.other = next;
4213     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"));
4214     sp_knot_set_position(n->knot, pos, 0);
4216     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4217     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4218     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4219     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4220     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4221     sp_knot_update_ctrl(n->knot);
4223     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4224     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4225     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4226     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4227     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4228     sp_knot_show(n->knot);
4230     // We only create handle knots and lines on demand
4231     n->p.knot = NULL;
4232     n->p.line = NULL;
4233     n->n.knot = NULL;
4234     n->n.line = NULL;
4236     sp->nodes = g_list_prepend(sp->nodes, n);
4238     return n;
4241 /**
4242  * Destroy node and its knots, link neighbors in subpath.
4243  */
4244 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4246     g_assert(node);
4247     g_assert(node->subpath);
4248     g_assert(SP_IS_KNOT(node->knot));
4250    Inkscape::NodePath::SubPath *sp = node->subpath;
4252     if (node->selected) { // first, deselect
4253         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4254         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4255     }
4257     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4259     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4260     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4261     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4262     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4263     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4264     g_object_unref(G_OBJECT(node->knot));
4266     if (node->p.knot) {
4267         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4268         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4269         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4270         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4271         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4272         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4273         g_object_unref(G_OBJECT(node->p.knot));
4274         node->p.knot = NULL;
4275     }
4277     if (node->n.knot) {
4278         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4279         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4280         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4281         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4282         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4283         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4284         g_object_unref(G_OBJECT(node->n.knot));
4285         node->n.knot = NULL;
4286     }
4288     if (node->p.line)
4289         gtk_object_destroy(GTK_OBJECT(node->p.line));
4290     if (node->n.line)
4291         gtk_object_destroy(GTK_OBJECT(node->n.line));
4293     if (sp->nodes) { // there are others nodes on the subpath
4294         if (sp->closed) {
4295             if (sp->first == node) {
4296                 g_assert(sp->last == node);
4297                 sp->first = node->n.other;
4298                 sp->last = sp->first;
4299             }
4300             node->p.other->n.other = node->n.other;
4301             node->n.other->p.other = node->p.other;
4302         } else {
4303             if (sp->first == node) {
4304                 sp->first = node->n.other;
4305                 sp->first->code = NR_MOVETO;
4306             }
4307             if (sp->last == node) sp->last = node->p.other;
4308             if (node->p.other) node->p.other->n.other = node->n.other;
4309             if (node->n.other) node->n.other->p.other = node->p.other;
4310         }
4311     } else { // this was the last node on subpath
4312         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4313     }
4315     g_mem_chunk_free(nodechunk, node);
4318 /**
4319  * Returns one of the node's two sides.
4320  * \param which Indicates which side.
4321  * \return Pointer to previous node side if which==-1, next if which==1.
4322  */
4323 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4325     g_assert(node);
4327     switch (which) {
4328         case -1:
4329             return &node->p;
4330         case 1:
4331             return &node->n;
4332         default:
4333             break;
4334     }
4336     g_assert_not_reached();
4338     return NULL;
4341 /**
4342  * Return the other side of the node, given one of its sides.
4343  */
4344 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4346     g_assert(node);
4348     if (me == &node->p) return &node->n;
4349     if (me == &node->n) return &node->p;
4351     g_assert_not_reached();
4353     return NULL;
4356 /**
4357  * Return NRPathcode on the given side of the node.
4358  */
4359 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4361     g_assert(node);
4363     if (me == &node->p) {
4364         if (node->p.other) return (NRPathcode)node->code;
4365         return NR_MOVETO;
4366     }
4368     if (me == &node->n) {
4369         if (node->n.other) return (NRPathcode)node->n.other->code;
4370         return NR_MOVETO;
4371     }
4373     g_assert_not_reached();
4375     return NR_END;
4378 /**
4379  * Return node with the given index
4380  */
4381 Inkscape::NodePath::Node *
4382 sp_nodepath_get_node_by_index(int index)
4384     Inkscape::NodePath::Node *e = NULL;
4386     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4387     if (!nodepath) {
4388         return e;
4389     }
4391     //find segment
4392     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4394         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4395         int n = g_list_length(sp->nodes);
4396         if (sp->closed) {
4397             n++;
4398         }
4400         //if the piece belongs to this subpath grab it
4401         //otherwise move onto the next subpath
4402         if (index < n) {
4403             e = sp->first;
4404             for (int i = 0; i < index; ++i) {
4405                 e = e->n.other;
4406             }
4407             break;
4408         } else {
4409             if (sp->closed) {
4410                 index -= (n+1);
4411             } else {
4412                 index -= n;
4413             }
4414         }
4415     }
4417     return e;
4420 /**
4421  * Returns plain text meaning of node type.
4422  */
4423 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4425     unsigned retracted = 0;
4426     bool endnode = false;
4428     for (int which = -1; which <= 1; which += 2) {
4429         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4430         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4431             retracted ++;
4432         if (!side->other)
4433             endnode = true;
4434     }
4436     if (retracted == 0) {
4437         if (endnode) {
4438                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4439                 return _("end node");
4440         } else {
4441             switch (node->type) {
4442                 case Inkscape::NodePath::NODE_CUSP:
4443                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4444                     return _("cusp");
4445                 case Inkscape::NodePath::NODE_SMOOTH:
4446                     // TRANSLATORS: "smooth" is an adjective here
4447                     return _("smooth");
4448                 case Inkscape::NodePath::NODE_SYMM:
4449                     return _("symmetric");
4450             }
4451         }
4452     } else if (retracted == 1) {
4453         if (endnode) {
4454             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4455             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4456         } else {
4457             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4458         }
4459     } else {
4460         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4461     }
4463     return NULL;
4466 /**
4467  * Handles content of statusbar as long as node tool is active.
4468  */
4469 void
4470 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4472     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");
4473     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4475     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4476     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4477     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4478     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4480     SPDesktop *desktop = NULL;
4481     if (nodepath) {
4482         desktop = nodepath->desktop;
4483     } else {
4484         desktop = SP_ACTIVE_DESKTOP;
4485     }
4487     SPEventContext *ec = desktop->event_context;
4488     if (!ec) return;
4489     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4490     if (!mc) return;
4492     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4494     if (selected_nodes == 0) {
4495         Inkscape::Selection *sel = desktop->selection;
4496         if (!sel || sel->isEmpty()) {
4497             mc->setF(Inkscape::NORMAL_MESSAGE,
4498                      _("Select a single object to edit its nodes or handles."));
4499         } else {
4500             if (nodepath) {
4501             mc->setF(Inkscape::NORMAL_MESSAGE,
4502                      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.",
4503                               "<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.",
4504                               total_nodes),
4505                      total_nodes);
4506             } else {
4507                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4508                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4509                 } else {
4510                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4511                 }
4512             }
4513         }
4514     } else if (nodepath && selected_nodes == 1) {
4515         mc->setF(Inkscape::NORMAL_MESSAGE,
4516                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4517                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4518                           total_nodes),
4519                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4520     } else {
4521         if (selected_subpaths > 1) {
4522             mc->setF(Inkscape::NORMAL_MESSAGE,
4523                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4524                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4525                               total_nodes),
4526                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4527         } else {
4528             mc->setF(Inkscape::NORMAL_MESSAGE,
4529                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4530                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4531                               total_nodes),
4532                      selected_nodes, total_nodes, when_selected);
4533         }
4534     }
4537 /*
4538  * returns a *copy* of the curve of that object.
4539  */
4540 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4541     if (!object)
4542         return NULL;
4544     SPCurve *curve = NULL;
4545     if (SP_IS_PATH(object)) {
4546         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4547         curve = sp_curve_copy(curve_new);
4548     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4549         const gchar *svgd = object->repr->attribute(key);
4550         if (svgd) {
4551             NArtBpath *bpath = sp_svg_read_path(svgd);
4552             SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
4553             if (curve_new) {
4554                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4555             } else {
4556                 g_free(bpath);
4557             }
4558         }
4559     }
4561     return curve;
4564 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4565     if (!np || !np->object || !curve)
4566         return;
4568     if (SP_IS_PATH(np->object)) {
4569         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4570             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4571         } else {
4572             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4573         }
4574     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4575         // FIXME: this writing to string and then reading from string is bound to be slow.
4576         // create a method to convert from curve directly to 2geom...
4577         gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
4578         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4579         g_free(svgpath);
4581         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4582     }
4585 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4586     np->show_helperpath = show;
4588     if (show) {
4589         SPCurve *helper_curve = sp_curve_copy(np->curve);
4590         sp_curve_transform(helper_curve, np->i2d );
4591         if (!np->helper_path) {
4592             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4593             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);
4594             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4595             sp_canvas_item_show(np->helper_path);
4596         } else {
4597             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4598         }
4599         sp_curve_unref(helper_curve);
4600     } else {
4601         if (np->helper_path) {
4602             GtkObject *temp = np->helper_path;
4603             np->helper_path = NULL;
4604             gtk_object_destroy(temp);
4605         }
4606     }
4609 /* this function does not work yet */
4610 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4611     np->straight_path = true;
4612     np->show_handles = false;
4613     g_message("add code to make the path straight.");
4614     // do sp_nodepath_convert_node_type on all nodes?
4615     // search for this text !!!   "Make selected segments lines"
4619 /*
4620   Local Variables:
4621   mode:c++
4622   c-file-style:"stroustrup"
4623   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4624   indent-tabs-mode:nil
4625   fill-column:99
4626   End:
4627 */
4628 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :