Code

fix bug: extra node added when joining two-node subpaths; comments
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include "display/sp-canvas-util.h"
23 #include <glibmm/i18n.h>
24 #include "libnr/n-art-bpath.h"
25 #include "libnr/nr-path.h"
26 #include "helper/units.h"
27 #include "knot.h"
28 #include "inkscape.h"
29 #include "document.h"
30 #include "sp-namedview.h"
31 #include "desktop.h"
32 #include "desktop-handles.h"
33 #include "snap.h"
34 #include "message-stack.h"
35 #include "message-context.h"
36 #include "node-context.h"
37 #include "shape-editor.h"
38 #include "selection-chemistry.h"
39 #include "selection.h"
40 #include "xml/repr.h"
41 #include "prefs-utils.h"
42 #include "sp-metrics.h"
43 #include "sp-path.h"
44 #include "libnr/nr-matrix-ops.h"
45 #include "splivarot.h"
46 #include "svg/svg.h"
47 #include "verbs.h"
48 #include "display/bezier-utils.h"
49 #include <vector>
50 #include <algorithm>
51 #include <cstring>
52 #include <string>
53 #include "live_effects/lpeobject.h"
54 #include "live_effects/parameter/parameter.h"
55 #include "util/mathfns.h"
56 #include "display/snap-indicator.h"
57 #include "snapped-point.h"
59 class NR::Matrix;
61 /// \todo
62 /// evil evil evil. FIXME: conflict of two different Path classes!
63 /// There is a conflict in the namespace between two classes named Path.
64 /// #include "sp-flowtext.h"
65 /// #include "sp-flowregion.h"
67 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
68 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
69 GType sp_flowregion_get_type (void);
70 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
71 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
72 GType sp_flowtext_get_type (void);
73 // end evil workaround
75 #include "helper/stlport.h"
78 /// \todo fixme: Implement these via preferences */
80 #define NODE_FILL          0xbfbfbf00
81 #define NODE_STROKE        0x000000ff
82 #define NODE_FILL_HI       0xff000000
83 #define NODE_STROKE_HI     0x000000ff
84 #define NODE_FILL_SEL      0x0000ffff
85 #define NODE_STROKE_SEL    0x000000ff
86 #define NODE_FILL_SEL_HI   0xff000000
87 #define NODE_STROKE_SEL_HI 0x000000ff
88 #define KNOT_FILL          0xffffffff
89 #define KNOT_STROKE        0x000000ff
90 #define KNOT_FILL_HI       0xff000000
91 #define KNOT_STROKE_HI     0x000000ff
93 static GMemChunk *nodechunk = NULL;
95 /* Creation from object */
97 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
98 static gchar *parse_nodetypes(gchar const *types, gint length);
100 /* Object updating */
102 static void stamp_repr(Inkscape::NodePath::Path *np);
103 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
104 static gchar *create_typestr(Inkscape::NodePath::Path *np);
106 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
108 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
110 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
112 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
114 /* Adjust handle placement, if the node or the other handle is moved */
115 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
116 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
118 /* Node event callbacks */
119 static void node_clicked(SPKnot *knot, guint state, gpointer data);
120 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
121 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
122 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
124 /* Handle event callbacks */
125 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
126 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
127 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
128 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
129 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
130 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
132 /* Constructors and destructors */
134 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
135 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
136 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
137 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
138 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
139                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
140 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
142 /* Helpers */
144 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
145 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
146 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
148 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
149 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
151 // active_node indicates mouseover node
152 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
154 /**
155  * \brief Creates new nodepath from item
156  */
157 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
159     Inkscape::XML::Node *repr = object->repr;
161     /** \todo
162      * FIXME: remove this. We don't want to edit paths inside flowtext.
163      * Instead we will build our flowtext with cloned paths, so that the
164      * real paths are outside the flowtext and thus editable as usual.
165      */
166     if (SP_IS_FLOWTEXT(object)) {
167         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
168             if SP_IS_FLOWREGION(child) {
169                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
170                 if (grandchild && SP_IS_PATH(grandchild)) {
171                     object = SP_ITEM(grandchild);
172                     break;
173                 }
174             }
175         }
176     }
178     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
180     if (curve == NULL)
181         return NULL;
183     NArtBpath *bpath = sp_curve_first_bpath(curve);
184     gint length = curve->end;
185     if (length == 0) {
186         sp_curve_unref(curve);
187         return NULL; // prevent crash for one-node paths
188     }
190     //Create new nodepath
191     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
192     if (!np) {
193         sp_curve_unref(curve);
194         return NULL;
195     }
197     // Set defaults
198     np->desktop     = desktop;
199     np->object      = object;
200     np->subpaths    = NULL;
201     np->selected    = NULL;
202     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
203     np->livarot_path = NULL;
204     np->local_change = 0;
205     np->show_handles = show_handles;
206     np->helper_path = NULL;
207     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
208     np->helperpath_width = 1.0;
209     np->curve = sp_curve_copy(curve);
210     np->show_helperpath = prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1;
211     np->straight_path = false;
212     if (IS_LIVEPATHEFFECT(object) && item) {
213         np->item = item;
214     } else {
215         np->item = SP_ITEM(object);
216     }
218     // we need to update item's transform from the repr here,
219     // because they may be out of sync when we respond
220     // to a change in repr by regenerating nodepath     --bb
221     sp_object_read_attr(SP_OBJECT(np->item), "transform");
223     np->i2d  = sp_item_i2d_affine(np->item);
224     np->d2i  = np->i2d.inverse();
226     np->repr = repr;
227     if (repr_key_in) { // apparantly the object is an LPEObject
228         np->repr_key = g_strdup(repr_key_in);
229         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
230         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
231         if (lpeparam) {
232             lpeparam->param_setup_nodepath(np);
233         }
234     } else {
235         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
236         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
237             np->repr_key = g_strdup("inkscape:original-d");
239             LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(SP_LPE_ITEM(np->object));
240             if (lpeobj && lpeobj->lpe) {
241                 lpeobj->lpe->setup_nodepath(np);
242             }
243         } else {
244             np->repr_key = g_strdup("d");
245         }
246     }
248     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
249     gchar *typestr = parse_nodetypes(nodetypes, length);
251     // create the subpath(s) from the bpath
252     NArtBpath *b = bpath;
253     while (b->code != NR_END) {
254         b = subpath_from_bpath(np, b, typestr + (b - bpath));
255     }
257     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
258     np->subpaths = g_list_reverse(np->subpaths);
260     g_free(typestr);
261     sp_curve_unref(curve);
263     // create the livarot representation from the same item
264     sp_nodepath_ensure_livarot_path(np);
266     // Draw helper curve
267     if (np->show_helperpath) {
268         SPCurve *helper_curve = sp_curve_copy(np->curve);
269         sp_curve_transform(helper_curve, np->i2d );
270         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
271         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);
272         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
273         sp_canvas_item_move_to_z(np->helper_path, 0);
274         sp_canvas_item_show(np->helper_path);
275         sp_curve_unref(helper_curve);
276     }
278     return np;
281 /**
282  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
283  */
284 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
286     if (!np)  //soft fail, like delete
287         return;
289     while (np->subpaths) {
290         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
291     }
293     //Inform the ShapeEditor that made me, if any, that I am gone.
294     if (np->shape_editor)
295         np->shape_editor->nodepath_destroyed();
297     g_assert(!np->selected);
299     if (np->livarot_path) {
300         delete np->livarot_path;
301         np->livarot_path = NULL;
302     }
304     if (np->helper_path) {
305         GtkObject *temp = np->helper_path;
306         np->helper_path = NULL;
307         gtk_object_destroy(temp);
308     }
309     if (np->curve) {
310         sp_curve_unref(np->curve);
311         np->curve = NULL;
312     }
314     if (np->repr_key) {
315         g_free(np->repr_key);
316         np->repr_key = NULL;
317     }
318     if (np->repr_nodetypes_key) {
319         g_free(np->repr_nodetypes_key);
320         np->repr_nodetypes_key = NULL;
321     }
323     np->desktop = NULL;
325     g_free(np);
329 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
331     if (np && np->livarot_path == NULL) {
332         SPCurve *curve = create_curve(np);
333         NArtBpath *bpath = SP_CURVE_BPATH(curve);
334         np->livarot_path = bpath_to_Path(bpath);
336         if (np->livarot_path)
337             np->livarot_path->ConvertWithBackData(0.01);
339         sp_curve_unref(curve);
340     }
344 /**
345  *  Return the node count of a given NodeSubPath.
346  */
347 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
349     if (!subpath)
350         return 0;
351     gint nodeCount = g_list_length(subpath->nodes);
352     return nodeCount;
355 /**
356  *  Return the node count of a given NodePath.
357  */
358 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
360     if (!np)
361         return 0;
362     gint nodeCount = 0;
363     for (GList *item = np->subpaths ; item ; item=item->next) {
364        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
365         nodeCount += g_list_length(subpath->nodes);
366     }
367     return nodeCount;
370 /**
371  *  Return the subpath count of a given NodePath.
372  */
373 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
375     if (!np)
376         return 0;
377     return g_list_length (np->subpaths);
380 /**
381  *  Return the selected node count of a given NodePath.
382  */
383 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
385     if (!np)
386         return 0;
387     return g_list_length (np->selected);
390 /**
391  *  Return the number of subpaths where nodes are selected in a given NodePath.
392  */
393 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
395     if (!np)
396         return 0;
397     if (!np->selected)
398         return 0;
399     if (!np->selected->next)
400         return 1;
401     gint count = 0;
402     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
403         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
404         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
405             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
406             if (node->selected) {
407                 count ++;
408                 break;
409             }
410         }
411     }
412     return count;
415 /**
416  * Clean up a nodepath after editing.
417  *
418  * Currently we are deleting trivial subpaths.
419  */
420 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
422     GList *badSubPaths = NULL;
424     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
425     for (GList *l = nodepath->subpaths; l ; l=l->next) {
426        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
427        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
428             badSubPaths = g_list_append(badSubPaths, sp);
429     }
431     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
432     //also removes the subpath from nodepath->subpaths
433     for (GList *l = badSubPaths; l ; l=l->next) {
434        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
435         sp_nodepath_subpath_destroy(sp);
436     }
438     g_list_free(badSubPaths);
441 /**
442  * Create new nodepath from b, make it subpath of np.
443  * \param t The node type.
444  * \todo Fixme: t should be a proper type, rather than gchar
445  */
446 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
448     NR::Point ppos, pos, npos;
450     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
452     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
453     bool const closed = (b->code == NR_MOVETO);
455     pos = NR::Point(b->x3, b->y3) * np->i2d;
456     if (b[1].code == NR_CURVETO) {
457         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
458     } else {
459         npos = pos;
460     }
461     Inkscape::NodePath::Node *n;
462     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
463     g_assert(sp->first == n);
464     g_assert(sp->last  == n);
466     b++;
467     t++;
468     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
469         pos = NR::Point(b->x3, b->y3) * np->i2d;
470         if (b->code == NR_CURVETO) {
471             ppos = NR::Point(b->x2, b->y2) * np->i2d;
472         } else {
473             ppos = pos;
474         }
475         if (b[1].code == NR_CURVETO) {
476             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
477         } else {
478             npos = pos;
479         }
480         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
481         b++;
482         t++;
483     }
485     if (closed) sp_nodepath_subpath_close(sp);
487     return b;
490 /**
491  * Convert from sodipodi:nodetypes to new style type string.
492  */
493 static gchar *parse_nodetypes(gchar const *types, gint length)
495     g_assert(length > 0);
497     gchar *typestr = g_new(gchar, length + 1);
499     gint pos = 0;
501     if (types) {
502         for (gint i = 0; types[i] && ( i < length ); i++) {
503             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
504             if (types[i] != '\0') {
505                 switch (types[i]) {
506                     case 's':
507                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
508                         break;
509                     case 'z':
510                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
511                         break;
512                     case 'c':
513                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
514                         break;
515                     default:
516                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
517                         break;
518                 }
519             }
520         }
521     }
523     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
525     return typestr;
528 /**
529  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
530  * updated but repr is not (for speed). Used during curve and node drag.
531  */
532 static void update_object(Inkscape::NodePath::Path *np)
534     g_assert(np);
536     sp_curve_unref(np->curve);
537     np->curve = create_curve(np);
539     sp_nodepath_set_curve(np, np->curve);
541     if (np->show_helperpath) {
542         SPCurve * helper_curve = sp_curve_copy(np->curve);
543         sp_curve_transform(helper_curve, np->i2d );
544         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
545         sp_curve_unref(helper_curve);
546     }
549 /**
550  * Update XML path node with data from path object.
551  */
552 static void update_repr_internal(Inkscape::NodePath::Path *np)
554     g_assert(np);
556     Inkscape::XML::Node *repr = np->object->repr;
558     sp_curve_unref(np->curve);
559     np->curve = create_curve(np);
561     gchar *typestr = create_typestr(np);
562     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
564     // determine if path has an effect applied and write to correct "d" attribute.
565     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
566         np->local_change++;
567         repr->setAttribute(np->repr_key, svgpath);
568     }
570     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
571         np->local_change++;
572         repr->setAttribute(np->repr_nodetypes_key, typestr);
573     }
575     g_free(svgpath);
576     g_free(typestr);
578     if (np->show_helperpath) {
579         SPCurve * helper_curve = sp_curve_copy(np->curve);
580         sp_curve_transform(helper_curve, np->i2d );
581         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
582         sp_curve_unref(helper_curve);
583     }
584  }
586 /**
587  * Update XML path node with data from path object, commit changes forever.
588  */
589 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
591     //fixme: np can be NULL, so check before proceeding
592     g_return_if_fail(np != NULL);
594     if (np->livarot_path) {
595         delete np->livarot_path;
596         np->livarot_path = NULL;
597     }
599     update_repr_internal(np);
600     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
602     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
603                      annotation);
606 /**
607  * Update XML path node with data from path object, commit changes with undo.
608  */
609 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
611     if (np->livarot_path) {
612         delete np->livarot_path;
613         np->livarot_path = NULL;
614     }
616     update_repr_internal(np);
617     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
618                            annotation);
621 /**
622  * Make duplicate of path, replace corresponding XML node in tree, commit.
623  */
624 static void stamp_repr(Inkscape::NodePath::Path *np)
626     g_assert(np);
628     Inkscape::XML::Node *old_repr = np->object->repr;
629     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
631     // remember the position of the item
632     gint pos = old_repr->position();
633     // remember parent
634     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
636     SPCurve *curve = create_curve(np);
637     gchar *typestr = create_typestr(np);
639     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
641     new_repr->setAttribute(np->repr_key, svgpath);
642     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
644     // add the new repr to the parent
645     parent->appendChild(new_repr);
646     // move to the saved position
647     new_repr->setPosition(pos > 0 ? pos : 0);
649     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
650                      _("Stamp"));
652     Inkscape::GC::release(new_repr);
653     g_free(svgpath);
654     g_free(typestr);
655     sp_curve_unref(curve);
658 /**
659  * Create curve from path.
660  */
661 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
663     SPCurve *curve = sp_curve_new();
665     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
666        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
667         sp_curve_moveto(curve,
668                         sp->first->pos * np->d2i);
669        Inkscape::NodePath::Node *n = sp->first->n.other;
670         while (n) {
671             NR::Point const end_pt = n->pos * np->d2i;
672             switch (n->code) {
673                 case NR_LINETO:
674                     sp_curve_lineto(curve, end_pt);
675                     break;
676                 case NR_CURVETO:
677                     sp_curve_curveto(curve,
678                                      n->p.other->n.pos * np->d2i,
679                                      n->p.pos * np->d2i,
680                                      end_pt);
681                     break;
682                 default:
683                     g_assert_not_reached();
684                     break;
685             }
686             if (n != sp->last) {
687                 n = n->n.other;
688             } else {
689                 n = NULL;
690             }
691         }
692         if (sp->closed) {
693             sp_curve_closepath(curve);
694         }
695     }
697     return curve;
700 /**
701  * Convert path type string to sodipodi:nodetypes style.
702  */
703 static gchar *create_typestr(Inkscape::NodePath::Path *np)
705     gchar *typestr = g_new(gchar, 32);
706     gint len = 32;
707     gint pos = 0;
709     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
710        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
712         if (pos >= len) {
713             typestr = g_renew(gchar, typestr, len + 32);
714             len += 32;
715         }
717         typestr[pos++] = 'c';
719        Inkscape::NodePath::Node *n;
720         n = sp->first->n.other;
721         while (n) {
722             gchar code;
724             switch (n->type) {
725                 case Inkscape::NodePath::NODE_CUSP:
726                     code = 'c';
727                     break;
728                 case Inkscape::NodePath::NODE_SMOOTH:
729                     code = 's';
730                     break;
731                 case Inkscape::NodePath::NODE_SYMM:
732                     code = 'z';
733                     break;
734                 default:
735                     g_assert_not_reached();
736                     code = '\0';
737                     break;
738             }
740             if (pos >= len) {
741                 typestr = g_renew(gchar, typestr, len + 32);
742                 len += 32;
743             }
745             typestr[pos++] = code;
747             if (n != sp->last) {
748                 n = n->n.other;
749             } else {
750                 n = NULL;
751             }
752         }
753     }
755     if (pos >= len) {
756         typestr = g_renew(gchar, typestr, len + 1);
757         len += 1;
758     }
760     typestr[pos++] = '\0';
762     return typestr;
765 /**
766  * Returns current path in context. // later eliminate this function at all!
767  */
768 static Inkscape::NodePath::Path *sp_nodepath_current()
770     if (!SP_ACTIVE_DESKTOP) {
771         return NULL;
772     }
774     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
776     if (!SP_IS_NODE_CONTEXT(event_context)) {
777         return NULL;
778     }
780     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
785 /**
786  \brief Fills node and handle positions for three nodes, splitting line
787   marked by end at distance t.
788  */
789 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
791     g_assert(new_path != NULL);
792     g_assert(end      != NULL);
794     g_assert(end->p.other == new_path);
795    Inkscape::NodePath::Node *start = new_path->p.other;
796     g_assert(start);
798     if (end->code == NR_LINETO) {
799         new_path->type =Inkscape::NodePath::NODE_CUSP;
800         new_path->code = NR_LINETO;
801         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
802     } else {
803         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
804         new_path->code = NR_CURVETO;
805         gdouble s      = 1 - t;
806         for (int dim = 0; dim < 2; dim++) {
807             NR::Coord const f000 = start->pos[dim];
808             NR::Coord const f001 = start->n.pos[dim];
809             NR::Coord const f011 = end->p.pos[dim];
810             NR::Coord const f111 = end->pos[dim];
811             NR::Coord const f00t = s * f000 + t * f001;
812             NR::Coord const f01t = s * f001 + t * f011;
813             NR::Coord const f11t = s * f011 + t * f111;
814             NR::Coord const f0tt = s * f00t + t * f01t;
815             NR::Coord const f1tt = s * f01t + t * f11t;
816             NR::Coord const fttt = s * f0tt + t * f1tt;
817             start->n.pos[dim]    = f00t;
818             new_path->p.pos[dim] = f0tt;
819             new_path->pos[dim]   = fttt;
820             new_path->n.pos[dim] = f1tt;
821             end->p.pos[dim]      = f11t;
822         }
823     }
826 /**
827  * Adds new node on direct line between two nodes, activates handles of all
828  * three nodes.
829  */
830 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
832     g_assert(end);
833     g_assert(end->subpath);
834     g_assert(g_list_find(end->subpath->nodes, end));
836    Inkscape::NodePath::Node *start = end->p.other;
837     g_assert( start->n.other == end );
838    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
839                                                end,
840                                                (NRPathcode)end->code == NR_LINETO?
841                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
842                                                (NRPathcode)end->code,
843                                                &start->pos, &start->pos, &start->n.pos);
844     sp_nodepath_line_midpoint(newnode, end, t);
846     sp_node_adjust_handles(start);
847     sp_node_update_handles(start);
848     sp_node_update_handles(newnode);
849     sp_node_adjust_handles(end);
850     sp_node_update_handles(end);
852     return newnode;
855 /**
856 \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
857 */
858 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
860     g_assert(node);
861     g_assert(node->subpath);
862     g_assert(g_list_find(node->subpath->nodes, node));
864    Inkscape::NodePath::SubPath *sp = node->subpath;
865     Inkscape::NodePath::Path *np    = sp->nodepath;
867     if (sp->closed) {
868         sp_nodepath_subpath_open(sp, node);
869         return sp->first;
870     } else {
871         // no break for end nodes
872         if (node == sp->first) return NULL;
873         if (node == sp->last ) return NULL;
875         // create a new subpath
876        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
878         // duplicate the break node as start of the new subpath
879        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
881         while (node->n.other) { // copy the remaining nodes into the new subpath
882            Inkscape::NodePath::Node *n  = node->n.other;
883            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);
884             if (n->selected) {
885                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
886             }
887             sp_nodepath_node_destroy(n); // remove the point on the original subpath
888         }
890         return newnode;
891     }
894 /**
895  * Duplicate node and connect to neighbours.
896  */
897 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
899     g_assert(node);
900     g_assert(node->subpath);
901     g_assert(g_list_find(node->subpath->nodes, node));
903    Inkscape::NodePath::SubPath *sp = node->subpath;
905     NRPathcode code = (NRPathcode) node->code;
906     if (code == NR_MOVETO) { // if node is the endnode,
907         node->code = NR_LINETO; // new one is inserted before it, so change that to line
908     }
910     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
912     if (!node->n.other || !node->p.other) // if node is an endnode, select it
913         return node;
914     else
915         return newnode; // otherwise select the newly created node
918 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
920     node->p.pos = (node->pos + (node->pos - node->n.pos));
923 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
925     node->n.pos = (node->pos + (node->pos - node->p.pos));
928 /**
929  * Change line type at node, with side effects on neighbours.
930  */
931 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
933     g_assert(end);
934     g_assert(end->subpath);
935     g_assert(end->p.other);
937     if (end->code == static_cast< guint > ( code ) )
938         return;
940    Inkscape::NodePath::Node *start = end->p.other;
942     end->code = code;
944     if (code == NR_LINETO) {
945         if (start->code == NR_LINETO) {
946             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
947         }
948         if (end->n.other) {
949             if (end->n.other->code == NR_LINETO) {
950                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
951             }
952         }
953     } else {
954         NR::Point delta = end->pos - start->pos;
955         start->n.pos = start->pos + delta / 3;
956         end->p.pos = end->pos - delta / 3;
957         sp_node_adjust_handle(start, 1);
958         sp_node_adjust_handle(end, -1);
959     }
961     sp_node_update_handles(start);
962     sp_node_update_handles(end);
965 /**
966  * Change node type, and its handles accordingly.
967  */
968 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
970     g_assert(node);
971     g_assert(node->subpath);
973     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
974         return node;
976     if ((node->p.other != NULL) && (node->n.other != NULL)) {
977         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
978             type =Inkscape::NodePath::NODE_CUSP;
979         }
980     }
982     node->type = type;
984     if (node->type == Inkscape::NodePath::NODE_CUSP) {
985         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
986         node->knot->setSize (node->selected? 11 : 9);
987         sp_knot_update_ctrl(node->knot);
988     } else {
989         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
990         node->knot->setSize (node->selected? 9 : 7);
991         sp_knot_update_ctrl(node->knot);
992     }
994     // if one of handles is mouseovered, preserve its position
995     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
996         sp_node_adjust_handle(node, 1);
997     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
998         sp_node_adjust_handle(node, -1);
999     } else {
1000         sp_node_adjust_handles(node);
1001     }
1003     sp_node_update_handles(node);
1005     sp_nodepath_update_statusbar(node->subpath->nodepath);
1007     return node;
1010 /**
1011  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
1012  * adjacent segments from lines to curves.
1013 */
1014 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1016     bool p_line = (node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos);
1017     bool n_line = (node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos);
1019     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1020         if (p_line && n_line) {
1021             // only if both adjacent segments are lines,
1022             // convert both to curves:
1024             node->code = NR_CURVETO;
1025             node->n.other->code = NR_CURVETO;
1027             NR::Point leg_prev = node->pos - node->p.other->pos;
1028             NR::Point leg_next = node->pos - node->n.other->pos;
1030             double norm_leg_prev = L2(leg_prev);
1031             double norm_leg_next = L2(leg_next);
1033             // delta has length 1 and is orthogonal to bisecting line
1034             NR::Point delta;
1035             if (norm_leg_next > 0.0) {
1036                 delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1037                 (&delta)->normalize();
1038             }
1040             if (type == Inkscape::NodePath::NODE_SYMM) {
1041                 double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1042                 node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1043                 node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1044             } else {
1045                 // length of handle is proportional to distance to adjacent node
1046                 node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1047                 node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1048             }
1050             sp_node_update_handles(node);
1051         }
1052     }
1054     sp_nodepath_set_node_type (node, type);
1057 /**
1058  * Move node to point, and adjust its and neighbouring handles.
1059  */
1060 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1062     NR::Point delta = p - node->pos;
1063     node->pos = p;
1065     node->p.pos += delta;
1066     node->n.pos += delta;
1068     Inkscape::NodePath::Node *node_p = NULL;
1069     Inkscape::NodePath::Node *node_n = NULL;
1071     if (node->p.other) {
1072         if (node->code == NR_LINETO) {
1073             sp_node_adjust_handle(node, 1);
1074             sp_node_adjust_handle(node->p.other, -1);
1075             node_p = node->p.other;
1076         }
1077     }
1078     if (node->n.other) {
1079         if (node->n.other->code == NR_LINETO) {
1080             sp_node_adjust_handle(node, -1);
1081             sp_node_adjust_handle(node->n.other, 1);
1082             node_n = node->n.other;
1083         }
1084     }
1086     // this function is only called from batch movers that will update display at the end
1087     // themselves, so here we just move all the knots without emitting move signals, for speed
1088     sp_node_update_handles(node, false);
1089     if (node_n) {
1090         sp_node_update_handles(node_n, false);
1091     }
1092     if (node_p) {
1093         sp_node_update_handles(node_p, false);
1094     }
1097 /**
1098  * Call sp_node_moveto() for node selection and handle possible snapping.
1099  */
1100 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1101                                             bool const snap = true)
1103     NR::Coord best = NR_HUGE;
1104     NR::Point delta(dx, dy);
1105     NR::Point best_pt = delta;
1106     Inkscape::SnappedPoint best_abs;
1108     
1109     if (snap) {    
1110         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1111          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1112          * must provide that information. */
1113           
1114         // Build a list of the unselected nodes to which the snapper should snap 
1115         std::vector<NR::Point> unselected_nodes;
1116         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1117             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1118             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1119                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1120                 if (!node->selected) {
1121                     unselected_nodes.push_back(node->pos);
1122                 }    
1123             }
1124         }        
1125         
1126         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1127         
1128         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1129             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1130             Inkscape::SnappedPoint s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);            
1131             if (s.getDistance() < best) {
1132                 best = s.getDistance();
1133                 best_abs = s;
1134                 best_pt = s.getPoint() - n->pos;
1135             }
1136         }
1137                         
1138         if (best_abs.getSnapped()) {
1139             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1140         }
1141     }
1143     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1144         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1145         sp_node_moveto(n, n->pos + best_pt);
1146     }
1148     // do not update repr here so that node dragging is acceptably fast
1149     update_object(nodepath);
1152 /**
1153 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1154 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1155 near x = 0.
1156  */
1157 double
1158 sculpt_profile (double x, double alpha, guint profile)
1160     if (x >= 1)
1161         return 0;
1162     if (x <= 0)
1163         return 1;
1165     switch (profile) {
1166         case SCULPT_PROFILE_LINEAR:
1167         return 1 - x;
1168         case SCULPT_PROFILE_BELL:
1169         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1170         case SCULPT_PROFILE_ELLIPTIC:
1171         return sqrt(1 - x*x);
1172     }
1174     return 1;
1177 double
1178 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1180     // extremely primitive for now, don't have time to look for the real one
1181     double lower = NR::L2(b - a);
1182     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1183     return (lower + upper)/2;
1186 void
1187 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1189     n->pos = n->origin + delta;
1190     n->n.pos = n->n.origin + delta_n;
1191     n->p.pos = n->p.origin + delta_p;
1192     sp_node_adjust_handles(n);
1193     sp_node_update_handles(n, false);
1196 /**
1197  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1198  * on how far they are from the dragged node n.
1199  */
1200 static void
1201 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1203     g_assert (n);
1204     g_assert (nodepath);
1205     g_assert (n->subpath->nodepath == nodepath);
1207     double pressure = n->knot->pressure;
1208     if (pressure == 0)
1209         pressure = 0.5; // default
1210     pressure = CLAMP (pressure, 0.2, 0.8);
1212     // map pressure to alpha = 1/5 ... 5
1213     double alpha = 1 - 2 * fabs(pressure - 0.5);
1214     if (pressure > 0.5)
1215         alpha = 1/alpha;
1217     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1219     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1220         // Only one subpath has selected nodes:
1221         // use linear mode, where the distance from n to node being dragged is calculated along the path
1223         double n_sel_range = 0, p_sel_range = 0;
1224         guint n_nodes = 0, p_nodes = 0;
1225         guint n_sel_nodes = 0, p_sel_nodes = 0;
1227         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1228         {
1229             double n_range = 0, p_range = 0;
1230             bool n_going = true, p_going = true;
1231             Inkscape::NodePath::Node *n_node = n;
1232             Inkscape::NodePath::Node *p_node = n;
1233             do {
1234                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1235                 if (n_node && n_going)
1236                     n_node = n_node->n.other;
1237                 if (n_node == NULL) {
1238                     n_going = false;
1239                 } else {
1240                     n_nodes ++;
1241                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1242                     if (n_node->selected) {
1243                         n_sel_nodes ++;
1244                         n_sel_range = n_range;
1245                     }
1246                     if (n_node == p_node) {
1247                         n_going = false;
1248                         p_going = false;
1249                     }
1250                 }
1251                 if (p_node && p_going)
1252                     p_node = p_node->p.other;
1253                 if (p_node == NULL) {
1254                     p_going = false;
1255                 } else {
1256                     p_nodes ++;
1257                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1258                     if (p_node->selected) {
1259                         p_sel_nodes ++;
1260                         p_sel_range = p_range;
1261                     }
1262                     if (p_node == n_node) {
1263                         n_going = false;
1264                         p_going = false;
1265                     }
1266                 }
1267             } while (n_going || p_going);
1268         }
1270         // Second pass: actually move nodes in this subpath
1271         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1272         {
1273             double n_range = 0, p_range = 0;
1274             bool n_going = true, p_going = true;
1275             Inkscape::NodePath::Node *n_node = n;
1276             Inkscape::NodePath::Node *p_node = n;
1277             do {
1278                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1279                 if (n_node && n_going)
1280                     n_node = n_node->n.other;
1281                 if (n_node == NULL) {
1282                     n_going = false;
1283                 } else {
1284                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1285                     if (n_node->selected) {
1286                         sp_nodepath_move_node_and_handles (n_node,
1287                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1288                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1289                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1290                     }
1291                     if (n_node == p_node) {
1292                         n_going = false;
1293                         p_going = false;
1294                     }
1295                 }
1296                 if (p_node && p_going)
1297                     p_node = p_node->p.other;
1298                 if (p_node == NULL) {
1299                     p_going = false;
1300                 } else {
1301                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1302                     if (p_node->selected) {
1303                         sp_nodepath_move_node_and_handles (p_node,
1304                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1305                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1306                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1307                     }
1308                     if (p_node == n_node) {
1309                         n_going = false;
1310                         p_going = false;
1311                     }
1312                 }
1313             } while (n_going || p_going);
1314         }
1316     } else {
1317         // Multiple subpaths have selected nodes:
1318         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1319         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1320         // fix the pear-like shape when sculpting e.g. a ring
1322         // First pass: calculate range
1323         gdouble direct_range = 0;
1324         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1325             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1326             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1327                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1328                 if (node->selected) {
1329                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1330                 }
1331             }
1332         }
1334         // Second pass: actually move nodes
1335         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1336             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1337             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1338                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1339                 if (node->selected) {
1340                     if (direct_range > 1e-6) {
1341                         sp_nodepath_move_node_and_handles (node,
1342                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1343                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1344                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1345                     } else {
1346                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1347                     }
1349                 }
1350             }
1351         }
1352     }
1354     // do not update repr here so that node dragging is acceptably fast
1355     update_object(nodepath);
1359 /**
1360  * Move node selection to point, adjust its and neighbouring handles,
1361  * handle possible snapping, and commit the change with possible undo.
1362  */
1363 void
1364 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1366     if (!nodepath) return;
1368     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1370     if (dx == 0) {
1371         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1372     } else if (dy == 0) {
1373         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1374     } else {
1375         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1376     }
1379 /**
1380  * Move node selection off screen and commit the change.
1381  */
1382 void
1383 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1385     // borrowed from sp_selection_move_screen in selection-chemistry.c
1386     // we find out the current zoom factor and divide deltas by it
1387     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1389     gdouble zoom = desktop->current_zoom();
1390     gdouble zdx = dx / zoom;
1391     gdouble zdy = dy / zoom;
1393     if (!nodepath) return;
1395     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1397     if (dx == 0) {
1398         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1399     } else if (dy == 0) {
1400         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1401     } else {
1402         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1403     }
1406 /**
1407  * Move selected nodes to the absolute position given
1408  */
1409 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, NR::Coord val, NR::Dim2 axis)
1411     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1412         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1413         NR::Point npos(axis == NR::X ? val : n->pos[NR::X], axis == NR::Y ? val : n->pos[NR::Y]);
1414         sp_node_moveto(n, npos);
1415     }
1417     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1420 /**
1421  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1422  */
1423 NR::Maybe<NR::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1425     NR::Maybe<NR::Coord> no_coord = NR::Nothing();
1426     g_return_val_if_fail(nodepath->selected, no_coord);
1428     // determine coordinate of first selected node
1429     GList *nsel = nodepath->selected;
1430     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1431     NR::Coord coord = n->pos[axis];
1432     bool coincide = true;
1434     // compare it to the coordinates of all the other selected nodes
1435     for (GList *l = nsel->next; l != NULL; l = l->next) {
1436         n = (Inkscape::NodePath::Node *) l->data;
1437         if (n->pos[axis] != coord) {
1438             coincide = false;
1439         }
1440     }
1441     if (coincide) {
1442         return coord;
1443     } else {
1444         NR::Rect bbox = sp_node_selected_bbox(nodepath);
1445         // currently we return the coordinate of the bounding box midpoint because I don't know how
1446         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1447         return bbox.midpoint()[axis];
1448     }
1451 /** If they don't yet exist, creates knot and line for the given side of the node */
1452 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1454     if (!side->knot) {
1455         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"));
1457         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1458         side->knot->setSize (7);
1459         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1460         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1461         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1462         sp_knot_update_ctrl(side->knot);
1464         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1465         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1466         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1467         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1468         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1469         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1470     }
1472     if (!side->line) {
1473         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1474                                         SP_TYPE_CTRLLINE, NULL);
1475     }
1478 /**
1479  * Ensure the given handle of the node is visible/invisible, update its screen position
1480  */
1481 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1483     g_assert(node != NULL);
1485    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1486     NRPathcode code = sp_node_path_code_from_side(node, side);
1488     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1490     if (show_handle) {
1491         if (!side->knot) { // No handle knot at all
1492             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1493             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1494             side->knot->pos = side->pos;
1495             if (side->knot->item)
1496                 SP_CTRL(side->knot->item)->moveto(side->pos);
1497             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1498             sp_knot_show(side->knot);
1499         } else {
1500             if (side->knot->pos != side->pos) { // only if it's really moved
1501                 if (fire_move_signals) {
1502                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1503                 } else {
1504                     sp_knot_moveto(side->knot, &side->pos);
1505                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1506                 }
1507             }
1508             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1509                 sp_knot_show(side->knot);
1510             }
1511         }
1512         sp_canvas_item_show(side->line);
1513     } else {
1514         if (side->knot) {
1515             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1516                 sp_knot_hide(side->knot);
1517             }
1518         }
1519         if (side->line) {
1520             sp_canvas_item_hide(side->line);
1521         }
1522     }
1525 /**
1526  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1527  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1528  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1529  * updated; otherwise, just move the knots silently (used in batch moves).
1530  */
1531 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1533     g_assert(node != NULL);
1535     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1536         sp_knot_show(node->knot);
1537     }
1539     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1540         if (fire_move_signals)
1541             sp_knot_set_position(node->knot, &node->pos, 0);
1542         else
1543             sp_knot_moveto(node->knot, &node->pos);
1544     }
1546     gboolean show_handles = node->selected;
1547     if (node->p.other != NULL) {
1548         if (node->p.other->selected) show_handles = TRUE;
1549     }
1550     if (node->n.other != NULL) {
1551         if (node->n.other->selected) show_handles = TRUE;
1552     }
1554     if (node->subpath->nodepath->show_handles == false)
1555         show_handles = FALSE;
1557     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1558     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1561 /**
1562  * Call sp_node_update_handles() for all nodes on subpath.
1563  */
1564 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1566     g_assert(subpath != NULL);
1568     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1569         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1570     }
1573 /**
1574  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1575  */
1576 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1578     g_assert(nodepath != NULL);
1580     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1581         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1582     }
1585 void
1586 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1588     if (nodepath == NULL) return;
1590     nodepath->show_handles = show;
1591     sp_nodepath_update_handles(nodepath);
1594 /**
1595  * Adds all selected nodes in nodepath to list.
1596  */
1597 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1599     StlConv<Node *>::list(l, selected);
1600 /// \todo this adds a copying, rework when the selection becomes a stl list
1603 /**
1604  * Align selected nodes on the specified axis.
1605  */
1606 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1608     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1609         return;
1610     }
1612     if ( !nodepath->selected->next ) { // only one node selected
1613         return;
1614     }
1615    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1616     NR::Point dest(pNode->pos);
1617     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1618         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1619         if (pNode) {
1620             dest[axis] = pNode->pos[axis];
1621             sp_node_moveto(pNode, dest);
1622         }
1623     }
1625     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1628 /// Helper struct.
1629 struct NodeSort
1631    Inkscape::NodePath::Node *_node;
1632     NR::Coord _coord;
1633     /// \todo use vectorof pointers instead of calling copy ctor
1634     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1635         _node(node), _coord(node->pos[axis])
1636     {}
1638 };
1640 static bool operator<(NodeSort const &a, NodeSort const &b)
1642     return (a._coord < b._coord);
1645 /**
1646  * Distribute selected nodes on the specified axis.
1647  */
1648 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1650     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1651         return;
1652     }
1654     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1655         return;
1656     }
1658    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1659     std::vector<NodeSort> sorted;
1660     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1661         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1662         if (pNode) {
1663             NodeSort n(pNode, axis);
1664             sorted.push_back(n);
1665             //dest[axis] = pNode->pos[axis];
1666             //sp_node_moveto(pNode, dest);
1667         }
1668     }
1669     std::sort(sorted.begin(), sorted.end());
1670     unsigned int len = sorted.size();
1671     //overall bboxes span
1672     float dist = (sorted.back()._coord -
1673                   sorted.front()._coord);
1674     //new distance between each bbox
1675     float step = (dist) / (len - 1);
1676     float pos = sorted.front()._coord;
1677     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1678           it < sorted.end();
1679           it ++ )
1680     {
1681         NR::Point dest((*it)._node->pos);
1682         dest[axis] = pos;
1683         sp_node_moveto((*it)._node, dest);
1684         pos += step;
1685     }
1687     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1691 /**
1692  * Call sp_nodepath_line_add_node() for all selected segments.
1693  */
1694 void
1695 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1697     if (!nodepath) {
1698         return;
1699     }
1701     GList *nl = NULL;
1703     int n_added = 0;
1705     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1706        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1707         g_assert(t->selected);
1708         if (t->p.other && t->p.other->selected) {
1709             nl = g_list_prepend(nl, t);
1710         }
1711     }
1713     while (nl) {
1714        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1715        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1716        sp_nodepath_node_select(n, TRUE, FALSE);
1717        n_added ++;
1718        nl = g_list_remove(nl, t);
1719     }
1721     /** \todo fixme: adjust ? */
1722     sp_nodepath_update_handles(nodepath);
1724     if (n_added > 1) {
1725         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1726     } else if (n_added > 0) {
1727         sp_nodepath_update_repr(nodepath, _("Add node"));
1728     }
1730     sp_nodepath_update_statusbar(nodepath);
1733 /**
1734  * Select segment nearest to point
1735  */
1736 void
1737 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1739     if (!nodepath) {
1740         return;
1741     }
1743     sp_nodepath_ensure_livarot_path(nodepath);
1744     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1745     if (!maybe_position) {
1746         return;
1747     }
1748     Path::cut_position position = *maybe_position;
1750     //find segment to segment
1751     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1753     //fixme: this can return NULL, so check before proceeding.
1754     g_return_if_fail(e != NULL);
1756     gboolean force = FALSE;
1757     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1758         force = TRUE;
1759     }
1760     sp_nodepath_node_select(e, (gboolean) toggle, force);
1761     if (e->p.other)
1762         sp_nodepath_node_select(e->p.other, TRUE, force);
1764     sp_nodepath_update_handles(nodepath);
1766     sp_nodepath_update_statusbar(nodepath);
1769 /**
1770  * Add a node nearest to point
1771  */
1772 void
1773 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1775     if (!nodepath) {
1776         return;
1777     }
1779     sp_nodepath_ensure_livarot_path(nodepath);
1780     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1781     if (!maybe_position) {
1782         return;
1783     }
1784     Path::cut_position position = *maybe_position;
1786     //find segment to split
1787     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1789     //don't know why but t seems to flip for lines
1790     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1791         position.t = 1.0 - position.t;
1792     }
1793     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1794     sp_nodepath_node_select(n, FALSE, TRUE);
1796     /* fixme: adjust ? */
1797     sp_nodepath_update_handles(nodepath);
1799     sp_nodepath_update_repr(nodepath, _("Add node"));
1801     sp_nodepath_update_statusbar(nodepath);
1804 /*
1805  * Adjusts a segment so that t moves by a certain delta for dragging
1806  * converts lines to curves
1807  *
1808  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1809  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1810  */
1811 void
1812 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1814     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1816     //fixme: e and e->p can be NULL, so check for those before proceeding
1817     g_return_if_fail(e != NULL);
1818     g_return_if_fail(&e->p != NULL);
1820     /* feel good is an arbitrary parameter that distributes the delta between handles
1821      * if t of the drag point is less than 1/6 distance form the endpoint only
1822      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1823      */
1824     double feel_good;
1825     if (t <= 1.0 / 6.0)
1826         feel_good = 0;
1827     else if (t <= 0.5)
1828         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1829     else if (t <= 5.0 / 6.0)
1830         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1831     else
1832         feel_good = 1;
1834     //if we're dragging a line convert it to a curve
1835     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1836         sp_nodepath_set_line_type(e, NR_CURVETO);
1837     }
1839     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1840     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1841     e->p.other->n.pos += offsetcoord0;
1842     e->p.pos += offsetcoord1;
1844     // adjust handles of adjacent nodes where necessary
1845     sp_node_adjust_handle(e,1);
1846     sp_node_adjust_handle(e->p.other,-1);
1848     sp_nodepath_update_handles(e->subpath->nodepath);
1850     update_object(e->subpath->nodepath);
1852     sp_nodepath_update_statusbar(e->subpath->nodepath);
1856 /**
1857  * Call sp_nodepath_break() for all selected segments.
1858  */
1859 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1861     if (!nodepath) return;
1863     GList *temp = NULL;
1864     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1865        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1866        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1867         if (nn == NULL) continue; // no break, no new node
1868         temp = g_list_prepend(temp, nn);
1869     }
1871     if (temp) {
1872         sp_nodepath_deselect(nodepath);
1873     }
1874     for (GList *l = temp; l != NULL; l = l->next) {
1875         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1876     }
1878     sp_nodepath_update_handles(nodepath);
1880     sp_nodepath_update_repr(nodepath, _("Break path"));
1883 /**
1884  * Duplicate the selected node(s).
1885  */
1886 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1888     if (!nodepath) {
1889         return;
1890     }
1892     GList *temp = NULL;
1893     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1894        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1895        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1896         if (nn == NULL) continue; // could not duplicate
1897         temp = g_list_prepend(temp, nn);
1898     }
1900     if (temp) {
1901         sp_nodepath_deselect(nodepath);
1902     }
1903     for (GList *l = temp; l != NULL; l = l->next) {
1904         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1905     }
1907     sp_nodepath_update_handles(nodepath);
1909     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1912 /**
1913  *  Internal function to join two nodes by merging them into one.
1914  */
1915 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
1917     /* a and b are endpoints */
1919     // if one of the two nodes is mouseovered, fix its position
1920     NR::Point c;
1921     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1922         c = a->pos;
1923     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1924         c = b->pos;
1925     } else {
1926         // otherwise, move joined node to the midpoint
1927         c = (a->pos + b->pos) / 2;
1928     }
1930     if (a->subpath == b->subpath) {
1931        Inkscape::NodePath::SubPath *sp = a->subpath;
1932         sp_nodepath_subpath_close(sp);
1933         sp_node_moveto (sp->first, c);
1935         sp_nodepath_update_handles(sp->nodepath);
1936         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1937         return;
1938     }
1940     /* a and b are separate subpaths */
1941     Inkscape::NodePath::SubPath *sa = a->subpath;
1942     Inkscape::NodePath::SubPath *sb = b->subpath;
1943     NR::Point p;
1944     Inkscape::NodePath::Node *n;
1945     NRPathcode code;
1946     if (a == sa->first) {
1947         // we will now reverse sa, so that a is its last node, not first, and drop that node
1948         p = sa->first->n.pos;
1949         code = (NRPathcode)sa->first->n.other->code;
1950         // create new subpath
1951        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1952        // create a first moveto node on it
1953         n = sa->last;
1954         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1955         n = n->p.other;
1956         if (n == sa->first) n = NULL;
1957         while (n) {
1958             // copy the rest of the nodes from sa to t, going backwards
1959             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1960             n = n->p.other;
1961             if (n == sa->first) n = NULL;
1962         }
1963         // replace sa with t
1964         sp_nodepath_subpath_destroy(sa);
1965         sa = t;
1966     } else if (a == sa->last) {
1967         // a is already last, just drop it
1968         p = sa->last->p.pos;
1969         code = (NRPathcode)sa->last->code;
1970         sp_nodepath_node_destroy(sa->last);
1971     } else {
1972         code = NR_END;
1973         g_assert_not_reached();
1974     }
1976     if (b == sb->first) {
1977         // copy all nodes from b to a, forward 
1978         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1979         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1980             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1981         }
1982     } else if (b == sb->last) {
1983         // copy all nodes from b to a, backward 
1984         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1985         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1986             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1987         }
1988     } else {
1989         g_assert_not_reached();
1990     }
1991     /* and now destroy sb */
1993     sp_nodepath_subpath_destroy(sb);
1995     sp_nodepath_update_handles(sa->nodepath);
1997     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1999     sp_nodepath_update_statusbar(nodepath);
2002 /**
2003  *  Internal function to join two nodes by adding a segment between them.
2004  */
2005 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2007     if (a->subpath == b->subpath) {
2008        Inkscape::NodePath::SubPath *sp = a->subpath;
2010         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2011         sp->closed = TRUE;
2013         sp->first->p.other = sp->last;
2014         sp->last->n.other  = sp->first;
2016         sp_node_handle_mirror_p_to_n(sp->last);
2017         sp_node_handle_mirror_n_to_p(sp->first);
2019         sp->first->code = sp->last->code;
2020         sp->first       = sp->last;
2022         sp_nodepath_update_handles(sp->nodepath);
2024         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2026         return;
2027     }
2029     /* a and b are separate subpaths */
2030     Inkscape::NodePath::SubPath *sa = a->subpath;
2031     Inkscape::NodePath::SubPath *sb = b->subpath;
2033     Inkscape::NodePath::Node *n;
2034     NR::Point p;
2035     NRPathcode code;
2036     if (a == sa->first) {
2037         code = (NRPathcode) sa->first->n.other->code;
2038        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2039         n = sa->last;
2040         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2041         for (n = n->p.other; n != NULL; n = n->p.other) {
2042             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2043         }
2044         sp_nodepath_subpath_destroy(sa);
2045         sa = t;
2046     } else if (a == sa->last) {
2047         code = (NRPathcode)sa->last->code;
2048     } else {
2049         code = NR_END;
2050         g_assert_not_reached();
2051     }
2053     if (b == sb->first) {
2054         n = sb->first;
2055         sp_node_handle_mirror_p_to_n(sa->last);
2056         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2057         sp_node_handle_mirror_n_to_p(sa->last);
2058         for (n = n->n.other; n != NULL; n = n->n.other) {
2059             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2060         }
2061     } else if (b == sb->last) {
2062         n = sb->last;
2063         sp_node_handle_mirror_p_to_n(sa->last);
2064         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2065         sp_node_handle_mirror_n_to_p(sa->last);
2066         for (n = n->p.other; n != NULL; n = n->p.other) {
2067             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2068         }
2069     } else {
2070         g_assert_not_reached();
2071     }
2072     /* and now destroy sb */
2074     sp_nodepath_subpath_destroy(sb);
2076     sp_nodepath_update_handles(sa->nodepath);
2078     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2081 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2083 /**
2084  * Internal function to handle joining two nodes.
2085  */
2086 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2088     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2090     if (g_list_length(nodepath->selected) != 2) {
2091         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2092         return;
2093     }
2095     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2096     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2098     g_assert(a != b);
2099     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2100         // someone tried to join an orphan node (i.e. a single-node subpath).
2101         // this is not worth an error message, just fail silently.
2102         return;
2103     }
2105     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2106         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2107         return;
2108     }
2110     switch(mode) {
2111         case NODE_JOIN_ENDPOINTS:
2112             do_node_selected_join(nodepath, a, b);
2113             break;
2114         case NODE_JOIN_SEGMENT:
2115             do_node_selected_join_segment(nodepath, a, b);
2116             break;
2117     }
2120 /**
2121  *  Join two nodes by merging them into one.
2122  */
2123 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2125     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2128 /**
2129  *  Join two nodes by adding a segment between them.
2130  */
2131 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2133     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2136 /**
2137  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2138  */
2139 void sp_node_delete_preserve(GList *nodes_to_delete)
2141     GSList *nodepaths = NULL;
2143     while (nodes_to_delete) {
2144         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2145         Inkscape::NodePath::SubPath *sp = node->subpath;
2146         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2147         Inkscape::NodePath::Node *sample_cursor = NULL;
2148         Inkscape::NodePath::Node *sample_end = NULL;
2149         Inkscape::NodePath::Node *delete_cursor = node;
2150         bool just_delete = false;
2152         //find the start of this contiguous selection
2153         //move left to the first node that is not selected
2154         //or the start of the non-closed path
2155         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2156             delete_cursor = curr;
2157         }
2159         //just delete at the beginning of an open path
2160         if (!delete_cursor->p.other) {
2161             sample_cursor = delete_cursor;
2162             just_delete = true;
2163         } else {
2164             sample_cursor = delete_cursor->p.other;
2165         }
2167         //calculate points for each segment
2168         int rate = 5;
2169         float period = 1.0 / rate;
2170         std::vector<NR::Point> data;
2171         if (!just_delete) {
2172             data.push_back(sample_cursor->pos);
2173             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2174                 //just delete at the end of an open path
2175                 if (!sp->closed && curr == sp->last) {
2176                     just_delete = true;
2177                     break;
2178                 }
2180                 //sample points on the contiguous selected segment
2181                 NR::Point *bez;
2182                 bez = new NR::Point [4];
2183                 bez[0] = curr->pos;
2184                 bez[1] = curr->n.pos;
2185                 bez[2] = curr->n.other->p.pos;
2186                 bez[3] = curr->n.other->pos;
2187                 for (int i=1; i<rate; i++) {
2188                     gdouble t = i * period;
2189                     NR::Point p = bezier_pt(3, bez, t);
2190                     data.push_back(p);
2191                 }
2192                 data.push_back(curr->n.other->pos);
2194                 sample_end = curr->n.other;
2195                 //break if we've come full circle or hit the end of the selection
2196                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2197                     break;
2198                 }
2199             }
2200         }
2202         if (!just_delete) {
2203             //calculate the best fitting single segment and adjust the endpoints
2204             NR::Point *adata;
2205             adata = new NR::Point [data.size()];
2206             copy(data.begin(), data.end(), adata);
2208             NR::Point *bez;
2209             bez = new NR::Point [4];
2210             //would decreasing error create a better fitting approximation?
2211             gdouble error = 1.0;
2212             gint ret;
2213             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2215             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2216             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2217             //the resulting nodes behave as expected.
2218             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2219             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2221             //adjust endpoints
2222             sample_cursor->n.pos = bez[1];
2223             sample_end->p.pos = bez[2];
2224         }
2226         //destroy this contiguous selection
2227         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2228             Inkscape::NodePath::Node *temp = delete_cursor;
2229             if (delete_cursor->n.other == delete_cursor) {
2230                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2231                 delete_cursor = NULL;
2232             } else {
2233                 delete_cursor = delete_cursor->n.other;
2234             }
2235             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2236             sp_nodepath_node_destroy(temp);
2237         }
2239         sp_nodepath_update_handles(nodepath);
2241         if (!g_slist_find(nodepaths, nodepath))
2242             nodepaths = g_slist_prepend (nodepaths, nodepath);
2243     }
2245     for (GSList *i = nodepaths; i; i = i->next) {
2246         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2247         // different nodepaths will give us one undo event per nodepath
2248         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2250         // if the entire nodepath is removed, delete the selected object.
2251         if (nodepath->subpaths == NULL ||
2252             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2253             //at least 2
2254             sp_nodepath_get_node_count(nodepath) < 2) {
2255             SPDocument *document = sp_desktop_document (nodepath->desktop);
2256             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2257             //delete this nodepath's object, not the entire selection! (though at this time, this
2258             //does not matter)
2259             sp_selection_delete();
2260             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2261                               _("Delete nodes"));
2262         } else {
2263             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2264             sp_nodepath_update_statusbar(nodepath);
2265         }
2266     }
2268     g_slist_free (nodepaths);
2271 /**
2272  * Delete one or more selected nodes.
2273  */
2274 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2276     if (!nodepath) return;
2277     if (!nodepath->selected) return;
2279     /** \todo fixme: do it the right way */
2280     while (nodepath->selected) {
2281        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2282         sp_nodepath_node_destroy(node);
2283     }
2286     //clean up the nodepath (such as for trivial subpaths)
2287     sp_nodepath_cleanup(nodepath);
2289     sp_nodepath_update_handles(nodepath);
2291     // if the entire nodepath is removed, delete the selected object.
2292     if (nodepath->subpaths == NULL ||
2293         sp_nodepath_get_node_count(nodepath) < 2) {
2294         SPDocument *document = sp_desktop_document (nodepath->desktop);
2295         sp_selection_delete();
2296         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2297                           _("Delete nodes"));
2298         return;
2299     }
2301     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2303     sp_nodepath_update_statusbar(nodepath);
2306 /**
2307  * Delete one or more segments between two selected nodes.
2308  * This is the code for 'split'.
2309  */
2310 void
2311 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2313    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2314    Inkscape::NodePath::Node *curr, *next;     //Iterators
2316     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2318     if (g_list_length(nodepath->selected) != 2) {
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     //Selected nodes, not inclusive
2325    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2326    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2328     if ( ( a==b)                       ||  //same node
2329          (a->subpath  != b->subpath )  ||  //not the same path
2330          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2331          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2332     {
2333         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2334                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2335         return;
2336     }
2338     //###########################################
2339     //# BEGIN EDITS
2340     //###########################################
2341     //##################################
2342     //# CLOSED PATH
2343     //##################################
2344     if (a->subpath->closed) {
2347         gboolean reversed = FALSE;
2349         //Since we can go in a circle, we need to find the shorter distance.
2350         //  a->b or b->a
2351         start = end = NULL;
2352         int distance    = 0;
2353         int minDistance = 0;
2354         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2355             if (curr==b) {
2356                 //printf("a to b:%d\n", distance);
2357                 start = a;//go from a to b
2358                 end   = b;
2359                 minDistance = distance;
2360                 //printf("A to B :\n");
2361                 break;
2362             }
2363             distance++;
2364         }
2366         //try again, the other direction
2367         distance = 0;
2368         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2369             if (curr==a) {
2370                 //printf("b to a:%d\n", distance);
2371                 if (distance < minDistance) {
2372                     start    = b;  //we go from b to a
2373                     end      = a;
2374                     reversed = TRUE;
2375                     //printf("B to A\n");
2376                 }
2377                 break;
2378             }
2379             distance++;
2380         }
2383         //Copy everything from 'end' to 'start' to a new subpath
2384        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2385         for (curr=end ; curr ; curr=curr->n.other) {
2386             NRPathcode code = (NRPathcode) curr->code;
2387             if (curr == end)
2388                 code = NR_MOVETO;
2389             sp_nodepath_node_new(t, NULL,
2390                                  (Inkscape::NodePath::NodeType)curr->type, code,
2391                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2392             if (curr == start)
2393                 break;
2394         }
2395         sp_nodepath_subpath_destroy(a->subpath);
2398     }
2402     //##################################
2403     //# OPEN PATH
2404     //##################################
2405     else {
2407         //We need to get the direction of the list between A and B
2408         //Can we walk from a to b?
2409         start = end = NULL;
2410         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2411             if (curr==b) {
2412                 start = a;  //did it!  we go from a to b
2413                 end   = b;
2414                 //printf("A to B\n");
2415                 break;
2416             }
2417         }
2418         if (!start) {//didn't work?  let's try the other direction
2419             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2420                 if (curr==a) {
2421                     start = b;  //did it!  we go from b to a
2422                     end   = a;
2423                     //printf("B to A\n");
2424                     break;
2425                 }
2426             }
2427         }
2428         if (!start) {
2429             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2430                                                      _("Cannot find path between nodes."));
2431             return;
2432         }
2436         //Copy everything after 'end' to a new subpath
2437        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2438         for (curr=end ; curr ; curr=curr->n.other) {
2439             NRPathcode code = (NRPathcode) curr->code;
2440             if (curr == end)
2441                 code = NR_MOVETO;
2442             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2443                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2444         }
2446         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2447         for (curr = start->n.other ; curr  ; curr=next) {
2448             next = curr->n.other;
2449             sp_nodepath_node_destroy(curr);
2450         }
2452     }
2453     //###########################################
2454     //# END EDITS
2455     //###########################################
2457     //clean up the nodepath (such as for trivial subpaths)
2458     sp_nodepath_cleanup(nodepath);
2460     sp_nodepath_update_handles(nodepath);
2462     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2464     sp_nodepath_update_statusbar(nodepath);
2467 /**
2468  * Call sp_nodepath_set_line() for all selected segments.
2469  */
2470 void
2471 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2473     if (nodepath == NULL) return;
2475     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2476        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2477         g_assert(n->selected);
2478         if (n->p.other && n->p.other->selected) {
2479             sp_nodepath_set_line_type(n, code);
2480         }
2481     }
2483     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2486 /**
2487  * Call sp_nodepath_convert_node_type() for all selected nodes.
2488  */
2489 void
2490 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2492     if (nodepath == NULL) return;
2494     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2496     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2497         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2498     }
2500     sp_nodepath_update_repr(nodepath, _("Change node type"));
2503 /**
2504  * Change select status of node, update its own and neighbour handles.
2505  */
2506 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2508     node->selected = selected;
2510     if (selected) {
2511         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2512         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2513         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2514         sp_knot_update_ctrl(node->knot);
2515     } else {
2516         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2517         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2518         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2519         sp_knot_update_ctrl(node->knot);
2520     }
2522     sp_node_update_handles(node);
2523     if (node->n.other) sp_node_update_handles(node->n.other);
2524     if (node->p.other) sp_node_update_handles(node->p.other);
2527 /**
2528 \brief Select a node
2529 \param node     The node to select
2530 \param incremental   If true, add to selection, otherwise deselect others
2531 \param override   If true, always select this node, otherwise toggle selected status
2532 */
2533 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2535     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2537     if (incremental) {
2538         if (override) {
2539             if (!g_list_find(nodepath->selected, node)) {
2540                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2541             }
2542             sp_node_set_selected(node, TRUE);
2543         } else { // toggle
2544             if (node->selected) {
2545                 g_assert(g_list_find(nodepath->selected, node));
2546                 nodepath->selected = g_list_remove(nodepath->selected, node);
2547             } else {
2548                 g_assert(!g_list_find(nodepath->selected, node));
2549                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2550             }
2551             sp_node_set_selected(node, !node->selected);
2552         }
2553     } else {
2554         sp_nodepath_deselect(nodepath);
2555         nodepath->selected = g_list_prepend(nodepath->selected, node);
2556         sp_node_set_selected(node, TRUE);
2557     }
2559     sp_nodepath_update_statusbar(nodepath);
2563 /**
2564 \brief Deselect all nodes in the nodepath
2565 */
2566 void
2567 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2569     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2571     while (nodepath->selected) {
2572         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2573         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2574     }
2575     sp_nodepath_update_statusbar(nodepath);
2578 /**
2579 \brief Select or invert selection of all nodes in the nodepath
2580 */
2581 void
2582 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2584     if (!nodepath) return;
2586     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2587        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2588         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2589            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2590            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2591         }
2592     }
2595 /**
2596  * If nothing selected, does the same as sp_nodepath_select_all();
2597  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2598  * (i.e., similar to "select all in layer", with the "selected" subpaths
2599  * being treated as "layers" in the path).
2600  */
2601 void
2602 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2604     if (!nodepath) return;
2606     if (g_list_length (nodepath->selected) == 0) {
2607         sp_nodepath_select_all (nodepath, invert);
2608         return;
2609     }
2611     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2612     GSList *subpaths = NULL;
2614     for (GList *l = copy; l != NULL; l = l->next) {
2615         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2616         Inkscape::NodePath::SubPath *subpath = n->subpath;
2617         if (!g_slist_find (subpaths, subpath))
2618             subpaths = g_slist_prepend (subpaths, subpath);
2619     }
2621     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2622         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2623         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2624             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2625             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2626         }
2627     }
2629     g_slist_free (subpaths);
2630     g_list_free (copy);
2633 /**
2634  * \brief Select the node after the last selected; if none is selected,
2635  * select the first within path.
2636  */
2637 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2639     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2641    Inkscape::NodePath::Node *last = NULL;
2642     if (nodepath->selected) {
2643         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2644            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2645             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2646             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2647                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2648                 if (node->selected) {
2649                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2650                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2651                             if (spl->next) { // there's a next subpath
2652                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2653                                 last = subpath_next->first;
2654                             } else if (spl->prev) { // there's a previous subpath
2655                                 last = NULL; // to be set later to the first node of first subpath
2656                             } else {
2657                                 last = node->n.other;
2658                             }
2659                         } else {
2660                             last = node->n.other;
2661                         }
2662                     } else {
2663                         if (node->n.other) {
2664                             last = node->n.other;
2665                         } else {
2666                             if (spl->next) { // there's a next subpath
2667                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2668                                 last = subpath_next->first;
2669                             } else if (spl->prev) { // there's a previous subpath
2670                                 last = NULL; // to be set later to the first node of first subpath
2671                             } else {
2672                                 last = (Inkscape::NodePath::Node *) subpath->first;
2673                             }
2674                         }
2675                     }
2676                 }
2677             }
2678         }
2679         sp_nodepath_deselect(nodepath);
2680     }
2682     if (last) { // there's at least one more node after selected
2683         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2684     } else { // no more nodes, select the first one in first subpath
2685        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2686         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2687     }
2690 /**
2691  * \brief Select the node before the first selected; if none is selected,
2692  * select the last within path
2693  */
2694 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2696     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2698    Inkscape::NodePath::Node *last = NULL;
2699     if (nodepath->selected) {
2700         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2701            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2702             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2703                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2704                 if (node->selected) {
2705                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2706                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2707                             if (spl->prev) { // there's a prev subpath
2708                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2709                                 last = subpath_prev->last;
2710                             } else if (spl->next) { // there's a next subpath
2711                                 last = NULL; // to be set later to the last node of last subpath
2712                             } else {
2713                                 last = node->p.other;
2714                             }
2715                         } else {
2716                             last = node->p.other;
2717                         }
2718                     } else {
2719                         if (node->p.other) {
2720                             last = node->p.other;
2721                         } else {
2722                             if (spl->prev) { // there's a prev subpath
2723                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2724                                 last = subpath_prev->last;
2725                             } else if (spl->next) { // there's a next subpath
2726                                 last = NULL; // to be set later to the last node of last subpath
2727                             } else {
2728                                 last = (Inkscape::NodePath::Node *) subpath->last;
2729                             }
2730                         }
2731                     }
2732                 }
2733             }
2734         }
2735         sp_nodepath_deselect(nodepath);
2736     }
2738     if (last) { // there's at least one more node before selected
2739         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2740     } else { // no more nodes, select the last one in last subpath
2741         GList *spl = g_list_last(nodepath->subpaths);
2742        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2743         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2744     }
2747 /**
2748  * \brief Select all nodes that are within the rectangle.
2749  */
2750 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2752     if (!incremental) {
2753         sp_nodepath_deselect(nodepath);
2754     }
2756     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2757        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2758         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2759            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2761             if (b.contains(node->pos)) {
2762                 sp_nodepath_node_select(node, TRUE, TRUE);
2763             }
2764         }
2765     }
2769 void
2770 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2772     g_assert (n);
2773     g_assert (nodepath);
2774     g_assert (n->subpath->nodepath == nodepath);
2776     if (g_list_length (nodepath->selected) == 0) {
2777         if (grow > 0) {
2778             sp_nodepath_node_select(n, TRUE, TRUE);
2779         }
2780         return;
2781     }
2783     if (g_list_length (nodepath->selected) == 1) {
2784         if (grow < 0) {
2785             sp_nodepath_deselect (nodepath);
2786             return;
2787         }
2788     }
2790         double n_sel_range = 0, p_sel_range = 0;
2791             Inkscape::NodePath::Node *farthest_n_node = n;
2792             Inkscape::NodePath::Node *farthest_p_node = n;
2794         // Calculate ranges
2795         {
2796             double n_range = 0, p_range = 0;
2797             bool n_going = true, p_going = true;
2798             Inkscape::NodePath::Node *n_node = n;
2799             Inkscape::NodePath::Node *p_node = n;
2800             do {
2801                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2802                 if (n_node && n_going)
2803                     n_node = n_node->n.other;
2804                 if (n_node == NULL) {
2805                     n_going = false;
2806                 } else {
2807                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2808                     if (n_node->selected) {
2809                         n_sel_range = n_range;
2810                         farthest_n_node = n_node;
2811                     }
2812                     if (n_node == p_node) {
2813                         n_going = false;
2814                         p_going = false;
2815                     }
2816                 }
2817                 if (p_node && p_going)
2818                     p_node = p_node->p.other;
2819                 if (p_node == NULL) {
2820                     p_going = false;
2821                 } else {
2822                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2823                     if (p_node->selected) {
2824                         p_sel_range = p_range;
2825                         farthest_p_node = p_node;
2826                     }
2827                     if (p_node == n_node) {
2828                         n_going = false;
2829                         p_going = false;
2830                     }
2831                 }
2832             } while (n_going || p_going);
2833         }
2835     if (grow > 0) {
2836         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2837                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2838         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2839                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2840         }
2841     } else {
2842         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2843                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2844         } else if (farthest_p_node && farthest_p_node->selected) {
2845                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2846         }
2847     }
2850 void
2851 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2853     g_assert (n);
2854     g_assert (nodepath);
2855     g_assert (n->subpath->nodepath == nodepath);
2857     if (g_list_length (nodepath->selected) == 0) {
2858         if (grow > 0) {
2859             sp_nodepath_node_select(n, TRUE, TRUE);
2860         }
2861         return;
2862     }
2864     if (g_list_length (nodepath->selected) == 1) {
2865         if (grow < 0) {
2866             sp_nodepath_deselect (nodepath);
2867             return;
2868         }
2869     }
2871     Inkscape::NodePath::Node *farthest_selected = NULL;
2872     double farthest_dist = 0;
2874     Inkscape::NodePath::Node *closest_unselected = NULL;
2875     double closest_dist = NR_HUGE;
2877     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2878        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2879         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2880            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2881            if (node == n)
2882                continue;
2883            if (node->selected) {
2884                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2885                    farthest_dist = NR::L2(node->pos - n->pos);
2886                    farthest_selected = node;
2887                }
2888            } else {
2889                if (NR::L2(node->pos - n->pos) < closest_dist) {
2890                    closest_dist = NR::L2(node->pos - n->pos);
2891                    closest_unselected = node;
2892                }
2893            }
2894         }
2895     }
2897     if (grow > 0) {
2898         if (closest_unselected) {
2899             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2900         }
2901     } else {
2902         if (farthest_selected) {
2903             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2904         }
2905     }
2909 /**
2910 \brief  Saves all nodes' and handles' current positions in their origin members
2911 */
2912 void
2913 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2915     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2916        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2917         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2918            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2919            n->origin = n->pos;
2920            n->p.origin = n->p.pos;
2921            n->n.origin = n->n.pos;
2922         }
2923     }
2926 /**
2927 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2928 */
2929 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2931     if (!nodepath->selected) {
2932         return NULL;
2933     }
2935     GList *r = NULL;
2936     guint i = 0;
2937     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2938        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2939         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2940            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2941             i++;
2942             if (node->selected) {
2943                 r = g_list_append(r, GINT_TO_POINTER(i));
2944             }
2945         }
2946     }
2947     return r;
2950 /**
2951 \brief  Restores selection by selecting nodes whose positions are in the list
2952 */
2953 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2955     sp_nodepath_deselect(nodepath);
2957     guint i = 0;
2958     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2959        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2960         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2961            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2962             i++;
2963             if (g_list_find(r, GINT_TO_POINTER(i))) {
2964                 sp_nodepath_node_select(node, TRUE, TRUE);
2965             }
2966         }
2967     }
2971 /**
2972 \brief Adjusts handle according to node type and line code.
2973 */
2974 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2976     double len, otherlen, linelen;
2978     g_assert(node);
2980    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2981    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2983     /** \todo fixme: */
2984     if (me->other == NULL) return;
2985     if (other->other == NULL) return;
2987     /* I have line */
2989     NRPathcode mecode, ocode;
2990     if (which_adjust == 1) {
2991         mecode = (NRPathcode)me->other->code;
2992         ocode = (NRPathcode)node->code;
2993     } else {
2994         mecode = (NRPathcode)node->code;
2995         ocode = (NRPathcode)other->other->code;
2996     }
2998     if (mecode == NR_LINETO) return;
3000     /* I am curve */
3002     if (other->other == NULL) return;
3004     /* Other has line */
3006     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3008     NR::Point delta;
3009     if (ocode == NR_LINETO) {
3010         /* other is lineto, we are either smooth or symm */
3011        Inkscape::NodePath::Node *othernode = other->other;
3012         len = NR::L2(me->pos - node->pos);
3013         delta = node->pos - othernode->pos;
3014         linelen = NR::L2(delta);
3015         if (linelen < 1e-18)
3016             return;
3017         me->pos = node->pos + (len / linelen)*delta;
3018         return;
3019     }
3021     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3023         me->pos = 2 * node->pos - other->pos;
3024         return;
3025     }
3027     /* We are smooth */
3029     len = NR::L2(me->pos - node->pos);
3030     delta = other->pos - node->pos;
3031     otherlen = NR::L2(delta);
3032     if (otherlen < 1e-18) return;
3034     me->pos = node->pos - (len / otherlen) * delta;
3037 /**
3038  \brief Adjusts both handles according to node type and line code
3039  */
3040 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3042     g_assert(node);
3044     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3046     /* we are either smooth or symm */
3048     if (node->p.other == NULL) return;
3050     if (node->n.other == NULL) return;
3052     if (node->code == NR_LINETO) {
3053         if (node->n.other->code == NR_LINETO) return;
3054         sp_node_adjust_handle(node, 1);
3055         return;
3056     }
3058     if (node->n.other->code == NR_LINETO) {
3059         if (node->code == NR_LINETO) return;
3060         sp_node_adjust_handle(node, -1);
3061         return;
3062     }
3064     /* both are curves */
3065     NR::Point const delta( node->n.pos - node->p.pos );
3067     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3068         node->p.pos = node->pos - delta / 2;
3069         node->n.pos = node->pos + delta / 2;
3070         return;
3071     }
3073     /* We are smooth */
3074     double plen = NR::L2(node->p.pos - node->pos);
3075     if (plen < 1e-18) return;
3076     double nlen = NR::L2(node->n.pos - node->pos);
3077     if (nlen < 1e-18) return;
3078     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3079     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3082 /**
3083  * Node event callback.
3084  */
3085 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3087     gboolean ret = FALSE;
3088     switch (event->type) {
3089         case GDK_ENTER_NOTIFY:
3090             Inkscape::NodePath::Path::active_node = n;
3091             break;
3092         case GDK_LEAVE_NOTIFY:
3093             Inkscape::NodePath::Path::active_node = NULL;
3094             break;
3095         case GDK_SCROLL:
3096             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3097                 switch (event->scroll.direction) {
3098                     case GDK_SCROLL_UP:
3099                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3100                         break;
3101                     case GDK_SCROLL_DOWN:
3102                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3103                         break;
3104                     default:
3105                         break;
3106                 }
3107                 ret = TRUE;
3108             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3109                 switch (event->scroll.direction) {
3110                     case GDK_SCROLL_UP:
3111                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3112                         break;
3113                     case GDK_SCROLL_DOWN:
3114                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3115                         break;
3116                     default:
3117                         break;
3118                 }
3119                 ret = TRUE;
3120             }
3121             break;
3122         case GDK_KEY_PRESS:
3123             switch (get_group0_keyval (&event->key)) {
3124                 case GDK_space:
3125                     if (event->key.state & GDK_BUTTON1_MASK) {
3126                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3127                         stamp_repr(nodepath);
3128                         ret = TRUE;
3129                     }
3130                     break;
3131                 case GDK_Page_Up:
3132                     if (event->key.state & GDK_CONTROL_MASK) {
3133                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3134                     } else {
3135                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3136                     }
3137                     break;
3138                 case GDK_Page_Down:
3139                     if (event->key.state & GDK_CONTROL_MASK) {
3140                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3141                     } else {
3142                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3143                     }
3144                     break;
3145                 default:
3146                     break;
3147             }
3148             break;
3149         default:
3150             break;
3151     }
3153     return ret;
3156 /**
3157  * Handle keypress on node; directly called.
3158  */
3159 gboolean node_key(GdkEvent *event)
3161     Inkscape::NodePath::Path *np;
3163     // there is no way to verify nodes so set active_node to nil when deleting!!
3164     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3166     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3167         gint ret = FALSE;
3168         switch (get_group0_keyval (&event->key)) {
3169             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3170             case GDK_BackSpace:
3171                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3172                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3173                 sp_nodepath_update_repr(np, _("Delete node"));
3174                 Inkscape::NodePath::Path::active_node = NULL;
3175                 ret = TRUE;
3176                 break;
3177             case GDK_c:
3178                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3179                 ret = TRUE;
3180                 break;
3181             case GDK_s:
3182                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3183                 ret = TRUE;
3184                 break;
3185             case GDK_y:
3186                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3187                 ret = TRUE;
3188                 break;
3189             case GDK_b:
3190                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3191                 ret = TRUE;
3192                 break;
3193         }
3194         return ret;
3195     }
3196     return FALSE;
3199 /**
3200  * Mouseclick on node callback.
3201  */
3202 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3204    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3206     if (state & GDK_CONTROL_MASK) {
3207         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3209         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3210             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3211                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3212             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3213                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3214             } else {
3215                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3216             }
3217             sp_nodepath_update_repr(nodepath, _("Change node type"));
3218             sp_nodepath_update_statusbar(nodepath);
3220         } else { //ctrl+alt+click: delete node
3221             GList *node_to_delete = NULL;
3222             node_to_delete = g_list_append(node_to_delete, n);
3223             sp_node_delete_preserve(node_to_delete);
3224         }
3226     } else {
3227         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3228     }
3231 /**
3232  * Mouse grabbed node callback.
3233  */
3234 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3236    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3238     if (!n->selected) {
3239         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3240     }
3242     n->is_dragging = true;
3243     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3245     sp_nodepath_remember_origins (n->subpath->nodepath);
3248 /**
3249  * Mouse ungrabbed node callback.
3250  */
3251 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3253    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3255    n->dragging_out = NULL;
3256    n->is_dragging = false;
3257    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3259    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3262 /**
3263  * The point on a line, given by its angle, closest to the given point.
3264  * \param p  A point.
3265  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3266  * \param closest  Pointer to the point struct where the result is stored.
3267  * \todo FIXME: use dot product perhaps?
3268  */
3269 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3271     if (a == HUGE_VAL) { // vertical
3272         *closest = NR::Point(0, (*p)[NR::Y]);
3273     } else {
3274         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3275         (*closest)[NR::Y] = a * (*closest)[NR::X];
3276     }
3279 /**
3280  * Distance from the point to a line given by its angle.
3281  * \param p  A point.
3282  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3283  */
3284 static double point_line_distance(NR::Point *p, double a)
3286     NR::Point c;
3287     point_line_closest(p, a, &c);
3288     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]));
3291 /**
3292  * Callback for node "request" signal.
3293  * \todo fixme: This goes to "moved" event? (lauris)
3294  */
3295 static gboolean
3296 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3298     double yn, xn, yp, xp;
3299     double an, ap, na, pa;
3300     double d_an, d_ap, d_na, d_pa;
3301     gboolean collinear = FALSE;
3302     NR::Point c;
3303     NR::Point pr;
3305    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3307     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3309    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3310     if ( (!n->subpath->nodepath->straight_path) &&
3311          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3312            || n->dragging_out ) )
3313     {
3314        NR::Point mouse = (*p);
3316        if (!n->dragging_out) {
3317            // This is the first drag-out event; find out which handle to drag out
3318            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3319            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3321            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3322                return FALSE;
3324            Inkscape::NodePath::NodeSide *opposite;
3325            if (appr_p > appr_n) { // closer to p
3326                n->dragging_out = &n->p;
3327                opposite = &n->n;
3328                n->code = NR_CURVETO;
3329            } else if (appr_p < appr_n) { // closer to n
3330                n->dragging_out = &n->n;
3331                opposite = &n->p;
3332                n->n.other->code = NR_CURVETO;
3333            } else { // p and n nodes are the same
3334                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3335                    n->dragging_out = &n->p;
3336                    opposite = &n->n;
3337                    n->code = NR_CURVETO;
3338                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3339                    n->dragging_out = &n->n;
3340                    opposite = &n->p;
3341                    n->n.other->code = NR_CURVETO;
3342                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3343                    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);
3344                    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);
3345                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3346                        n->dragging_out = &n->n;
3347                        opposite = &n->p;
3348                        n->n.other->code = NR_CURVETO;
3349                    } else { // closer to other's n handle
3350                        n->dragging_out = &n->p;
3351                        opposite = &n->n;
3352                        n->code = NR_CURVETO;
3353                    }
3354                }
3355            }
3357            // if there's another handle, make sure the one we drag out starts parallel to it
3358            if (opposite->pos != n->pos) {
3359                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3360            }
3362            // knots might not be created yet!
3363            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3364            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3365        }
3367        // pass this on to the handle-moved callback
3368        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3369        sp_node_update_handles(n);
3370        return TRUE;
3371    }
3373     if (state & GDK_CONTROL_MASK) { // constrained motion
3375         // calculate relative distances of handles
3376         // n handle:
3377         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3378         xn = n->n.pos[NR::X] - n->pos[NR::X];
3379         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3380         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3381             if (n->n.other) { // if there is the next point
3382                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3383                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3384                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3385             }
3386         }
3387         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3388         if (yn < 0) { xn = -xn; yn = -yn; }
3390         // p handle:
3391         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3392         xp = n->p.pos[NR::X] - n->pos[NR::X];
3393         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3394         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3395             if (n->p.other) {
3396                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3397                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3398                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3399             }
3400         }
3401         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3402         if (yp < 0) { xp = -xp; yp = -yp; }
3404         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3405             // sliding on handles, only if at least one of the handles is non-vertical
3406             // (otherwise it's the same as ctrl+drag anyway)
3408             // calculate angles of the handles
3409             if (xn == 0) {
3410                 if (yn == 0) { // no handle, consider it the continuation of the other one
3411                     an = 0;
3412                     collinear = TRUE;
3413                 }
3414                 else an = 0; // vertical; set the angle to horizontal
3415             } else an = yn/xn;
3417             if (xp == 0) {
3418                 if (yp == 0) { // no handle, consider it the continuation of the other one
3419                     ap = an;
3420                 }
3421                 else ap = 0; // vertical; set the angle to horizontal
3422             } else  ap = yp/xp;
3424             if (collinear) an = ap;
3426             // angles of the perpendiculars; HUGE_VAL means vertical
3427             if (an == 0) na = HUGE_VAL; else na = -1/an;
3428             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3430             // mouse point relative to the node's original pos
3431             pr = (*p) - n->origin;
3433             // distances to the four lines (two handles and two perpendiculars)
3434             d_an = point_line_distance(&pr, an);
3435             d_na = point_line_distance(&pr, na);
3436             d_ap = point_line_distance(&pr, ap);
3437             d_pa = point_line_distance(&pr, pa);
3439             // find out which line is the closest, save its closest point in c
3440             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3441                 point_line_closest(&pr, an, &c);
3442             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3443                 point_line_closest(&pr, ap, &c);
3444             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3445                 point_line_closest(&pr, na, &c);
3446             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3447                 point_line_closest(&pr, pa, &c);
3448             }
3450             // move the node to the closest point
3451             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3452                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3453                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3455         } else {  // constraining to hor/vert
3457             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3458                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3459             } else { // snap to vert
3460                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3461             }
3462         }
3463     } else { // move freely
3464         if (n->is_dragging) {
3465             if (state & GDK_MOD1_MASK) { // sculpt
3466                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3467             } else {
3468                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3469                                             (*p)[NR::X] - n->pos[NR::X],
3470                                             (*p)[NR::Y] - n->pos[NR::Y],
3471                                             (state & GDK_SHIFT_MASK) == 0);
3472             }
3473         }
3474     }
3476     n->subpath->nodepath->desktop->scroll_to_point(p);
3478     return TRUE;
3481 /**
3482  * Node handle clicked callback.
3483  */
3484 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3486    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3488     if (state & GDK_CONTROL_MASK) { // "delete" handle
3489         if (n->p.knot == knot) {
3490             n->p.pos = n->pos;
3491         } else if (n->n.knot == knot) {
3492             n->n.pos = n->pos;
3493         }
3494         sp_node_update_handles(n);
3495         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3496         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3497         sp_nodepath_update_statusbar(nodepath);
3499     } else { // just select or add to selection, depending in Shift
3500         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3501     }
3504 /**
3505  * Node handle grabbed callback.
3506  */
3507 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3509    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3511     if (!n->selected) {
3512         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3513     }
3515     // remember the origin point of the handle
3516     if (n->p.knot == knot) {
3517         n->p.origin_radial = n->p.pos - n->pos;
3518     } else if (n->n.knot == knot) {
3519         n->n.origin_radial = n->n.pos - n->pos;
3520     } else {
3521         g_assert_not_reached();
3522     }
3524     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3527 /**
3528  * Node handle ungrabbed callback.
3529  */
3530 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3532    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3534     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3535     if (n->p.knot == knot) {
3536         n->p.origin_radial.a = 0;
3537         sp_knot_set_position(knot, &n->p.pos, state);
3538     } else if (n->n.knot == knot) {
3539         n->n.origin_radial.a = 0;
3540         sp_knot_set_position(knot, &n->n.pos, state);
3541     } else {
3542         g_assert_not_reached();
3543     }
3545     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3548 /**
3549  * Node handle "request" signal callback.
3550  */
3551 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint /*state*/, gpointer data)
3553     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3555     Inkscape::NodePath::NodeSide *me, *opposite;
3556     gint which;
3557     if (n->p.knot == knot) {
3558         me = &n->p;
3559         opposite = &n->n;
3560         which = -1;
3561     } else if (n->n.knot == knot) {
3562         me = &n->n;
3563         opposite = &n->p;
3564         which = 1;
3565     } else {
3566         me = opposite = NULL;
3567         which = 0;
3568         g_assert_not_reached();
3569     }
3571     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3573     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3574     Inkscape::SnappedPoint s ;
3575     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3576         /* We are smooth node adjacent with line */
3577         NR::Point const delta = *p - n->pos;
3578         NR::Coord const len = NR::L2(delta);
3579         Inkscape::NodePath::Node *othernode = opposite->other;
3580         NR::Point const ndelta = n->pos - othernode->pos;
3581         NR::Coord const linelen = NR::L2(ndelta);
3582         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3583             NR::Coord const scal = dot(delta, ndelta) / linelen;
3584             (*p) = n->pos + (scal / linelen) * ndelta;
3585         }
3586         s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), n->subpath->nodepath->item);
3587     } else {
3588         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, n->subpath->nodepath->item);
3589     }
3590     *p = s.getPoint();
3591     if (s.getSnapped()) {
3592         n->subpath->nodepath->desktop->snapindicator->set_new_snappoint(s);
3593     }
3595     sp_node_adjust_handle(n, -which);
3597     return FALSE;
3600 /**
3601  * Node handle moved callback.
3602  */
3603 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3605    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3607    Inkscape::NodePath::NodeSide *me;
3608    Inkscape::NodePath::NodeSide *other;
3609     if (n->p.knot == knot) {
3610         me = &n->p;
3611         other = &n->n;
3612     } else if (n->n.knot == knot) {
3613         me = &n->n;
3614         other = &n->p;
3615     } else {
3616         me = NULL;
3617         other = NULL;
3618         g_assert_not_reached();
3619     }
3621     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3622     Radial rme(me->pos - n->pos);
3623     Radial rother(other->pos - n->pos);
3624     Radial rnew(*p - n->pos);
3626     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3627         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3628         /* 0 interpreted as "no snapping". */
3630         // The closest PI/snaps angle, starting from zero.
3631         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3632         if (me->origin_radial.a == HUGE_VAL) {
3633             // ortho doesn't exist: original handle was zero length.
3634             rnew.a = a_snapped;
3635         } else {
3636             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3637              * its opposite and perpendiculars). */
3638             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3640             // Snap to the closest.
3641             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3642                        ? a_snapped
3643                        : a_ortho );
3644         }
3645     }
3647     if (state & GDK_MOD1_MASK) {
3648         // lock handle length
3649         rnew.r = me->origin_radial.r;
3650     }
3652     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3653         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3654         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3655         rother.a += rnew.a - rme.a;
3656         other->pos = NR::Point(rother) + n->pos;
3657         if (other->knot) {
3658             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3659             sp_knot_moveto(other->knot, &other->pos);
3660         }
3661     }
3663     me->pos = NR::Point(rnew) + n->pos;
3664     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3666     // move knot, but without emitting the signal:
3667     // we cannot emit a "moved" signal because we're now processing it
3668     sp_knot_moveto(me->knot, &(me->pos));
3670     update_object(n->subpath->nodepath);
3672     /* status text */
3673     SPDesktop *desktop = n->subpath->nodepath->desktop;
3674     if (!desktop) return;
3675     SPEventContext *ec = desktop->event_context;
3676     if (!ec) return;
3677     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3678     if (!mc) return;
3680     double degrees = 180 / M_PI * rnew.a;
3681     if (degrees > 180) degrees -= 360;
3682     if (degrees < -180) degrees += 360;
3683     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3684         degrees = angle_to_compass (degrees);
3686     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3688     mc->setF(Inkscape::NORMAL_MESSAGE,
3689          _("<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);
3691     g_string_free(length, TRUE);
3694 /**
3695  * Node handle event callback.
3696  */
3697 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3699     gboolean ret = FALSE;
3700     switch (event->type) {
3701         case GDK_KEY_PRESS:
3702             switch (get_group0_keyval (&event->key)) {
3703                 case GDK_space:
3704                     if (event->key.state & GDK_BUTTON1_MASK) {
3705                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3706                         stamp_repr(nodepath);
3707                         ret = TRUE;
3708                     }
3709                     break;
3710                 default:
3711                     break;
3712             }
3713             break;
3714         case GDK_ENTER_NOTIFY:
3715             // we use an experimentally determined threshold that seems to work fine
3716             if (NR::L2(n->pos - knot->pos) < 0.75)
3717                 Inkscape::NodePath::Path::active_node = n;
3718             break;
3719         case GDK_LEAVE_NOTIFY:
3720             // we use an experimentally determined threshold that seems to work fine
3721             if (NR::L2(n->pos - knot->pos) < 0.75)
3722                 Inkscape::NodePath::Path::active_node = NULL;
3723             break;
3724         default:
3725             break;
3726     }
3728     return ret;
3731 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3732                                  Radial &rme, Radial &rother, gboolean const both)
3734     rme.a += angle;
3735     if ( both
3736          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3737          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3738     {
3739         rother.a += angle;
3740     }
3743 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3744                                         Radial &rme, Radial &rother, gboolean const both)
3746     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3748     gdouble r;
3749     if ( both
3750          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3751          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3752     {
3753         r = MAX(rme.r, rother.r);
3754     } else {
3755         r = rme.r;
3756     }
3758     gdouble const weird_angle = atan2(norm_angle, r);
3759 /* Bulia says norm_angle is just the visible distance that the
3760  * object's end must travel on the screen.  Left as 'angle' for want of
3761  * a better name.*/
3763     rme.a += weird_angle;
3764     if ( both
3765          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3766          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3767     {
3768         rother.a += weird_angle;
3769     }
3772 /**
3773  * Rotate one node.
3774  */
3775 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3777     Inkscape::NodePath::NodeSide *me, *other;
3778     bool both = false;
3780     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3781     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3783     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3784         me = &(n->p);
3785         other = &(n->n);
3786     } else if (!n->p.other) {
3787         me = &(n->n);
3788         other = &(n->p);
3789     } else {
3790         if (which > 0) { // right handle
3791             if (xn > xp) {
3792                 me = &(n->n);
3793                 other = &(n->p);
3794             } else {
3795                 me = &(n->p);
3796                 other = &(n->n);
3797             }
3798         } else if (which < 0){ // left handle
3799             if (xn <= xp) {
3800                 me = &(n->n);
3801                 other = &(n->p);
3802             } else {
3803                 me = &(n->p);
3804                 other = &(n->n);
3805             }
3806         } else { // both handles
3807             me = &(n->n);
3808             other = &(n->p);
3809             both = true;
3810         }
3811     }
3813     Radial rme(me->pos - n->pos);
3814     Radial rother(other->pos - n->pos);
3816     if (screen) {
3817         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3818     } else {
3819         node_rotate_one_internal (*n, angle, rme, rother, both);
3820     }
3822     me->pos = n->pos + NR::Point(rme);
3824     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3825         other->pos =  n->pos + NR::Point(rother);
3826     }
3828     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3829     // so here we just move all the knots without emitting move signals, for speed
3830     sp_node_update_handles(n, false);
3833 /**
3834  * Rotate selected nodes.
3835  */
3836 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3838     if (!nodepath || !nodepath->selected) return;
3840     if (g_list_length(nodepath->selected) == 1) {
3841        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3842         node_rotate_one (n, angle, which, screen);
3843     } else {
3844        // rotate as an object:
3846         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3847         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3848         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3849             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3850             box.expandTo (n->pos); // contain all selected nodes
3851         }
3853         gdouble rot;
3854         if (screen) {
3855             gdouble const zoom = nodepath->desktop->current_zoom();
3856             gdouble const zmove = angle / zoom;
3857             gdouble const r = NR::L2(box.max() - box.midpoint());
3858             rot = atan2(zmove, r);
3859         } else {
3860             rot = angle;
3861         }
3863         NR::Point rot_center;
3864         if (Inkscape::NodePath::Path::active_node == NULL)
3865             rot_center = box.midpoint();
3866         else
3867             rot_center = Inkscape::NodePath::Path::active_node->pos;
3869         NR::Matrix t =
3870             NR::Matrix (NR::translate(-rot_center)) *
3871             NR::Matrix (NR::rotate(rot)) *
3872             NR::Matrix (NR::translate(rot_center));
3874         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3875             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3876             n->pos *= t;
3877             n->n.pos *= t;
3878             n->p.pos *= t;
3879             sp_node_update_handles(n, false);
3880         }
3881     }
3883     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3886 /**
3887  * Scale one node.
3888  */
3889 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3891     bool both = false;
3892     Inkscape::NodePath::NodeSide *me, *other;
3894     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3895     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3897     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3898         me = &(n->p);
3899         other = &(n->n);
3900         n->code = NR_CURVETO;
3901     } else if (!n->p.other) {
3902         me = &(n->n);
3903         other = &(n->p);
3904         if (n->n.other)
3905             n->n.other->code = NR_CURVETO;
3906     } else {
3907         if (which > 0) { // right handle
3908             if (xn > xp) {
3909                 me = &(n->n);
3910                 other = &(n->p);
3911                 if (n->n.other)
3912                     n->n.other->code = NR_CURVETO;
3913             } else {
3914                 me = &(n->p);
3915                 other = &(n->n);
3916                 n->code = NR_CURVETO;
3917             }
3918         } else if (which < 0){ // left handle
3919             if (xn <= xp) {
3920                 me = &(n->n);
3921                 other = &(n->p);
3922                 if (n->n.other)
3923                     n->n.other->code = NR_CURVETO;
3924             } else {
3925                 me = &(n->p);
3926                 other = &(n->n);
3927                 n->code = NR_CURVETO;
3928             }
3929         } else { // both handles
3930             me = &(n->n);
3931             other = &(n->p);
3932             both = true;
3933             n->code = NR_CURVETO;
3934             if (n->n.other)
3935                 n->n.other->code = NR_CURVETO;
3936         }
3937     }
3939     Radial rme(me->pos - n->pos);
3940     Radial rother(other->pos - n->pos);
3942     rme.r += grow;
3943     if (rme.r < 0) rme.r = 0;
3944     if (rme.a == HUGE_VAL) {
3945         if (me->other) { // if direction is unknown, initialize it towards the next node
3946             Radial rme_next(me->other->pos - n->pos);
3947             rme.a = rme_next.a;
3948         } else { // if there's no next, initialize to 0
3949             rme.a = 0;
3950         }
3951     }
3952     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3953         rother.r += grow;
3954         if (rother.r < 0) rother.r = 0;
3955         if (rother.a == HUGE_VAL) {
3956             rother.a = rme.a + M_PI;
3957         }
3958     }
3960     me->pos = n->pos + NR::Point(rme);
3962     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3963         other->pos = n->pos + NR::Point(rother);
3964     }
3966     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3967     // so here we just move all the knots without emitting move signals, for speed
3968     sp_node_update_handles(n, false);
3971 /**
3972  * Scale selected nodes.
3973  */
3974 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3976     if (!nodepath || !nodepath->selected) return;
3978     if (g_list_length(nodepath->selected) == 1) {
3979         // scale handles of the single selected node
3980         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3981         node_scale_one (n, grow, which);
3982     } else {
3983         // scale nodes as an "object":
3985         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3986         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3987         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3988             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3989             box.expandTo (n->pos); // contain all selected nodes
3990         }
3992         double scale = (box.maxExtent() + grow)/box.maxExtent();
3994         NR::Point scale_center;
3995         if (Inkscape::NodePath::Path::active_node == NULL)
3996             scale_center = box.midpoint();
3997         else
3998             scale_center = Inkscape::NodePath::Path::active_node->pos;
4000         NR::Matrix t =
4001             NR::Matrix (NR::translate(-scale_center)) *
4002             NR::Matrix (NR::scale(scale, scale)) *
4003             NR::Matrix (NR::translate(scale_center));
4005         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4006             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4007             n->pos *= t;
4008             n->n.pos *= t;
4009             n->p.pos *= t;
4010             sp_node_update_handles(n, false);
4011         }
4012     }
4014     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4017 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4019     if (!nodepath) return;
4020     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4023 /**
4024  * Flip selected nodes horizontally/vertically.
4025  */
4026 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
4028     if (!nodepath || !nodepath->selected) return;
4030     if (g_list_length(nodepath->selected) == 1 && !center) {
4031         // flip handles of the single selected node
4032         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4033         double temp = n->p.pos[axis];
4034         n->p.pos[axis] = n->n.pos[axis];
4035         n->n.pos[axis] = temp;
4036         sp_node_update_handles(n, false);
4037     } else {
4038         // scale nodes as an "object":
4040         NR::Rect box = sp_node_selected_bbox (nodepath);
4041         if (!center) {
4042             center = box.midpoint();
4043         }
4044         NR::Matrix t =
4045             NR::Matrix (NR::translate(- *center)) *
4046             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4047             NR::Matrix (NR::translate(*center));
4049         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4050             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4051             n->pos *= t;
4052             n->n.pos *= t;
4053             n->p.pos *= t;
4054             sp_node_update_handles(n, false);
4055         }
4056     }
4058     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4061 NR::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4063     g_assert (nodepath->selected);
4065     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4066     NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4067     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4068         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4069         box.expandTo (n->pos); // contain all selected nodes
4070     }
4071     return box;
4074 //-----------------------------------------------
4075 /**
4076  * Return new subpath under given nodepath.
4077  */
4078 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4080     g_assert(nodepath);
4081     g_assert(nodepath->desktop);
4083    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4085     s->nodepath = nodepath;
4086     s->closed = FALSE;
4087     s->nodes = NULL;
4088     s->first = NULL;
4089     s->last = NULL;
4091     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4092     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4093     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4095     return s;
4098 /**
4099  * Destroy nodes in subpath, then subpath itself.
4100  */
4101 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4103     g_assert(subpath);
4104     g_assert(subpath->nodepath);
4105     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4107     while (subpath->nodes) {
4108         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4109     }
4111     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4113     g_free(subpath);
4116 /**
4117  * Link head to tail in subpath.
4118  */
4119 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4121     g_assert(!sp->closed);
4122     g_assert(sp->last != sp->first);
4123     g_assert(sp->first->code == NR_MOVETO);
4125     sp->closed = TRUE;
4127     //Link the head to the tail
4128     sp->first->p.other = sp->last;
4129     sp->last->n.other  = sp->first;
4130     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4131     sp->first          = sp->last;
4133     //Remove the extra end node
4134     sp_nodepath_node_destroy(sp->last->n.other);
4137 /**
4138  * Open closed (loopy) subpath at node.
4139  */
4140 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4142     g_assert(sp->closed);
4143     g_assert(n->subpath == sp);
4144     g_assert(sp->first == sp->last);
4146     /* We create new startpoint, current node will become last one */
4148    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4149                                                 &n->pos, &n->pos, &n->n.pos);
4152     sp->closed        = FALSE;
4154     //Unlink to make a head and tail
4155     sp->first         = new_path;
4156     sp->last          = n;
4157     n->n.other        = NULL;
4158     new_path->p.other = NULL;
4161 /**
4162  * Return new node in subpath with given properties.
4163  * \param pos Position of node.
4164  * \param ppos Handle position in previous direction
4165  * \param npos Handle position in previous direction
4166  */
4167 Inkscape::NodePath::Node *
4168 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)
4170     g_assert(sp);
4171     g_assert(sp->nodepath);
4172     g_assert(sp->nodepath->desktop);
4174     if (nodechunk == NULL)
4175         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4177     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4179     n->subpath  = sp;
4181     if (type != Inkscape::NodePath::NODE_NONE) {
4182         // use the type from sodipodi:nodetypes
4183         n->type = type;
4184     } else {
4185         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4186             // points are (almost) collinear
4187             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4188                 // endnode, or a node with a retracted handle
4189                 n->type = Inkscape::NodePath::NODE_CUSP;
4190             } else {
4191                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4192             }
4193         } else {
4194             n->type = Inkscape::NodePath::NODE_CUSP;
4195         }
4196     }
4198     n->code     = code;
4199     n->selected = FALSE;
4200     n->pos      = *pos;
4201     n->p.pos    = *ppos;
4202     n->n.pos    = *npos;
4204     n->dragging_out = NULL;
4206     Inkscape::NodePath::Node *prev;
4207     if (next) {
4208         //g_assert(g_list_find(sp->nodes, next));
4209         prev = next->p.other;
4210     } else {
4211         prev = sp->last;
4212     }
4214     if (prev)
4215         prev->n.other = n;
4216     else
4217         sp->first = n;
4219     if (next)
4220         next->p.other = n;
4221     else
4222         sp->last = n;
4224     n->p.other = prev;
4225     n->n.other = next;
4227     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"));
4228     sp_knot_set_position(n->knot, pos, 0);
4230     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4231     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4232     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4233     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4234     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4235     sp_knot_update_ctrl(n->knot);
4237     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4238     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4239     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4240     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4241     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4242     sp_knot_show(n->knot);
4244     // We only create handle knots and lines on demand
4245     n->p.knot = NULL;
4246     n->p.line = NULL;
4247     n->n.knot = NULL;
4248     n->n.line = NULL;
4250     sp->nodes = g_list_prepend(sp->nodes, n);
4252     return n;
4255 /**
4256  * Destroy node and its knots, link neighbors in subpath.
4257  */
4258 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4260     g_assert(node);
4261     g_assert(node->subpath);
4262     g_assert(SP_IS_KNOT(node->knot));
4264    Inkscape::NodePath::SubPath *sp = node->subpath;
4266     if (node->selected) { // first, deselect
4267         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4268         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4269     }
4271     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4273     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4274     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4275     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4276     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4277     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4278     g_object_unref(G_OBJECT(node->knot));
4280     if (node->p.knot) {
4281         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4282         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4283         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4284         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4285         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4286         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4287         g_object_unref(G_OBJECT(node->p.knot));
4288         node->p.knot = NULL;
4289     }
4291     if (node->n.knot) {
4292         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4293         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4294         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4295         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4296         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4297         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4298         g_object_unref(G_OBJECT(node->n.knot));
4299         node->n.knot = NULL;
4300     }
4302     if (node->p.line)
4303         gtk_object_destroy(GTK_OBJECT(node->p.line));
4304     if (node->n.line)
4305         gtk_object_destroy(GTK_OBJECT(node->n.line));
4307     if (sp->nodes) { // there are others nodes on the subpath
4308         if (sp->closed) {
4309             if (sp->first == node) {
4310                 g_assert(sp->last == node);
4311                 sp->first = node->n.other;
4312                 sp->last = sp->first;
4313             }
4314             node->p.other->n.other = node->n.other;
4315             node->n.other->p.other = node->p.other;
4316         } else {
4317             if (sp->first == node) {
4318                 sp->first = node->n.other;
4319                 sp->first->code = NR_MOVETO;
4320             }
4321             if (sp->last == node) sp->last = node->p.other;
4322             if (node->p.other) node->p.other->n.other = node->n.other;
4323             if (node->n.other) node->n.other->p.other = node->p.other;
4324         }
4325     } else { // this was the last node on subpath
4326         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4327     }
4329     g_mem_chunk_free(nodechunk, node);
4332 /**
4333  * Returns one of the node's two sides.
4334  * \param which Indicates which side.
4335  * \return Pointer to previous node side if which==-1, next if which==1.
4336  */
4337 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4339     g_assert(node);
4341     switch (which) {
4342         case -1:
4343             return &node->p;
4344         case 1:
4345             return &node->n;
4346         default:
4347             break;
4348     }
4350     g_assert_not_reached();
4352     return NULL;
4355 /**
4356  * Return the other side of the node, given one of its sides.
4357  */
4358 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4360     g_assert(node);
4362     if (me == &node->p) return &node->n;
4363     if (me == &node->n) return &node->p;
4365     g_assert_not_reached();
4367     return NULL;
4370 /**
4371  * Return NRPathcode on the given side of the node.
4372  */
4373 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4375     g_assert(node);
4377     if (me == &node->p) {
4378         if (node->p.other) return (NRPathcode)node->code;
4379         return NR_MOVETO;
4380     }
4382     if (me == &node->n) {
4383         if (node->n.other) return (NRPathcode)node->n.other->code;
4384         return NR_MOVETO;
4385     }
4387     g_assert_not_reached();
4389     return NR_END;
4392 /**
4393  * Return node with the given index
4394  */
4395 Inkscape::NodePath::Node *
4396 sp_nodepath_get_node_by_index(int index)
4398     Inkscape::NodePath::Node *e = NULL;
4400     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4401     if (!nodepath) {
4402         return e;
4403     }
4405     //find segment
4406     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4408         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4409         int n = g_list_length(sp->nodes);
4410         if (sp->closed) {
4411             n++;
4412         }
4414         //if the piece belongs to this subpath grab it
4415         //otherwise move onto the next subpath
4416         if (index < n) {
4417             e = sp->first;
4418             for (int i = 0; i < index; ++i) {
4419                 e = e->n.other;
4420             }
4421             break;
4422         } else {
4423             if (sp->closed) {
4424                 index -= (n+1);
4425             } else {
4426                 index -= n;
4427             }
4428         }
4429     }
4431     return e;
4434 /**
4435  * Returns plain text meaning of node type.
4436  */
4437 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4439     unsigned retracted = 0;
4440     bool endnode = false;
4442     for (int which = -1; which <= 1; which += 2) {
4443         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4444         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4445             retracted ++;
4446         if (!side->other)
4447             endnode = true;
4448     }
4450     if (retracted == 0) {
4451         if (endnode) {
4452                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4453                 return _("end node");
4454         } else {
4455             switch (node->type) {
4456                 case Inkscape::NodePath::NODE_CUSP:
4457                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4458                     return _("cusp");
4459                 case Inkscape::NodePath::NODE_SMOOTH:
4460                     // TRANSLATORS: "smooth" is an adjective here
4461                     return _("smooth");
4462                 case Inkscape::NodePath::NODE_SYMM:
4463                     return _("symmetric");
4464             }
4465         }
4466     } else if (retracted == 1) {
4467         if (endnode) {
4468             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4469             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4470         } else {
4471             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4472         }
4473     } else {
4474         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4475     }
4477     return NULL;
4480 /**
4481  * Handles content of statusbar as long as node tool is active.
4482  */
4483 void
4484 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4486     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");
4487     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4489     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4490     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4491     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4492     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4494     SPDesktop *desktop = NULL;
4495     if (nodepath) {
4496         desktop = nodepath->desktop;
4497     } else {
4498         desktop = SP_ACTIVE_DESKTOP;
4499     }
4501     SPEventContext *ec = desktop->event_context;
4502     if (!ec) return;
4503     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4504     if (!mc) return;
4506     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4508     if (selected_nodes == 0) {
4509         Inkscape::Selection *sel = desktop->selection;
4510         if (!sel || sel->isEmpty()) {
4511             mc->setF(Inkscape::NORMAL_MESSAGE,
4512                      _("Select a single object to edit its nodes or handles."));
4513         } else {
4514             if (nodepath) {
4515             mc->setF(Inkscape::NORMAL_MESSAGE,
4516                      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.",
4517                               "<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.",
4518                               total_nodes),
4519                      total_nodes);
4520             } else {
4521                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4522                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4523                 } else {
4524                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4525                 }
4526             }
4527         }
4528     } else if (nodepath && selected_nodes == 1) {
4529         mc->setF(Inkscape::NORMAL_MESSAGE,
4530                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4531                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4532                           total_nodes),
4533                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4534     } else {
4535         if (selected_subpaths > 1) {
4536             mc->setF(Inkscape::NORMAL_MESSAGE,
4537                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4538                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4539                               total_nodes),
4540                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4541         } else {
4542             mc->setF(Inkscape::NORMAL_MESSAGE,
4543                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4544                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4545                               total_nodes),
4546                      selected_nodes, total_nodes, when_selected);
4547         }
4548     }
4551 /*
4552  * returns a *copy* of the curve of that object.
4553  */
4554 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4555     if (!object)
4556         return NULL;
4558     SPCurve *curve = NULL;
4559     if (SP_IS_PATH(object)) {
4560         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4561         curve = sp_curve_copy(curve_new);
4562     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4563         const gchar *svgd = object->repr->attribute(key);
4564         if (svgd) {
4565             NArtBpath *bpath = sp_svg_read_path(svgd);
4566             SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
4567             if (curve_new) {
4568                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4569             } else {
4570                 g_free(bpath);
4571             }
4572         }
4573     }
4575     return curve;
4578 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4579     if (!np || !np->object || !curve)
4580         return;
4582     if (SP_IS_PATH(np->object)) {
4583         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4584             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4585         } else {
4586             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4587         }
4588     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4589         // FIXME: this writing to string and then reading from string is bound to be slow.
4590         // create a method to convert from curve directly to 2geom...
4591         gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
4592         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4593         g_free(svgpath);
4595         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4596     }
4599 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4600     np->show_helperpath = show;
4602     if (show) {
4603         SPCurve *helper_curve = sp_curve_copy(np->curve);
4604         sp_curve_transform(helper_curve, np->i2d );
4605         if (!np->helper_path) {
4606             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4607             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);
4608             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4609             sp_canvas_item_move_to_z(np->helper_path, 0);
4610             sp_canvas_item_show(np->helper_path);
4611         } else {
4612             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4613         }
4614         sp_curve_unref(helper_curve);
4615     } else {
4616         if (np->helper_path) {
4617             GtkObject *temp = np->helper_path;
4618             np->helper_path = NULL;
4619             gtk_object_destroy(temp);
4620         }
4621     }
4624 /* this function does not work yet */
4625 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4626     np->straight_path = true;
4627     np->show_handles = false;
4628     g_message("add code to make the path straight.");
4629     // do sp_nodepath_convert_node_type on all nodes?
4630     // search for this text !!!   "Make selected segments lines"
4634 /*
4635   Local Variables:
4636   mode:c++
4637   c-file-style:"stroustrup"
4638   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4639   indent-tabs-mode:nil
4640   fill-column:99
4641   End:
4642 */
4643 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :