Code

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