Code

make setup_notepath function for LPE's and LPE parameters.
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include <glibmm/i18n.h>
23 #include "libnr/n-art-bpath.h"
24 #include "libnr/nr-path.h"
25 #include "helper/units.h"
26 #include "knot.h"
27 #include "inkscape.h"
28 #include "document.h"
29 #include "sp-namedview.h"
30 #include "desktop.h"
31 #include "desktop-handles.h"
32 #include "snap.h"
33 #include "message-stack.h"
34 #include "message-context.h"
35 #include "node-context.h"
36 #include "shape-editor.h"
37 #include "selection-chemistry.h"
38 #include "selection.h"
39 #include "xml/repr.h"
40 #include "prefs-utils.h"
41 #include "sp-metrics.h"
42 #include "sp-path.h"
43 #include "libnr/nr-matrix-ops.h"
44 #include "splivarot.h"
45 #include "svg/svg.h"
46 #include "verbs.h"
47 #include "display/bezier-utils.h"
48 #include <vector>
49 #include <algorithm>
50 #include "live_effects/lpeobject.h"
51 #include "live_effects/parameter/parameter.h"
53 class NR::Matrix;
55 /// \todo
56 /// evil evil evil. FIXME: conflict of two different Path classes!
57 /// There is a conflict in the namespace between two classes named Path.
58 /// #include "sp-flowtext.h"
59 /// #include "sp-flowregion.h"
61 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
62 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
63 GType sp_flowregion_get_type (void);
64 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
65 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
66 GType sp_flowtext_get_type (void);
67 // end evil workaround
69 #include "helper/stlport.h"
72 /// \todo fixme: Implement these via preferences */
74 #define NODE_FILL          0xbfbfbf00
75 #define NODE_STROKE        0x000000ff
76 #define NODE_FILL_HI       0xff000000
77 #define NODE_STROKE_HI     0x000000ff
78 #define NODE_FILL_SEL      0x0000ffff
79 #define NODE_STROKE_SEL    0x000000ff
80 #define NODE_FILL_SEL_HI   0xff000000
81 #define NODE_STROKE_SEL_HI 0x000000ff
82 #define KNOT_FILL          0xffffffff
83 #define KNOT_STROKE        0x000000ff
84 #define KNOT_FILL_HI       0xff000000
85 #define KNOT_STROKE_HI     0x000000ff
87 static GMemChunk *nodechunk = NULL;
89 /* Creation from object */
91 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
92 static gchar *parse_nodetypes(gchar const *types, gint length);
94 /* Object updating */
96 static void stamp_repr(Inkscape::NodePath::Path *np);
97 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
98 static gchar *create_typestr(Inkscape::NodePath::Path *np);
100 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
102 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
104 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
106 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
108 /* Adjust handle placement, if the node or the other handle is moved */
109 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
110 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
112 /* Node event callbacks */
113 static void node_clicked(SPKnot *knot, guint state, gpointer data);
114 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
115 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
116 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
118 /* Handle event callbacks */
119 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
120 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
121 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
122 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
123 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
124 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
126 /* Constructors and destructors */
128 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
129 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
130 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
131 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
132 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
133                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
134 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
136 /* Helpers */
138 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
139 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
140 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
142 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
143 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
145 // active_node indicates mouseover node
146 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
148 /**
149  * \brief Creates new nodepath from item
150  */
151 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
153     Inkscape::XML::Node *repr = object->repr;
155     /** \todo
156      * FIXME: remove this. We don't want to edit paths inside flowtext.
157      * Instead we will build our flowtext with cloned paths, so that the
158      * real paths are outside the flowtext and thus editable as usual.
159      */
160     if (SP_IS_FLOWTEXT(object)) {
161         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
162             if SP_IS_FLOWREGION(child) {
163                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
164                 if (grandchild && SP_IS_PATH(grandchild)) {
165                     object = SP_ITEM(grandchild);
166                     break;
167                 }
168             }
169         }
170     }
172     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
174     if (curve == NULL)
175         return NULL;
177     NArtBpath *bpath = sp_curve_first_bpath(curve);
178     gint length = curve->end;
179     if (length == 0) {
180         sp_curve_unref(curve);
181         return NULL; // prevent crash for one-node paths
182     }
184     //Create new nodepath
185     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
186     if (!np) {
187         sp_curve_unref(curve);
188         return NULL;
189     }
191     // Set defaults
192     np->desktop     = desktop;
193     np->object      = object;
194     np->subpaths    = NULL;
195     np->selected    = NULL;
196     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
197     np->livarot_path = NULL;
198     np->local_change = 0;
199     np->show_handles = show_handles;
200     np->helper_path = NULL;
201     np->curve = sp_curve_copy(curve);
202     np->show_helperpath = false;
203     np->straight_path = false;
204     if (IS_LIVEPATHEFFECT(object) && item) {
205         np->item = item;
206     } else {
207         np->item = SP_ITEM(object);
208     }
210     // we need to update item's transform from the repr here,
211     // because they may be out of sync when we respond
212     // to a change in repr by regenerating nodepath     --bb
213     sp_object_read_attr(SP_OBJECT(np->item), "transform");
215     np->i2d  = sp_item_i2d_affine(np->item);
216     np->d2i  = np->i2d.inverse();
218     np->repr = repr;
219     if (repr_key_in) { // apparantly the object is an LPEObject
220         np->repr_key = g_strdup(repr_key_in);
221         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
222         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
223         if (lpeparam) {
224             lpeparam->param_setup_notepath(np);
225         }
226     } else {
227         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
228         if ( SP_SHAPE(np->object)->path_effect_href ) {
229             np->repr_key = g_strdup("inkscape:original-d");
231             LivePathEffectObject *lpeobj = sp_shape_get_livepatheffectobject(SP_SHAPE(np->object));
232             if (lpeobj && lpeobj->lpe) {
233                 lpeobj->lpe->setup_notepath(np);
234             }
235         } else {
236             np->repr_key = g_strdup("d");
237         }
238     }
240     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
241     gchar *typestr = parse_nodetypes(nodetypes, length);
243     // create the subpath(s) from the bpath
244     NArtBpath *b = bpath;
245     while (b->code != NR_END) {
246         b = subpath_from_bpath(np, b, typestr + (b - bpath));
247     }
249     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
250     np->subpaths = g_list_reverse(np->subpaths);
252     g_free(typestr);
253     sp_curve_unref(curve);
255     // create the livarot representation from the same item
256     sp_nodepath_ensure_livarot_path(np);
258     // Draw helper curve
259     if (np->show_helperpath) {
260         SPCurve *helper_curve = sp_curve_copy(np->curve);
261         sp_curve_transform(helper_curve, np->i2d );
262         np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(desktop), helper_curve);
263         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);
264         sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
265         sp_canvas_item_show(np->helper_path);
266         sp_curve_unref(helper_curve);
267     }
269     return np;
272 /**
273  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
274  */
275 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
277     if (!np)  //soft fail, like delete
278         return;
280     while (np->subpaths) {
281         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
282     }
284     //Inform the ShapeEditor that made me, if any, that I am gone.
285     if (np->shape_editor)
286         np->shape_editor->nodepath_destroyed();
288     g_assert(!np->selected);
290     if (np->livarot_path) {
291         delete np->livarot_path;
292         np->livarot_path = NULL;
293     }
294     
295     if (np->helper_path) {
296         GtkObject *temp = np->helper_path;
297         np->helper_path = NULL;
298         gtk_object_destroy(temp);
299     }
300     if (np->curve) {
301         sp_curve_unref(np->curve);
302         np->curve = NULL;
303     }
305     if (np->repr_key) {
306         g_free(np->repr_key);
307         np->repr_key = NULL;
308     }
309     if (np->repr_nodetypes_key) {
310         g_free(np->repr_nodetypes_key);
311         np->repr_nodetypes_key = NULL;
312     }
314     np->desktop = NULL;
316     g_free(np);
320 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
322     if (np && np->livarot_path == NULL) {
323         SPCurve *curve = create_curve(np);
324         NArtBpath *bpath = SP_CURVE_BPATH(curve);
325         np->livarot_path = bpath_to_Path(bpath);
327         if (np->livarot_path)
328             np->livarot_path->ConvertWithBackData(0.01);
330         sp_curve_unref(curve);
331     }
335 /**
336  *  Return the node count of a given NodeSubPath.
337  */
338 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
340     if (!subpath)
341         return 0;
342     gint nodeCount = g_list_length(subpath->nodes);
343     return nodeCount;
346 /**
347  *  Return the node count of a given NodePath.
348  */
349 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
351     if (!np)
352         return 0;
353     gint nodeCount = 0;
354     for (GList *item = np->subpaths ; item ; item=item->next) {
355        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
356         nodeCount += g_list_length(subpath->nodes);
357     }
358     return nodeCount;
361 /**
362  *  Return the subpath count of a given NodePath.
363  */
364 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
366     if (!np)
367         return 0;
368     return g_list_length (np->subpaths);
371 /**
372  *  Return the selected node count of a given NodePath.
373  */
374 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
376     if (!np)
377         return 0;
378     return g_list_length (np->selected);
381 /**
382  *  Return the number of subpaths where nodes are selected in a given NodePath.
383  */
384 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
386     if (!np)
387         return 0;
388     if (!np->selected)
389         return 0;
390     if (!np->selected->next)
391         return 1;
392     gint count = 0;
393     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
394         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
395         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
396             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
397             if (node->selected) {
398                 count ++;
399                 break;
400             }
401         }
402     }
403     return count;
405  
406 /**
407  * Clean up a nodepath after editing.
408  *
409  * Currently we are deleting trivial subpaths.
410  */
411 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
413     GList *badSubPaths = NULL;
415     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
416     for (GList *l = nodepath->subpaths; l ; l=l->next) {
417        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
418        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
419             badSubPaths = g_list_append(badSubPaths, sp);
420     }
422     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
423     //also removes the subpath from nodepath->subpaths
424     for (GList *l = badSubPaths; l ; l=l->next) {
425        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
426         sp_nodepath_subpath_destroy(sp);
427     }
429     g_list_free(badSubPaths);
432 /**
433  * Create new nodepath from b, make it subpath of np.
434  * \param t The node type.
435  * \todo Fixme: t should be a proper type, rather than gchar
436  */
437 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
439     NR::Point ppos, pos, npos;
441     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
443     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
444     bool const closed = (b->code == NR_MOVETO);
446     pos = NR::Point(b->x3, b->y3) * np->i2d;
447     if (b[1].code == NR_CURVETO) {
448         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
449     } else {
450         npos = pos;
451     }
452     Inkscape::NodePath::Node *n;
453     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
454     g_assert(sp->first == n);
455     g_assert(sp->last  == n);
457     b++;
458     t++;
459     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
460         pos = NR::Point(b->x3, b->y3) * np->i2d;
461         if (b->code == NR_CURVETO) {
462             ppos = NR::Point(b->x2, b->y2) * np->i2d;
463         } else {
464             ppos = pos;
465         }
466         if (b[1].code == NR_CURVETO) {
467             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
468         } else {
469             npos = pos;
470         }
471         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
472         b++;
473         t++;
474     }
476     if (closed) sp_nodepath_subpath_close(sp);
478     return b;
481 /**
482  * Convert from sodipodi:nodetypes to new style type string.
483  */
484 static gchar *parse_nodetypes(gchar const *types, gint length)
486     g_assert(length > 0);
488     gchar *typestr = g_new(gchar, length + 1);
490     gint pos = 0;
492     if (types) {
493         for (gint i = 0; types[i] && ( i < length ); i++) {
494             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
495             if (types[i] != '\0') {
496                 switch (types[i]) {
497                     case 's':
498                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
499                         break;
500                     case 'z':
501                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
502                         break;
503                     case 'c':
504                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
505                         break;
506                     default:
507                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
508                         break;
509                 }
510             }
511         }
512     }
514     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
516     return typestr;
519 /**
520  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
521  * updated but repr is not (for speed). Used during curve and node drag.
522  */
523 static void update_object(Inkscape::NodePath::Path *np)
525     g_assert(np);
527     sp_curve_unref(np->curve);
528     np->curve = create_curve(np);
530     sp_nodepath_set_curve(np, np->curve);
532     if (np->show_helperpath) {
533         SPCurve * helper_curve = sp_curve_copy(np->curve);
534         sp_curve_transform(helper_curve, np->i2d );
535         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
536         sp_curve_unref(helper_curve);
537     }
540 /**
541  * Update XML path node with data from path object.
542  */
543 static void update_repr_internal(Inkscape::NodePath::Path *np)
545     g_assert(np);
547     Inkscape::XML::Node *repr = np->object->repr;
549     sp_curve_unref(np->curve);
550     np->curve = create_curve(np);
551     
552     gchar *typestr = create_typestr(np);
553     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
555     // determine if path has an effect applied and write to correct "d" attribute.
556     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
557         np->local_change++;
558         repr->setAttribute(np->repr_key, svgpath);
559     }
561     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
562         np->local_change++;
563         repr->setAttribute(np->repr_nodetypes_key, typestr);
564     }
566     g_free(svgpath);
567     g_free(typestr);
569     if (np->show_helperpath) {
570         SPCurve * helper_curve = sp_curve_copy(np->curve);
571         sp_curve_transform(helper_curve, np->i2d );
572         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
573         sp_curve_unref(helper_curve);
574     }
575  }
577 /**
578  * Update XML path node with data from path object, commit changes forever.
579  */
580 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
582     //fixme: np can be NULL, so check before proceeding
583     g_return_if_fail(np != NULL);
585     if (np->livarot_path) {
586         delete np->livarot_path;
587         np->livarot_path = NULL;
588     }
590     update_repr_internal(np);
591     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
592     
593     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
594                      annotation);
597 /**
598  * Update XML path node with data from path object, commit changes with undo.
599  */
600 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
602     if (np->livarot_path) {
603         delete np->livarot_path;
604         np->livarot_path = NULL;
605     }
607     update_repr_internal(np);
608     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
609                            annotation);
612 /**
613  * Make duplicate of path, replace corresponding XML node in tree, commit.
614  */
615 static void stamp_repr(Inkscape::NodePath::Path *np)
617     g_assert(np);
619     Inkscape::XML::Node *old_repr = np->object->repr;
620     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
622     // remember the position of the item
623     gint pos = old_repr->position();
624     // remember parent
625     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
627     SPCurve *curve = create_curve(np);
628     gchar *typestr = create_typestr(np);
630     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
632     new_repr->setAttribute(np->repr_key, svgpath);
633     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
635     // add the new repr to the parent
636     parent->appendChild(new_repr);
637     // move to the saved position
638     new_repr->setPosition(pos > 0 ? pos : 0);
640     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
641                      _("Stamp"));
643     Inkscape::GC::release(new_repr);
644     g_free(svgpath);
645     g_free(typestr);
646     sp_curve_unref(curve);
649 /**
650  * Create curve from path.
651  */
652 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
654     SPCurve *curve = sp_curve_new();
656     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
657        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
658         sp_curve_moveto(curve,
659                         sp->first->pos * np->d2i);
660        Inkscape::NodePath::Node *n = sp->first->n.other;
661         while (n) {
662             NR::Point const end_pt = n->pos * np->d2i;
663             switch (n->code) {
664                 case NR_LINETO:
665                     sp_curve_lineto(curve, end_pt);
666                     break;
667                 case NR_CURVETO:
668                     sp_curve_curveto(curve,
669                                      n->p.other->n.pos * np->d2i,
670                                      n->p.pos * np->d2i,
671                                      end_pt);
672                     break;
673                 default:
674                     g_assert_not_reached();
675                     break;
676             }
677             if (n != sp->last) {
678                 n = n->n.other;
679             } else {
680                 n = NULL;
681             }
682         }
683         if (sp->closed) {
684             sp_curve_closepath(curve);
685         }
686     }
688     return curve;
691 /**
692  * Convert path type string to sodipodi:nodetypes style.
693  */
694 static gchar *create_typestr(Inkscape::NodePath::Path *np)
696     gchar *typestr = g_new(gchar, 32);
697     gint len = 32;
698     gint pos = 0;
700     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
701        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
703         if (pos >= len) {
704             typestr = g_renew(gchar, typestr, len + 32);
705             len += 32;
706         }
708         typestr[pos++] = 'c';
710        Inkscape::NodePath::Node *n;
711         n = sp->first->n.other;
712         while (n) {
713             gchar code;
715             switch (n->type) {
716                 case Inkscape::NodePath::NODE_CUSP:
717                     code = 'c';
718                     break;
719                 case Inkscape::NodePath::NODE_SMOOTH:
720                     code = 's';
721                     break;
722                 case Inkscape::NodePath::NODE_SYMM:
723                     code = 'z';
724                     break;
725                 default:
726                     g_assert_not_reached();
727                     code = '\0';
728                     break;
729             }
731             if (pos >= len) {
732                 typestr = g_renew(gchar, typestr, len + 32);
733                 len += 32;
734             }
736             typestr[pos++] = code;
738             if (n != sp->last) {
739                 n = n->n.other;
740             } else {
741                 n = NULL;
742             }
743         }
744     }
746     if (pos >= len) {
747         typestr = g_renew(gchar, typestr, len + 1);
748         len += 1;
749     }
751     typestr[pos++] = '\0';
753     return typestr;
756 /**
757  * Returns current path in context. // later eliminate this function at all!
758  */
759 static Inkscape::NodePath::Path *sp_nodepath_current()
761     if (!SP_ACTIVE_DESKTOP) {
762         return NULL;
763     }
765     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
767     if (!SP_IS_NODE_CONTEXT(event_context)) {
768         return NULL;
769     }
771     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
776 /**
777  \brief Fills node and handle positions for three nodes, splitting line
778   marked by end at distance t.
779  */
780 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
782     g_assert(new_path != NULL);
783     g_assert(end      != NULL);
785     g_assert(end->p.other == new_path);
786    Inkscape::NodePath::Node *start = new_path->p.other;
787     g_assert(start);
789     if (end->code == NR_LINETO) {
790         new_path->type =Inkscape::NodePath::NODE_CUSP;
791         new_path->code = NR_LINETO;
792         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
793     } else {
794         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
795         new_path->code = NR_CURVETO;
796         gdouble s      = 1 - t;
797         for (int dim = 0; dim < 2; dim++) {
798             NR::Coord const f000 = start->pos[dim];
799             NR::Coord const f001 = start->n.pos[dim];
800             NR::Coord const f011 = end->p.pos[dim];
801             NR::Coord const f111 = end->pos[dim];
802             NR::Coord const f00t = s * f000 + t * f001;
803             NR::Coord const f01t = s * f001 + t * f011;
804             NR::Coord const f11t = s * f011 + t * f111;
805             NR::Coord const f0tt = s * f00t + t * f01t;
806             NR::Coord const f1tt = s * f01t + t * f11t;
807             NR::Coord const fttt = s * f0tt + t * f1tt;
808             start->n.pos[dim]    = f00t;
809             new_path->p.pos[dim] = f0tt;
810             new_path->pos[dim]   = fttt;
811             new_path->n.pos[dim] = f1tt;
812             end->p.pos[dim]      = f11t;
813         }
814     }
817 /**
818  * Adds new node on direct line between two nodes, activates handles of all
819  * three nodes.
820  */
821 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
823     g_assert(end);
824     g_assert(end->subpath);
825     g_assert(g_list_find(end->subpath->nodes, end));
827    Inkscape::NodePath::Node *start = end->p.other;
828     g_assert( start->n.other == end );
829    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
830                                                end,
831                                                (NRPathcode)end->code == NR_LINETO? 
832                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
833                                                (NRPathcode)end->code,
834                                                &start->pos, &start->pos, &start->n.pos);
835     sp_nodepath_line_midpoint(newnode, end, t);
837     sp_node_adjust_handles(start);
838     sp_node_update_handles(start);
839     sp_node_update_handles(newnode);
840     sp_node_adjust_handles(end);
841     sp_node_update_handles(end);
843     return newnode;
846 /**
847 \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
848 */
849 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
851     g_assert(node);
852     g_assert(node->subpath);
853     g_assert(g_list_find(node->subpath->nodes, node));
855    Inkscape::NodePath::SubPath *sp = node->subpath;
856     Inkscape::NodePath::Path *np    = sp->nodepath;
858     if (sp->closed) {
859         sp_nodepath_subpath_open(sp, node);
860         return sp->first;
861     } else {
862         // no break for end nodes
863         if (node == sp->first) return NULL;
864         if (node == sp->last ) return NULL;
866         // create a new subpath
867        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
869         // duplicate the break node as start of the new subpath
870        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
872         while (node->n.other) { // copy the remaining nodes into the new subpath
873            Inkscape::NodePath::Node *n  = node->n.other;
874            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);
875             if (n->selected) {
876                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
877             }
878             sp_nodepath_node_destroy(n); // remove the point on the original subpath
879         }
881         return newnode;
882     }
885 /**
886  * Duplicate node and connect to neighbours.
887  */
888 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
890     g_assert(node);
891     g_assert(node->subpath);
892     g_assert(g_list_find(node->subpath->nodes, node));
894    Inkscape::NodePath::SubPath *sp = node->subpath;
896     NRPathcode code = (NRPathcode) node->code;
897     if (code == NR_MOVETO) { // if node is the endnode,
898         node->code = NR_LINETO; // new one is inserted before it, so change that to line
899     }
901     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
903     if (!node->n.other || !node->p.other) // if node is an endnode, select it
904         return node;
905     else
906         return newnode; // otherwise select the newly created node
909 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
911     node->p.pos = (node->pos + (node->pos - node->n.pos));
914 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
916     node->n.pos = (node->pos + (node->pos - node->p.pos));
919 /**
920  * Change line type at node, with side effects on neighbours.
921  */
922 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
924     g_assert(end);
925     g_assert(end->subpath);
926     g_assert(end->p.other);
928     if (end->code == static_cast< guint > ( code ) )
929         return;
931    Inkscape::NodePath::Node *start = end->p.other;
933     end->code = code;
935     if (code == NR_LINETO) {
936         if (start->code == NR_LINETO) {
937             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
938         }
939         if (end->n.other) {
940             if (end->n.other->code == NR_LINETO) {
941                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
942             }
943         }
944     } else {
945         NR::Point delta = end->pos - start->pos;
946         start->n.pos = start->pos + delta / 3;
947         end->p.pos = end->pos - delta / 3;
948         sp_node_adjust_handle(start, 1);
949         sp_node_adjust_handle(end, -1);
950     }
952     sp_node_update_handles(start);
953     sp_node_update_handles(end);
956 /**
957  * Change node type, and its handles accordingly.
958  */
959 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
961     g_assert(node);
962     g_assert(node->subpath);
964     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
965         return node;
967     if ((node->p.other != NULL) && (node->n.other != NULL)) {
968         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
969             type =Inkscape::NodePath::NODE_CUSP;
970         }
971     }
973     node->type = type;
975     if (node->type == Inkscape::NodePath::NODE_CUSP) {
976         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
977         node->knot->setSize (node->selected? 11 : 9);
978         sp_knot_update_ctrl(node->knot);
979     } else {
980         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
981         node->knot->setSize (node->selected? 9 : 7);
982         sp_knot_update_ctrl(node->knot);
983     }
985     // if one of handles is mouseovered, preserve its position
986     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
987         sp_node_adjust_handle(node, 1);
988     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
989         sp_node_adjust_handle(node, -1);
990     } else {
991         sp_node_adjust_handles(node);
992     }
994     sp_node_update_handles(node);
996     sp_nodepath_update_statusbar(node->subpath->nodepath);
998     return node;
1001 /**
1002  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
1003  * adjacent segments from lines to curves.
1004 */
1005 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1007     bool p_line = (node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos);
1008     bool n_line = (node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos);
1010     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1011         if (p_line && n_line) {
1012             // only if both adjacent segments are lines, 
1013             // convert both to curves:
1015             // BEFORE:
1016             {
1017             node->code = NR_CURVETO;
1018             NR::Point delta = node->n.other->pos - node->p.other->pos;
1019             node->p.pos = node->pos - delta / 4;
1020             }
1022             // AFTER:
1023             {
1024             node->n.other->code = NR_CURVETO;
1025             NR::Point delta = node->p.other->pos - node->n.other->pos;
1026             node->n.pos = node->pos - delta / 4;
1027             }
1029             sp_node_update_handles(node);
1030         }
1031     }
1033     sp_nodepath_set_node_type (node, type);
1036 /**
1037  * Move node to point, and adjust its and neighbouring handles.
1038  */
1039 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1041     NR::Point delta = p - node->pos;
1042     node->pos = p;
1044     node->p.pos += delta;
1045     node->n.pos += delta;
1047     Inkscape::NodePath::Node *node_p = NULL;
1048     Inkscape::NodePath::Node *node_n = NULL;
1050     if (node->p.other) {
1051         if (node->code == NR_LINETO) {
1052             sp_node_adjust_handle(node, 1);
1053             sp_node_adjust_handle(node->p.other, -1);
1054             node_p = node->p.other;
1055         }
1056     }
1057     if (node->n.other) {
1058         if (node->n.other->code == NR_LINETO) {
1059             sp_node_adjust_handle(node, -1);
1060             sp_node_adjust_handle(node->n.other, 1);
1061             node_n = node->n.other;
1062         }
1063     }
1065     // this function is only called from batch movers that will update display at the end
1066     // themselves, so here we just move all the knots without emitting move signals, for speed
1067     sp_node_update_handles(node, false);
1068     if (node_n) {
1069         sp_node_update_handles(node_n, false);
1070     }
1071     if (node_p) {
1072         sp_node_update_handles(node_p, false);
1073     }
1076 /**
1077  * Call sp_node_moveto() for node selection and handle possible snapping.
1078  */
1079 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1080                                             bool const snap = true)
1082     NR::Coord best = NR_HUGE;
1083     NR::Point delta(dx, dy);
1084     NR::Point best_pt = delta;
1086     if (snap) {
1087         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
1088         
1089         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1090             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1091             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, n->pos + delta, SP_PATH(n->subpath->nodepath->item));
1092             if (s.getDistance() < best) {
1093                 best = s.getDistance();
1094                 best_pt = s.getPoint() - n->pos;
1095             }
1096         }
1097     }
1099     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1100         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1101         sp_node_moveto(n, n->pos + best_pt);
1102     }
1104     // do not update repr here so that node dragging is acceptably fast
1105     update_object(nodepath);
1108 /**
1109 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1110 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1111 near x = 0.
1112  */
1113 double
1114 sculpt_profile (double x, double alpha, guint profile)
1116     if (x >= 1)
1117         return 0;
1118     if (x <= 0)
1119         return 1;
1121     switch (profile) {
1122         case SCULPT_PROFILE_LINEAR:
1123         return 1 - x;
1124         case SCULPT_PROFILE_BELL:
1125         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1126         case SCULPT_PROFILE_ELLIPTIC:
1127         return sqrt(1 - x*x);
1128     }
1130     return 1;
1133 double
1134 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1136     // extremely primitive for now, don't have time to look for the real one
1137     double lower = NR::L2(b - a);
1138     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1139     return (lower + upper)/2;
1142 void
1143 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1145     n->pos = n->origin + delta;
1146     n->n.pos = n->n.origin + delta_n;
1147     n->p.pos = n->p.origin + delta_p;
1148     sp_node_adjust_handles(n);
1149     sp_node_update_handles(n, false);
1152 /**
1153  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1154  * on how far they are from the dragged node n.
1155  */
1156 static void 
1157 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1159     g_assert (n);
1160     g_assert (nodepath);
1161     g_assert (n->subpath->nodepath == nodepath);
1163     double pressure = n->knot->pressure;
1164     if (pressure == 0)
1165         pressure = 0.5; // default
1166     pressure = CLAMP (pressure, 0.2, 0.8);
1168     // map pressure to alpha = 1/5 ... 5
1169     double alpha = 1 - 2 * fabs(pressure - 0.5);
1170     if (pressure > 0.5)
1171         alpha = 1/alpha;
1173     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1175     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1176         // Only one subpath has selected nodes:
1177         // use linear mode, where the distance from n to node being dragged is calculated along the path
1179         double n_sel_range = 0, p_sel_range = 0;
1180         guint n_nodes = 0, p_nodes = 0;
1181         guint n_sel_nodes = 0, p_sel_nodes = 0;
1183         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1184         {
1185             double n_range = 0, p_range = 0;
1186             bool n_going = true, p_going = true;
1187             Inkscape::NodePath::Node *n_node = n;
1188             Inkscape::NodePath::Node *p_node = n;
1189             do {
1190                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1191                 if (n_node && n_going)
1192                     n_node = n_node->n.other;
1193                 if (n_node == NULL) {
1194                     n_going = false;
1195                 } else {
1196                     n_nodes ++;
1197                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1198                     if (n_node->selected) {
1199                         n_sel_nodes ++;
1200                         n_sel_range = n_range;
1201                     }
1202                     if (n_node == p_node) {
1203                         n_going = false;
1204                         p_going = false;
1205                     }
1206                 }
1207                 if (p_node && p_going)
1208                     p_node = p_node->p.other;
1209                 if (p_node == NULL) {
1210                     p_going = false;
1211                 } else {
1212                     p_nodes ++;
1213                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1214                     if (p_node->selected) {
1215                         p_sel_nodes ++;
1216                         p_sel_range = p_range;
1217                     }
1218                     if (p_node == n_node) {
1219                         n_going = false;
1220                         p_going = false;
1221                     }
1222                 }
1223             } while (n_going || p_going);
1224         }
1226         // Second pass: actually move nodes in this subpath
1227         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1228         {
1229             double n_range = 0, p_range = 0;
1230             bool n_going = true, p_going = true;
1231             Inkscape::NodePath::Node *n_node = n;
1232             Inkscape::NodePath::Node *p_node = n;
1233             do {
1234                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1235                 if (n_node && n_going)
1236                     n_node = n_node->n.other;
1237                 if (n_node == NULL) {
1238                     n_going = false;
1239                 } else {
1240                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1241                     if (n_node->selected) {
1242                         sp_nodepath_move_node_and_handles (n_node, 
1243                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1244                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1245                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1246                     }
1247                     if (n_node == p_node) {
1248                         n_going = false;
1249                         p_going = false;
1250                     }
1251                 }
1252                 if (p_node && p_going)
1253                     p_node = p_node->p.other;
1254                 if (p_node == NULL) {
1255                     p_going = false;
1256                 } else {
1257                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1258                     if (p_node->selected) {
1259                         sp_nodepath_move_node_and_handles (p_node, 
1260                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1261                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1262                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1263                     }
1264                     if (p_node == n_node) {
1265                         n_going = false;
1266                         p_going = false;
1267                     }
1268                 }
1269             } while (n_going || p_going);
1270         }
1272     } else {
1273         // Multiple subpaths have selected nodes:
1274         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1275         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1276         // fix the pear-like shape when sculpting e.g. a ring
1278         // First pass: calculate range
1279         gdouble direct_range = 0;
1280         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1281             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1282             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1283                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1284                 if (node->selected) {
1285                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1286                 }
1287             }
1288         }
1290         // Second pass: actually move nodes
1291         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1292             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1293             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1294                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1295                 if (node->selected) {
1296                     if (direct_range > 1e-6) {
1297                         sp_nodepath_move_node_and_handles (node,
1298                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1299                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1300                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1301                     } else {
1302                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1303                     }
1305                 }
1306             }
1307         }
1308     }
1310     // do not update repr here so that node dragging is acceptably fast
1311     update_object(nodepath);
1315 /**
1316  * Move node selection to point, adjust its and neighbouring handles,
1317  * handle possible snapping, and commit the change with possible undo.
1318  */
1319 void
1320 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1322     if (!nodepath) return;
1324     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1326     if (dx == 0) {
1327         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1328     } else if (dy == 0) {
1329         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1330     } else {
1331         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1332     }
1335 /**
1336  * Move node selection off screen and commit the change.
1337  */
1338 void
1339 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1341     // borrowed from sp_selection_move_screen in selection-chemistry.c
1342     // we find out the current zoom factor and divide deltas by it
1343     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1345     gdouble zoom = desktop->current_zoom();
1346     gdouble zdx = dx / zoom;
1347     gdouble zdy = dy / zoom;
1349     if (!nodepath) return;
1351     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1353     if (dx == 0) {
1354         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1355     } else if (dy == 0) {
1356         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1357     } else {
1358         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1359     }
1362 /** If they don't yet exist, creates knot and line for the given side of the node */
1363 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1365     if (!side->knot) {
1366         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"));
1368         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1369         side->knot->setSize (7);
1370         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1371         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1372         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1373         sp_knot_update_ctrl(side->knot);
1375         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1376         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1377         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1378         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1379         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1380         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1381     }
1383     if (!side->line) {
1384         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1385                                         SP_TYPE_CTRLLINE, NULL);
1386     }
1389 /**
1390  * Ensure the given handle of the node is visible/invisible, update its screen position
1391  */
1392 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1394     g_assert(node != NULL);
1396    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1397     NRPathcode code = sp_node_path_code_from_side(node, side);
1399     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1401     if (show_handle) {
1402         if (!side->knot) { // No handle knot at all
1403             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1404             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1405             side->knot->pos = side->pos;
1406             if (side->knot->item) 
1407                 SP_CTRL(side->knot->item)->moveto(side->pos);
1408             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1409             sp_knot_show(side->knot);
1410         } else {
1411             if (side->knot->pos != side->pos) { // only if it's really moved
1412                 if (fire_move_signals) {
1413                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1414                 } else {
1415                     sp_knot_moveto(side->knot, &side->pos);
1416                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1417                 }
1418             }
1419             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1420                 sp_knot_show(side->knot);
1421             }
1422         }
1423         sp_canvas_item_show(side->line);
1424     } else {
1425         if (side->knot) {
1426             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1427                 sp_knot_hide(side->knot);
1428             }
1429         }
1430         if (side->line) {
1431             sp_canvas_item_hide(side->line);
1432         }
1433     }
1436 /**
1437  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1438  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1439  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1440  * updated; otherwise, just move the knots silently (used in batch moves).
1441  */
1442 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1444     g_assert(node != NULL);
1446     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1447         sp_knot_show(node->knot);
1448     }
1450     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1451         if (fire_move_signals)
1452             sp_knot_set_position(node->knot, &node->pos, 0);
1453         else 
1454             sp_knot_moveto(node->knot, &node->pos);
1455     }
1457     gboolean show_handles = node->selected;
1458     if (node->p.other != NULL) {
1459         if (node->p.other->selected) show_handles = TRUE;
1460     }
1461     if (node->n.other != NULL) {
1462         if (node->n.other->selected) show_handles = TRUE;
1463     }
1465     if (node->subpath->nodepath->show_handles == false)
1466         show_handles = FALSE;
1468     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1469     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1472 /**
1473  * Call sp_node_update_handles() for all nodes on subpath.
1474  */
1475 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1477     g_assert(subpath != NULL);
1479     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1480         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1481     }
1484 /**
1485  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1486  */
1487 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1489     g_assert(nodepath != NULL);
1491     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1492         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1493     }
1496 void
1497 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1499     if (nodepath == NULL) return;
1501     nodepath->show_handles = show;
1502     sp_nodepath_update_handles(nodepath);
1505 /**
1506  * Adds all selected nodes in nodepath to list.
1507  */
1508 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1510     StlConv<Node *>::list(l, selected);
1511 /// \todo this adds a copying, rework when the selection becomes a stl list
1514 /**
1515  * Align selected nodes on the specified axis.
1516  */
1517 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1519     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1520         return;
1521     }
1523     if ( !nodepath->selected->next ) { // only one node selected
1524         return;
1525     }
1526    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1527     NR::Point dest(pNode->pos);
1528     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1529         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1530         if (pNode) {
1531             dest[axis] = pNode->pos[axis];
1532             sp_node_moveto(pNode, dest);
1533         }
1534     }
1536     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1539 /// Helper struct.
1540 struct NodeSort
1542    Inkscape::NodePath::Node *_node;
1543     NR::Coord _coord;
1544     /// \todo use vectorof pointers instead of calling copy ctor
1545     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1546         _node(node), _coord(node->pos[axis])
1547     {}
1549 };
1551 static bool operator<(NodeSort const &a, NodeSort const &b)
1553     return (a._coord < b._coord);
1556 /**
1557  * Distribute selected nodes on the specified axis.
1558  */
1559 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1561     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1562         return;
1563     }
1565     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1566         return;
1567     }
1569    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1570     std::vector<NodeSort> sorted;
1571     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1572         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1573         if (pNode) {
1574             NodeSort n(pNode, axis);
1575             sorted.push_back(n);
1576             //dest[axis] = pNode->pos[axis];
1577             //sp_node_moveto(pNode, dest);
1578         }
1579     }
1580     std::sort(sorted.begin(), sorted.end());
1581     unsigned int len = sorted.size();
1582     //overall bboxes span
1583     float dist = (sorted.back()._coord -
1584                   sorted.front()._coord);
1585     //new distance between each bbox
1586     float step = (dist) / (len - 1);
1587     float pos = sorted.front()._coord;
1588     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1589           it < sorted.end();
1590           it ++ )
1591     {
1592         NR::Point dest((*it)._node->pos);
1593         dest[axis] = pos;
1594         sp_node_moveto((*it)._node, dest);
1595         pos += step;
1596     }
1598     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1602 /**
1603  * Call sp_nodepath_line_add_node() for all selected segments.
1604  */
1605 void
1606 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1608     if (!nodepath) {
1609         return;
1610     }
1612     GList *nl = NULL;
1614     int n_added = 0;
1616     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1617        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1618         g_assert(t->selected);
1619         if (t->p.other && t->p.other->selected) {
1620             nl = g_list_prepend(nl, t);
1621         }
1622     }
1624     while (nl) {
1625        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1626        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1627        sp_nodepath_node_select(n, TRUE, FALSE);
1628        n_added ++;
1629        nl = g_list_remove(nl, t);
1630     }
1632     /** \todo fixme: adjust ? */
1633     sp_nodepath_update_handles(nodepath);
1635     if (n_added > 1) {
1636         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1637     } else if (n_added > 0) {
1638         sp_nodepath_update_repr(nodepath, _("Add node"));
1639     }
1641     sp_nodepath_update_statusbar(nodepath);
1644 /**
1645  * Select segment nearest to point
1646  */
1647 void
1648 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1650     if (!nodepath) {
1651         return;
1652     }
1654     sp_nodepath_ensure_livarot_path(nodepath);
1655     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1656     if (!maybe_position) {
1657         return;
1658     }
1659     Path::cut_position position = *maybe_position;
1661     //find segment to segment
1662     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1664     //fixme: this can return NULL, so check before proceeding.
1665     g_return_if_fail(e != NULL);
1666     
1667     gboolean force = FALSE;
1668     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1669         force = TRUE;
1670     }
1671     sp_nodepath_node_select(e, (gboolean) toggle, force);
1672     if (e->p.other)
1673         sp_nodepath_node_select(e->p.other, TRUE, force);
1675     sp_nodepath_update_handles(nodepath);
1677     sp_nodepath_update_statusbar(nodepath);
1680 /**
1681  * Add a node nearest to point
1682  */
1683 void
1684 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1686     if (!nodepath) {
1687         return;
1688     }
1690     sp_nodepath_ensure_livarot_path(nodepath);
1691     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1692     if (!maybe_position) {
1693         return;
1694     }
1695     Path::cut_position position = *maybe_position;
1697     //find segment to split
1698     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1700     //don't know why but t seems to flip for lines
1701     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1702         position.t = 1.0 - position.t;
1703     }
1704     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1705     sp_nodepath_node_select(n, FALSE, TRUE);
1707     /* fixme: adjust ? */
1708     sp_nodepath_update_handles(nodepath);
1710     sp_nodepath_update_repr(nodepath, _("Add node"));
1712     sp_nodepath_update_statusbar(nodepath);
1715 /*
1716  * Adjusts a segment so that t moves by a certain delta for dragging
1717  * converts lines to curves
1718  *
1719  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1720  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1721  */
1722 void
1723 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1725     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1727     //fixme: e and e->p can be NULL, so check for those before proceeding
1728     g_return_if_fail(e != NULL);
1729     g_return_if_fail(&e->p != NULL);
1730     
1731     /* feel good is an arbitrary parameter that distributes the delta between handles
1732      * if t of the drag point is less than 1/6 distance form the endpoint only
1733      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1734      */
1735     double feel_good;
1736     if (t <= 1.0 / 6.0)
1737         feel_good = 0;
1738     else if (t <= 0.5)
1739         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1740     else if (t <= 5.0 / 6.0)
1741         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1742     else
1743         feel_good = 1;
1745     //if we're dragging a line convert it to a curve
1746     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1747         sp_nodepath_set_line_type(e, NR_CURVETO);
1748     }
1750     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1751     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1752     e->p.other->n.pos += offsetcoord0;
1753     e->p.pos += offsetcoord1;
1755     // adjust handles of adjacent nodes where necessary
1756     sp_node_adjust_handle(e,1);
1757     sp_node_adjust_handle(e->p.other,-1);
1759     sp_nodepath_update_handles(e->subpath->nodepath);
1761     update_object(e->subpath->nodepath);
1763     sp_nodepath_update_statusbar(e->subpath->nodepath);
1767 /**
1768  * Call sp_nodepath_break() for all selected segments.
1769  */
1770 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1772     if (!nodepath) return;
1774     GList *temp = NULL;
1775     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1776        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1777        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1778         if (nn == NULL) continue; // no break, no new node
1779         temp = g_list_prepend(temp, nn);
1780     }
1782     if (temp) {
1783         sp_nodepath_deselect(nodepath);
1784     }
1785     for (GList *l = temp; l != NULL; l = l->next) {
1786         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1787     }
1789     sp_nodepath_update_handles(nodepath);
1791     sp_nodepath_update_repr(nodepath, _("Break path"));
1794 /**
1795  * Duplicate the selected node(s).
1796  */
1797 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1799     if (!nodepath) {
1800         return;
1801     }
1803     GList *temp = NULL;
1804     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1805        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1806        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1807         if (nn == NULL) continue; // could not duplicate
1808         temp = g_list_prepend(temp, nn);
1809     }
1811     if (temp) {
1812         sp_nodepath_deselect(nodepath);
1813     }
1814     for (GList *l = temp; l != NULL; l = l->next) {
1815         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1816     }
1818     sp_nodepath_update_handles(nodepath);
1820     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1823 /**
1824  *  Join two nodes by merging them into one.
1825  */
1826 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
1828     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1830     if (g_list_length(nodepath->selected) != 2) {
1831         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1832         return;
1833     }
1835    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1836    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1838     g_assert(a != b);
1839     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1840         // someone tried to join an orphan node (i.e. a single-node subpath).
1841         // this is not worth an error message, just fail silently.
1842         return;
1843     }
1845     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1846         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1847         return;
1848     }
1850     /* a and b are endpoints */
1852     NR::Point c;
1853     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1854         c = a->pos;
1855     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1856         c = b->pos;
1857     } else {
1858         c = (a->pos + b->pos) / 2;
1859     }
1861     if (a->subpath == b->subpath) {
1862        Inkscape::NodePath::SubPath *sp = a->subpath;
1863         sp_nodepath_subpath_close(sp);
1864         sp_node_moveto (sp->first, c);
1866         sp_nodepath_update_handles(sp->nodepath);
1867         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1868         return;
1869     }
1871     /* a and b are separate subpaths */
1872    Inkscape::NodePath::SubPath *sa = a->subpath;
1873    Inkscape::NodePath::SubPath *sb = b->subpath;
1874     NR::Point p;
1875    Inkscape::NodePath::Node *n;
1876     NRPathcode code;
1877     if (a == sa->first) {
1878         p = sa->first->n.pos;
1879         code = (NRPathcode)sa->first->n.other->code;
1880        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1881         n = sa->last;
1882         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1883         n = n->p.other;
1884         while (n) {
1885             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1886             n = n->p.other;
1887             if (n == sa->first) n = NULL;
1888         }
1889         sp_nodepath_subpath_destroy(sa);
1890         sa = t;
1891     } else if (a == sa->last) {
1892         p = sa->last->p.pos;
1893         code = (NRPathcode)sa->last->code;
1894         sp_nodepath_node_destroy(sa->last);
1895     } else {
1896         code = NR_END;
1897         g_assert_not_reached();
1898     }
1900     if (b == sb->first) {
1901         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1902         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1903             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1904         }
1905     } else if (b == sb->last) {
1906         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1907         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1908             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1909         }
1910     } else {
1911         g_assert_not_reached();
1912     }
1913     /* and now destroy sb */
1915     sp_nodepath_subpath_destroy(sb);
1917     sp_nodepath_update_handles(sa->nodepath);
1919     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1921     sp_nodepath_update_statusbar(nodepath);
1924 /**
1925  *  Join two nodes by adding a segment between them.
1926  */
1927 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
1929     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1931     if (g_list_length(nodepath->selected) != 2) {
1932         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1933         return;
1934     }
1936    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1937    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1939     g_assert(a != b);
1940     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1941         // someone tried to join an orphan node (i.e. a single-node subpath).
1942         // this is not worth an error message, just fail silently.
1943         return;
1944     }
1946     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1947         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1948         return;
1949     }
1951     if (a->subpath == b->subpath) {
1952        Inkscape::NodePath::SubPath *sp = a->subpath;
1954         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1955         sp->closed = TRUE;
1957         sp->first->p.other = sp->last;
1958         sp->last->n.other  = sp->first;
1960         sp_node_handle_mirror_p_to_n(sp->last);
1961         sp_node_handle_mirror_n_to_p(sp->first);
1963         sp->first->code = sp->last->code;
1964         sp->first       = sp->last;
1966         sp_nodepath_update_handles(sp->nodepath);
1968         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1970         return;
1971     }
1973     /* a and b are separate subpaths */
1974    Inkscape::NodePath::SubPath *sa = a->subpath;
1975    Inkscape::NodePath::SubPath *sb = b->subpath;
1977    Inkscape::NodePath::Node *n;
1978     NR::Point p;
1979     NRPathcode code;
1980     if (a == sa->first) {
1981         code = (NRPathcode) sa->first->n.other->code;
1982        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1983         n = sa->last;
1984         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1985         for (n = n->p.other; n != NULL; n = n->p.other) {
1986             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1987         }
1988         sp_nodepath_subpath_destroy(sa);
1989         sa = t;
1990     } else if (a == sa->last) {
1991         code = (NRPathcode)sa->last->code;
1992     } else {
1993         code = NR_END;
1994         g_assert_not_reached();
1995     }
1997     if (b == sb->first) {
1998         n = sb->first;
1999         sp_node_handle_mirror_p_to_n(sa->last);
2000         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2001         sp_node_handle_mirror_n_to_p(sa->last);
2002         for (n = n->n.other; n != NULL; n = n->n.other) {
2003             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2004         }
2005     } else if (b == sb->last) {
2006         n = sb->last;
2007         sp_node_handle_mirror_p_to_n(sa->last);
2008         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2009         sp_node_handle_mirror_n_to_p(sa->last);
2010         for (n = n->p.other; n != NULL; n = n->p.other) {
2011             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2012         }
2013     } else {
2014         g_assert_not_reached();
2015     }
2016     /* and now destroy sb */
2018     sp_nodepath_subpath_destroy(sb);
2020     sp_nodepath_update_handles(sa->nodepath);
2022     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2025 /**
2026  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2027  */
2028 void sp_node_delete_preserve(GList *nodes_to_delete)
2030     GSList *nodepaths = NULL;
2031     
2032     while (nodes_to_delete) {
2033         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2034         Inkscape::NodePath::SubPath *sp = node->subpath;
2035         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2036         Inkscape::NodePath::Node *sample_cursor = NULL;
2037         Inkscape::NodePath::Node *sample_end = NULL;
2038         Inkscape::NodePath::Node *delete_cursor = node;
2039         bool just_delete = false;
2040         
2041         //find the start of this contiguous selection
2042         //move left to the first node that is not selected
2043         //or the start of the non-closed path
2044         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2045             delete_cursor = curr;
2046         }
2048         //just delete at the beginning of an open path
2049         if (!delete_cursor->p.other) {
2050             sample_cursor = delete_cursor;
2051             just_delete = true;
2052         } else {
2053             sample_cursor = delete_cursor->p.other;
2054         }
2055         
2056         //calculate points for each segment
2057         int rate = 5;
2058         float period = 1.0 / rate;
2059         std::vector<NR::Point> data;
2060         if (!just_delete) {
2061             data.push_back(sample_cursor->pos);
2062             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2063                 //just delete at the end of an open path
2064                 if (!sp->closed && curr == sp->last) {
2065                     just_delete = true;
2066                     break;
2067                 }
2068                 
2069                 //sample points on the contiguous selected segment
2070                 NR::Point *bez;
2071                 bez = new NR::Point [4];
2072                 bez[0] = curr->pos;
2073                 bez[1] = curr->n.pos;
2074                 bez[2] = curr->n.other->p.pos;
2075                 bez[3] = curr->n.other->pos;
2076                 for (int i=1; i<rate; i++) {
2077                     gdouble t = i * period;
2078                     NR::Point p = bezier_pt(3, bez, t);
2079                     data.push_back(p);
2080                 }
2081                 data.push_back(curr->n.other->pos);
2083                 sample_end = curr->n.other;
2084                 //break if we've come full circle or hit the end of the selection
2085                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2086                     break;
2087                 }
2088             }
2089         }
2091         if (!just_delete) {
2092             //calculate the best fitting single segment and adjust the endpoints
2093             NR::Point *adata;
2094             adata = new NR::Point [data.size()];
2095             copy(data.begin(), data.end(), adata);
2096             
2097             NR::Point *bez;
2098             bez = new NR::Point [4];
2099             //would decreasing error create a better fitting approximation?
2100             gdouble error = 1.0;
2101             gint ret;
2102             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2104             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2105             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2106             //the resulting nodes behave as expected.
2107             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2108             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2109             
2110             //adjust endpoints
2111             sample_cursor->n.pos = bez[1];
2112             sample_end->p.pos = bez[2];
2113         }
2114        
2115         //destroy this contiguous selection
2116         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2117             Inkscape::NodePath::Node *temp = delete_cursor;
2118             if (delete_cursor->n.other == delete_cursor) {
2119                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2120                 delete_cursor = NULL; 
2121             } else {
2122                 delete_cursor = delete_cursor->n.other;
2123             }
2124             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2125             sp_nodepath_node_destroy(temp);
2126         }
2128         sp_nodepath_update_handles(nodepath);
2130         if (!g_slist_find(nodepaths, nodepath))
2131             nodepaths = g_slist_prepend (nodepaths, nodepath);
2132     }
2134     for (GSList *i = nodepaths; i; i = i->next) {
2135         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2136         // different nodepaths will give us one undo event per nodepath
2137         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2139         // if the entire nodepath is removed, delete the selected object.
2140         if (nodepath->subpaths == NULL ||
2141             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2142             //at least 2
2143             sp_nodepath_get_node_count(nodepath) < 2) {
2144             SPDocument *document = sp_desktop_document (nodepath->desktop);
2145             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2146             //delete this nodepath's object, not the entire selection! (though at this time, this
2147             //does not matter)
2148             sp_selection_delete();
2149             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2150                               _("Delete nodes"));
2151         } else {
2152             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2153             sp_nodepath_update_statusbar(nodepath);
2154         }
2155     }
2157     g_slist_free (nodepaths);
2160 /**
2161  * Delete one or more selected nodes.
2162  */
2163 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2165     if (!nodepath) return;
2166     if (!nodepath->selected) return;
2168     /** \todo fixme: do it the right way */
2169     while (nodepath->selected) {
2170        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2171         sp_nodepath_node_destroy(node);
2172     }
2175     //clean up the nodepath (such as for trivial subpaths)
2176     sp_nodepath_cleanup(nodepath);
2178     sp_nodepath_update_handles(nodepath);
2180     // if the entire nodepath is removed, delete the selected object.
2181     if (nodepath->subpaths == NULL ||
2182         sp_nodepath_get_node_count(nodepath) < 2) {
2183         SPDocument *document = sp_desktop_document (nodepath->desktop);
2184         sp_selection_delete();
2185         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2186                           _("Delete nodes"));
2187         return;
2188     }
2190     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2192     sp_nodepath_update_statusbar(nodepath);
2195 /**
2196  * Delete one or more segments between two selected nodes.
2197  * This is the code for 'split'.
2198  */
2199 void
2200 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2202    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2203    Inkscape::NodePath::Node *curr, *next;     //Iterators
2205     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2207     if (g_list_length(nodepath->selected) != 2) {
2208         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2209                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2210         return;
2211     }
2213     //Selected nodes, not inclusive
2214    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2215    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2217     if ( ( a==b)                       ||  //same node
2218          (a->subpath  != b->subpath )  ||  //not the same path
2219          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2220          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2221     {
2222         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2223                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2224         return;
2225     }
2227     //###########################################
2228     //# BEGIN EDITS
2229     //###########################################
2230     //##################################
2231     //# CLOSED PATH
2232     //##################################
2233     if (a->subpath->closed) {
2236         gboolean reversed = FALSE;
2238         //Since we can go in a circle, we need to find the shorter distance.
2239         //  a->b or b->a
2240         start = end = NULL;
2241         int distance    = 0;
2242         int minDistance = 0;
2243         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2244             if (curr==b) {
2245                 //printf("a to b:%d\n", distance);
2246                 start = a;//go from a to b
2247                 end   = b;
2248                 minDistance = distance;
2249                 //printf("A to B :\n");
2250                 break;
2251             }
2252             distance++;
2253         }
2255         //try again, the other direction
2256         distance = 0;
2257         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2258             if (curr==a) {
2259                 //printf("b to a:%d\n", distance);
2260                 if (distance < minDistance) {
2261                     start    = b;  //we go from b to a
2262                     end      = a;
2263                     reversed = TRUE;
2264                     //printf("B to A\n");
2265                 }
2266                 break;
2267             }
2268             distance++;
2269         }
2272         //Copy everything from 'end' to 'start' to a new subpath
2273        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2274         for (curr=end ; curr ; curr=curr->n.other) {
2275             NRPathcode code = (NRPathcode) curr->code;
2276             if (curr == end)
2277                 code = NR_MOVETO;
2278             sp_nodepath_node_new(t, NULL,
2279                                  (Inkscape::NodePath::NodeType)curr->type, code,
2280                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2281             if (curr == start)
2282                 break;
2283         }
2284         sp_nodepath_subpath_destroy(a->subpath);
2287     }
2291     //##################################
2292     //# OPEN PATH
2293     //##################################
2294     else {
2296         //We need to get the direction of the list between A and B
2297         //Can we walk from a to b?
2298         start = end = NULL;
2299         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2300             if (curr==b) {
2301                 start = a;  //did it!  we go from a to b
2302                 end   = b;
2303                 //printf("A to B\n");
2304                 break;
2305             }
2306         }
2307         if (!start) {//didn't work?  let's try the other direction
2308             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2309                 if (curr==a) {
2310                     start = b;  //did it!  we go from b to a
2311                     end   = a;
2312                     //printf("B to A\n");
2313                     break;
2314                 }
2315             }
2316         }
2317         if (!start) {
2318             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2319                                                      _("Cannot find path between nodes."));
2320             return;
2321         }
2325         //Copy everything after 'end' to a new subpath
2326        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2327         for (curr=end ; curr ; curr=curr->n.other) {
2328             NRPathcode code = (NRPathcode) curr->code;
2329             if (curr == end)
2330                 code = NR_MOVETO;
2331             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2332                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2333         }
2335         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2336         for (curr = start->n.other ; curr  ; curr=next) {
2337             next = curr->n.other;
2338             sp_nodepath_node_destroy(curr);
2339         }
2341     }
2342     //###########################################
2343     //# END EDITS
2344     //###########################################
2346     //clean up the nodepath (such as for trivial subpaths)
2347     sp_nodepath_cleanup(nodepath);
2349     sp_nodepath_update_handles(nodepath);
2351     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2353     sp_nodepath_update_statusbar(nodepath);
2356 /**
2357  * Call sp_nodepath_set_line() for all selected segments.
2358  */
2359 void
2360 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2362     if (nodepath == NULL) return;
2364     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2365        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2366         g_assert(n->selected);
2367         if (n->p.other && n->p.other->selected) {
2368             sp_nodepath_set_line_type(n, code);
2369         }
2370     }
2372     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2375 /**
2376  * Call sp_nodepath_convert_node_type() for all selected nodes.
2377  */
2378 void
2379 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2381     if (nodepath == NULL) return;
2383     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2385     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2386         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2387     }
2389     sp_nodepath_update_repr(nodepath, _("Change node type"));
2392 /**
2393  * Change select status of node, update its own and neighbour handles.
2394  */
2395 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2397     node->selected = selected;
2399     if (selected) {
2400         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2401         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2402         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2403         sp_knot_update_ctrl(node->knot);
2404     } else {
2405         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2406         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2407         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2408         sp_knot_update_ctrl(node->knot);
2409     }
2411     sp_node_update_handles(node);
2412     if (node->n.other) sp_node_update_handles(node->n.other);
2413     if (node->p.other) sp_node_update_handles(node->p.other);
2416 /**
2417 \brief Select a node
2418 \param node     The node to select
2419 \param incremental   If true, add to selection, otherwise deselect others
2420 \param override   If true, always select this node, otherwise toggle selected status
2421 */
2422 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2424     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2426     if (incremental) {
2427         if (override) {
2428             if (!g_list_find(nodepath->selected, node)) {
2429                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2430             }
2431             sp_node_set_selected(node, TRUE);
2432         } else { // toggle
2433             if (node->selected) {
2434                 g_assert(g_list_find(nodepath->selected, node));
2435                 nodepath->selected = g_list_remove(nodepath->selected, node);
2436             } else {
2437                 g_assert(!g_list_find(nodepath->selected, node));
2438                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2439             }
2440             sp_node_set_selected(node, !node->selected);
2441         }
2442     } else {
2443         sp_nodepath_deselect(nodepath);
2444         nodepath->selected = g_list_prepend(nodepath->selected, node);
2445         sp_node_set_selected(node, TRUE);
2446     }
2448     sp_nodepath_update_statusbar(nodepath);
2452 /**
2453 \brief Deselect all nodes in the nodepath
2454 */
2455 void
2456 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2458     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2460     while (nodepath->selected) {
2461         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2462         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2463     }
2464     sp_nodepath_update_statusbar(nodepath);
2467 /**
2468 \brief Select or invert selection of all nodes in the nodepath
2469 */
2470 void
2471 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2473     if (!nodepath) return;
2475     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2476        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2477         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2478            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2479            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2480         }
2481     }
2484 /**
2485  * If nothing selected, does the same as sp_nodepath_select_all();
2486  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2487  * (i.e., similar to "select all in layer", with the "selected" subpaths
2488  * being treated as "layers" in the path).
2489  */
2490 void
2491 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2493     if (!nodepath) return;
2495     if (g_list_length (nodepath->selected) == 0) {
2496         sp_nodepath_select_all (nodepath, invert);
2497         return;
2498     }
2500     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2501     GSList *subpaths = NULL;
2503     for (GList *l = copy; l != NULL; l = l->next) {
2504         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2505         Inkscape::NodePath::SubPath *subpath = n->subpath;
2506         if (!g_slist_find (subpaths, subpath))
2507             subpaths = g_slist_prepend (subpaths, subpath);
2508     }
2510     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2511         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2512         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2513             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2514             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2515         }
2516     }
2518     g_slist_free (subpaths);
2519     g_list_free (copy);
2522 /**
2523  * \brief Select the node after the last selected; if none is selected,
2524  * select the first within path.
2525  */
2526 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2528     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2530    Inkscape::NodePath::Node *last = NULL;
2531     if (nodepath->selected) {
2532         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2533            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2534             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2535             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2536                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2537                 if (node->selected) {
2538                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2539                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2540                             if (spl->next) { // there's a next subpath
2541                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2542                                 last = subpath_next->first;
2543                             } else if (spl->prev) { // there's a previous subpath
2544                                 last = NULL; // to be set later to the first node of first subpath
2545                             } else {
2546                                 last = node->n.other;
2547                             }
2548                         } else {
2549                             last = node->n.other;
2550                         }
2551                     } else {
2552                         if (node->n.other) {
2553                             last = node->n.other;
2554                         } else {
2555                             if (spl->next) { // there's a next subpath
2556                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2557                                 last = subpath_next->first;
2558                             } else if (spl->prev) { // there's a previous subpath
2559                                 last = NULL; // to be set later to the first node of first subpath
2560                             } else {
2561                                 last = (Inkscape::NodePath::Node *) subpath->first;
2562                             }
2563                         }
2564                     }
2565                 }
2566             }
2567         }
2568         sp_nodepath_deselect(nodepath);
2569     }
2571     if (last) { // there's at least one more node after selected
2572         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2573     } else { // no more nodes, select the first one in first subpath
2574        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2575         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2576     }
2579 /**
2580  * \brief Select the node before the first selected; if none is selected,
2581  * select the last within path
2582  */
2583 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2585     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2587    Inkscape::NodePath::Node *last = NULL;
2588     if (nodepath->selected) {
2589         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2590            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2591             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2592                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2593                 if (node->selected) {
2594                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2595                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2596                             if (spl->prev) { // there's a prev subpath
2597                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2598                                 last = subpath_prev->last;
2599                             } else if (spl->next) { // there's a next subpath
2600                                 last = NULL; // to be set later to the last node of last subpath
2601                             } else {
2602                                 last = node->p.other;
2603                             }
2604                         } else {
2605                             last = node->p.other;
2606                         }
2607                     } else {
2608                         if (node->p.other) {
2609                             last = node->p.other;
2610                         } else {
2611                             if (spl->prev) { // there's a prev subpath
2612                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2613                                 last = subpath_prev->last;
2614                             } else if (spl->next) { // there's a next subpath
2615                                 last = NULL; // to be set later to the last node of last subpath
2616                             } else {
2617                                 last = (Inkscape::NodePath::Node *) subpath->last;
2618                             }
2619                         }
2620                     }
2621                 }
2622             }
2623         }
2624         sp_nodepath_deselect(nodepath);
2625     }
2627     if (last) { // there's at least one more node before selected
2628         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2629     } else { // no more nodes, select the last one in last subpath
2630         GList *spl = g_list_last(nodepath->subpaths);
2631        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2632         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2633     }
2636 /**
2637  * \brief Select all nodes that are within the rectangle.
2638  */
2639 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2641     if (!incremental) {
2642         sp_nodepath_deselect(nodepath);
2643     }
2645     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2646        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2647         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2648            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2650             if (b.contains(node->pos)) {
2651                 sp_nodepath_node_select(node, TRUE, TRUE);
2652             }
2653         }
2654     }
2658 void
2659 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2661     g_assert (n);
2662     g_assert (nodepath);
2663     g_assert (n->subpath->nodepath == nodepath);
2665     if (g_list_length (nodepath->selected) == 0) {
2666         if (grow > 0) {
2667             sp_nodepath_node_select(n, TRUE, TRUE);
2668         }
2669         return;
2670     }
2672     if (g_list_length (nodepath->selected) == 1) {
2673         if (grow < 0) {
2674             sp_nodepath_deselect (nodepath);
2675             return;
2676         }
2677     }
2679         double n_sel_range = 0, p_sel_range = 0;
2680             Inkscape::NodePath::Node *farthest_n_node = n;
2681             Inkscape::NodePath::Node *farthest_p_node = n;
2683         // Calculate ranges
2684         {
2685             double n_range = 0, p_range = 0;
2686             bool n_going = true, p_going = true;
2687             Inkscape::NodePath::Node *n_node = n;
2688             Inkscape::NodePath::Node *p_node = n;
2689             do {
2690                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2691                 if (n_node && n_going)
2692                     n_node = n_node->n.other;
2693                 if (n_node == NULL) {
2694                     n_going = false;
2695                 } else {
2696                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2697                     if (n_node->selected) {
2698                         n_sel_range = n_range;
2699                         farthest_n_node = n_node;
2700                     }
2701                     if (n_node == p_node) {
2702                         n_going = false;
2703                         p_going = false;
2704                     }
2705                 }
2706                 if (p_node && p_going)
2707                     p_node = p_node->p.other;
2708                 if (p_node == NULL) {
2709                     p_going = false;
2710                 } else {
2711                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2712                     if (p_node->selected) {
2713                         p_sel_range = p_range;
2714                         farthest_p_node = p_node;
2715                     }
2716                     if (p_node == n_node) {
2717                         n_going = false;
2718                         p_going = false;
2719                     }
2720                 }
2721             } while (n_going || p_going);
2722         }
2724     if (grow > 0) {
2725         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2726                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2727         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2728                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2729         }
2730     } else {
2731         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2732                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2733         } else if (farthest_p_node && farthest_p_node->selected) {
2734                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2735         }
2736     }
2739 void
2740 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2742     g_assert (n);
2743     g_assert (nodepath);
2744     g_assert (n->subpath->nodepath == nodepath);
2746     if (g_list_length (nodepath->selected) == 0) {
2747         if (grow > 0) {
2748             sp_nodepath_node_select(n, TRUE, TRUE);
2749         }
2750         return;
2751     }
2753     if (g_list_length (nodepath->selected) == 1) {
2754         if (grow < 0) {
2755             sp_nodepath_deselect (nodepath);
2756             return;
2757         }
2758     }
2760     Inkscape::NodePath::Node *farthest_selected = NULL;
2761     double farthest_dist = 0;
2763     Inkscape::NodePath::Node *closest_unselected = NULL;
2764     double closest_dist = NR_HUGE;
2766     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2767        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2768         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2769            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2770            if (node == n)
2771                continue;
2772            if (node->selected) {
2773                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2774                    farthest_dist = NR::L2(node->pos - n->pos);
2775                    farthest_selected = node;
2776                }
2777            } else {
2778                if (NR::L2(node->pos - n->pos) < closest_dist) {
2779                    closest_dist = NR::L2(node->pos - n->pos);
2780                    closest_unselected = node;
2781                }
2782            }
2783         }
2784     }
2786     if (grow > 0) {
2787         if (closest_unselected) {
2788             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2789         }
2790     } else {
2791         if (farthest_selected) {
2792             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2793         }
2794     }
2798 /**
2799 \brief  Saves all nodes' and handles' current positions in their origin members
2800 */
2801 void
2802 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2804     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2805        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2806         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2807            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2808            n->origin = n->pos;
2809            n->p.origin = n->p.pos;
2810            n->n.origin = n->n.pos;
2811         }
2812     }
2813
2815 /**
2816 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2817 */
2818 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2820     if (!nodepath->selected) {
2821         return NULL;
2822     }
2824     GList *r = NULL;
2825     guint i = 0;
2826     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2827        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2828         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2829            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2830             i++;
2831             if (node->selected) {
2832                 r = g_list_append(r, GINT_TO_POINTER(i));
2833             }
2834         }
2835     }
2836     return r;
2839 /**
2840 \brief  Restores selection by selecting nodes whose positions are in the list
2841 */
2842 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2844     sp_nodepath_deselect(nodepath);
2846     guint i = 0;
2847     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2848        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2849         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2850            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2851             i++;
2852             if (g_list_find(r, GINT_TO_POINTER(i))) {
2853                 sp_nodepath_node_select(node, TRUE, TRUE);
2854             }
2855         }
2856     }
2860 /**
2861 \brief Adjusts handle according to node type and line code.
2862 */
2863 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2865     double len, otherlen, linelen;
2867     g_assert(node);
2869    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2870    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2872     /** \todo fixme: */
2873     if (me->other == NULL) return;
2874     if (other->other == NULL) return;
2876     /* I have line */
2878     NRPathcode mecode, ocode;
2879     if (which_adjust == 1) {
2880         mecode = (NRPathcode)me->other->code;
2881         ocode = (NRPathcode)node->code;
2882     } else {
2883         mecode = (NRPathcode)node->code;
2884         ocode = (NRPathcode)other->other->code;
2885     }
2887     if (mecode == NR_LINETO) return;
2889     /* I am curve */
2891     if (other->other == NULL) return;
2893     /* Other has line */
2895     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2897     NR::Point delta;
2898     if (ocode == NR_LINETO) {
2899         /* other is lineto, we are either smooth or symm */
2900        Inkscape::NodePath::Node *othernode = other->other;
2901         len = NR::L2(me->pos - node->pos);
2902         delta = node->pos - othernode->pos;
2903         linelen = NR::L2(delta);
2904         if (linelen < 1e-18) 
2905             return;
2906         me->pos = node->pos + (len / linelen)*delta;
2907         return;
2908     }
2910     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2912         me->pos = 2 * node->pos - other->pos;
2913         return;
2914     }
2916     /* We are smooth */
2918     len = NR::L2(me->pos - node->pos);
2919     delta = other->pos - node->pos;
2920     otherlen = NR::L2(delta);
2921     if (otherlen < 1e-18) return;
2923     me->pos = node->pos - (len / otherlen) * delta;
2926 /**
2927  \brief Adjusts both handles according to node type and line code
2928  */
2929 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2931     g_assert(node);
2933     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2935     /* we are either smooth or symm */
2937     if (node->p.other == NULL) return;
2939     if (node->n.other == NULL) return;
2941     if (node->code == NR_LINETO) {
2942         if (node->n.other->code == NR_LINETO) return;
2943         sp_node_adjust_handle(node, 1);
2944         return;
2945     }
2947     if (node->n.other->code == NR_LINETO) {
2948         if (node->code == NR_LINETO) return;
2949         sp_node_adjust_handle(node, -1);
2950         return;
2951     }
2953     /* both are curves */
2954     NR::Point const delta( node->n.pos - node->p.pos );
2956     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2957         node->p.pos = node->pos - delta / 2;
2958         node->n.pos = node->pos + delta / 2;
2959         return;
2960     }
2962     /* We are smooth */
2963     double plen = NR::L2(node->p.pos - node->pos);
2964     if (plen < 1e-18) return;
2965     double nlen = NR::L2(node->n.pos - node->pos);
2966     if (nlen < 1e-18) return;
2967     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2968     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2971 /**
2972  * Node event callback.
2973  */
2974 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2976     gboolean ret = FALSE;
2977     switch (event->type) {
2978         case GDK_ENTER_NOTIFY:
2979             Inkscape::NodePath::Path::active_node = n;
2980             break;
2981         case GDK_LEAVE_NOTIFY:
2982             Inkscape::NodePath::Path::active_node = NULL;
2983             break;
2984         case GDK_SCROLL:
2985             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
2986                 switch (event->scroll.direction) {
2987                     case GDK_SCROLL_UP:
2988                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2989                         break;
2990                     case GDK_SCROLL_DOWN:
2991                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2992                         break;
2993                     default:
2994                         break;
2995                 }
2996                 ret = TRUE;
2997             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
2998                 switch (event->scroll.direction) {
2999                     case GDK_SCROLL_UP:
3000                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3001                         break;
3002                     case GDK_SCROLL_DOWN:
3003                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3004                         break;
3005                     default:
3006                         break;
3007                 }
3008                 ret = TRUE;
3009             }
3010             break;
3011         case GDK_KEY_PRESS:
3012             switch (get_group0_keyval (&event->key)) {
3013                 case GDK_space:
3014                     if (event->key.state & GDK_BUTTON1_MASK) {
3015                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3016                         stamp_repr(nodepath);
3017                         ret = TRUE;
3018                     }
3019                     break;
3020                 case GDK_Page_Up:
3021                     if (event->key.state & GDK_CONTROL_MASK) {
3022                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3023                     } else {
3024                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3025                     }
3026                     break;
3027                 case GDK_Page_Down:
3028                     if (event->key.state & GDK_CONTROL_MASK) {
3029                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3030                     } else {
3031                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3032                     }
3033                     break;
3034                 default:
3035                     break;
3036             }
3037             break;
3038         default:
3039             break;
3040     }
3042     return ret;
3045 /**
3046  * Handle keypress on node; directly called.
3047  */
3048 gboolean node_key(GdkEvent *event)
3050     Inkscape::NodePath::Path *np;
3052     // there is no way to verify nodes so set active_node to nil when deleting!!
3053     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3055     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3056         gint ret = FALSE;
3057         switch (get_group0_keyval (&event->key)) {
3058             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3059             case GDK_BackSpace:
3060                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3061                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3062                 sp_nodepath_update_repr(np, _("Delete node"));
3063                 Inkscape::NodePath::Path::active_node = NULL;
3064                 ret = TRUE;
3065                 break;
3066             case GDK_c:
3067                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3068                 ret = TRUE;
3069                 break;
3070             case GDK_s:
3071                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3072                 ret = TRUE;
3073                 break;
3074             case GDK_y:
3075                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3076                 ret = TRUE;
3077                 break;
3078             case GDK_b:
3079                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3080                 ret = TRUE;
3081                 break;
3082         }
3083         return ret;
3084     }
3085     return FALSE;
3088 /**
3089  * Mouseclick on node callback.
3090  */
3091 static void node_clicked(SPKnot *knot, guint state, gpointer data)
3093    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3095     if (state & GDK_CONTROL_MASK) {
3096         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3098         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3099             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3100                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3101             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3102                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3103             } else {
3104                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3105             }
3106             sp_nodepath_update_repr(nodepath, _("Change node type"));
3107             sp_nodepath_update_statusbar(nodepath);
3109         } else { //ctrl+alt+click: delete node
3110             GList *node_to_delete = NULL;
3111             node_to_delete = g_list_append(node_to_delete, n);
3112             sp_node_delete_preserve(node_to_delete);
3113         }
3115     } else {
3116         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3117     }
3120 /**
3121  * Mouse grabbed node callback.
3122  */
3123 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3125    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3127     if (!n->selected) {
3128         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3129     }
3131     n->is_dragging = true;
3132     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3134     sp_nodepath_remember_origins (n->subpath->nodepath);
3137 /**
3138  * Mouse ungrabbed node callback.
3139  */
3140 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
3142    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3144    n->dragging_out = NULL;
3145    n->is_dragging = false;
3146    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3148    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3151 /**
3152  * The point on a line, given by its angle, closest to the given point.
3153  * \param p  A point.
3154  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3155  * \param closest  Pointer to the point struct where the result is stored.
3156  * \todo FIXME: use dot product perhaps?
3157  */
3158 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3160     if (a == HUGE_VAL) { // vertical
3161         *closest = NR::Point(0, (*p)[NR::Y]);
3162     } else {
3163         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3164         (*closest)[NR::Y] = a * (*closest)[NR::X];
3165     }
3168 /**
3169  * Distance from the point to a line given by its angle.
3170  * \param p  A point.
3171  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3172  */
3173 static double point_line_distance(NR::Point *p, double a)
3175     NR::Point c;
3176     point_line_closest(p, a, &c);
3177     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]));
3180 /**
3181  * Callback for node "request" signal.
3182  * \todo fixme: This goes to "moved" event? (lauris)
3183  */
3184 static gboolean
3185 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3187     double yn, xn, yp, xp;
3188     double an, ap, na, pa;
3189     double d_an, d_ap, d_na, d_pa;
3190     gboolean collinear = FALSE;
3191     NR::Point c;
3192     NR::Point pr;
3194    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3196    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3197     if ( (!n->subpath->nodepath->straight_path) &&
3198          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3199            || n->dragging_out ) )
3200     {
3201        NR::Point mouse = (*p);
3203        if (!n->dragging_out) {
3204            // This is the first drag-out event; find out which handle to drag out
3205            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3206            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3208            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3209                return FALSE;
3211            Inkscape::NodePath::NodeSide *opposite;
3212            if (appr_p > appr_n) { // closer to p
3213                n->dragging_out = &n->p;
3214                opposite = &n->n;
3215                n->code = NR_CURVETO;
3216            } else if (appr_p < appr_n) { // closer to n
3217                n->dragging_out = &n->n;
3218                opposite = &n->p;
3219                n->n.other->code = NR_CURVETO;
3220            } else { // p and n nodes are the same
3221                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3222                    n->dragging_out = &n->p;
3223                    opposite = &n->n;
3224                    n->code = NR_CURVETO;
3225                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3226                    n->dragging_out = &n->n;
3227                    opposite = &n->p;
3228                    n->n.other->code = NR_CURVETO;
3229                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3230                    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);
3231                    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);
3232                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3233                        n->dragging_out = &n->n;
3234                        opposite = &n->p;
3235                        n->n.other->code = NR_CURVETO;
3236                    } else { // closer to other's n handle
3237                        n->dragging_out = &n->p;
3238                        opposite = &n->n;
3239                        n->code = NR_CURVETO;
3240                    }
3241                }
3242            }
3244            // if there's another handle, make sure the one we drag out starts parallel to it
3245            if (opposite->pos != n->pos) {
3246                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3247            }
3249            // knots might not be created yet!
3250            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3251            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3252        }
3254        // pass this on to the handle-moved callback
3255        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3256        sp_node_update_handles(n);
3257        return TRUE;
3258    }
3260     if (state & GDK_CONTROL_MASK) { // constrained motion
3262         // calculate relative distances of handles
3263         // n handle:
3264         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3265         xn = n->n.pos[NR::X] - n->pos[NR::X];
3266         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3267         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3268             if (n->n.other) { // if there is the next point
3269                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3270                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3271                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3272             }
3273         }
3274         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3275         if (yn < 0) { xn = -xn; yn = -yn; }
3277         // p handle:
3278         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3279         xp = n->p.pos[NR::X] - n->pos[NR::X];
3280         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3281         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3282             if (n->p.other) {
3283                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3284                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3285                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3286             }
3287         }
3288         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3289         if (yp < 0) { xp = -xp; yp = -yp; }
3291         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3292             // sliding on handles, only if at least one of the handles is non-vertical
3293             // (otherwise it's the same as ctrl+drag anyway)
3295             // calculate angles of the handles
3296             if (xn == 0) {
3297                 if (yn == 0) { // no handle, consider it the continuation of the other one
3298                     an = 0;
3299                     collinear = TRUE;
3300                 }
3301                 else an = 0; // vertical; set the angle to horizontal
3302             } else an = yn/xn;
3304             if (xp == 0) {
3305                 if (yp == 0) { // no handle, consider it the continuation of the other one
3306                     ap = an;
3307                 }
3308                 else ap = 0; // vertical; set the angle to horizontal
3309             } else  ap = yp/xp;
3311             if (collinear) an = ap;
3313             // angles of the perpendiculars; HUGE_VAL means vertical
3314             if (an == 0) na = HUGE_VAL; else na = -1/an;
3315             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3317             // mouse point relative to the node's original pos
3318             pr = (*p) - n->origin;
3320             // distances to the four lines (two handles and two perpendiculars)
3321             d_an = point_line_distance(&pr, an);
3322             d_na = point_line_distance(&pr, na);
3323             d_ap = point_line_distance(&pr, ap);
3324             d_pa = point_line_distance(&pr, pa);
3326             // find out which line is the closest, save its closest point in c
3327             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3328                 point_line_closest(&pr, an, &c);
3329             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3330                 point_line_closest(&pr, ap, &c);
3331             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3332                 point_line_closest(&pr, na, &c);
3333             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3334                 point_line_closest(&pr, pa, &c);
3335             }
3337             // move the node to the closest point
3338             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3339                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3340                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3342         } else {  // constraining to hor/vert
3344             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3345                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3346             } else { // snap to vert
3347                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3348             }
3349         }
3350     } else { // move freely
3351         if (n->is_dragging) {
3352             if (state & GDK_MOD1_MASK) { // sculpt
3353                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3354             } else {
3355                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3356                                             (*p)[NR::X] - n->pos[NR::X],
3357                                             (*p)[NR::Y] - n->pos[NR::Y],
3358                                             (state & GDK_SHIFT_MASK) == 0);
3359             }
3360         }
3361     }
3363     n->subpath->nodepath->desktop->scroll_to_point(p);
3365     return TRUE;
3368 /**
3369  * Node handle clicked callback.
3370  */
3371 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3373    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3375     if (state & GDK_CONTROL_MASK) { // "delete" handle
3376         if (n->p.knot == knot) {
3377             n->p.pos = n->pos;
3378         } else if (n->n.knot == knot) {
3379             n->n.pos = n->pos;
3380         }
3381         sp_node_update_handles(n);
3382         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3383         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3384         sp_nodepath_update_statusbar(nodepath);
3386     } else { // just select or add to selection, depending in Shift
3387         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3388     }
3391 /**
3392  * Node handle grabbed callback.
3393  */
3394 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3396    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3398     if (!n->selected) {
3399         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3400     }
3402     // remember the origin point of the handle
3403     if (n->p.knot == knot) {
3404         n->p.origin_radial = n->p.pos - n->pos;
3405     } else if (n->n.knot == knot) {
3406         n->n.origin_radial = n->n.pos - n->pos;
3407     } else {
3408         g_assert_not_reached();
3409     }
3411     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3414 /**
3415  * Node handle ungrabbed callback.
3416  */
3417 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3419    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3421     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3422     if (n->p.knot == knot) {
3423         n->p.origin_radial.a = 0;
3424         sp_knot_set_position(knot, &n->p.pos, state);
3425     } else if (n->n.knot == knot) {
3426         n->n.origin_radial.a = 0;
3427         sp_knot_set_position(knot, &n->n.pos, state);
3428     } else {
3429         g_assert_not_reached();
3430     }
3432     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3435 /**
3436  * Node handle "request" signal callback.
3437  */
3438 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3440     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3442     Inkscape::NodePath::NodeSide *me, *opposite;
3443     gint which;
3444     if (n->p.knot == knot) {
3445         me = &n->p;
3446         opposite = &n->n;
3447         which = -1;
3448     } else if (n->n.knot == knot) {
3449         me = &n->n;
3450         opposite = &n->p;
3451         which = 1;
3452     } else {
3453         me = opposite = NULL;
3454         which = 0;
3455         g_assert_not_reached();
3456     }
3458     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3460     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3462     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3463         /* We are smooth node adjacent with line */
3464         NR::Point const delta = *p - n->pos;
3465         NR::Coord const len = NR::L2(delta);
3466         Inkscape::NodePath::Node *othernode = opposite->other;
3467         NR::Point const ndelta = n->pos - othernode->pos;
3468         NR::Coord const linelen = NR::L2(ndelta);
3469         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3470             NR::Coord const scal = dot(delta, ndelta) / linelen;
3471             (*p) = n->pos + (scal / linelen) * ndelta;
3472         }
3473         *p = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), n->subpath->nodepath->item).getPoint();
3474     } else {
3475         *p = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, *p, n->subpath->nodepath->item).getPoint();
3476     }
3478     sp_node_adjust_handle(n, -which);
3480     return FALSE;
3483 /**
3484  * Node handle moved callback.
3485  */
3486 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3488    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3490    Inkscape::NodePath::NodeSide *me;
3491    Inkscape::NodePath::NodeSide *other;
3492     if (n->p.knot == knot) {
3493         me = &n->p;
3494         other = &n->n;
3495     } else if (n->n.knot == knot) {
3496         me = &n->n;
3497         other = &n->p;
3498     } else {
3499         me = NULL;
3500         other = NULL;
3501         g_assert_not_reached();
3502     }
3504     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3505     Radial rme(me->pos - n->pos);
3506     Radial rother(other->pos - n->pos);
3507     Radial rnew(*p - n->pos);
3509     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3510         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3511         /* 0 interpreted as "no snapping". */
3513         // The closest PI/snaps angle, starting from zero.
3514         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3515         if (me->origin_radial.a == HUGE_VAL) {
3516             // ortho doesn't exist: original handle was zero length.
3517             rnew.a = a_snapped;
3518         } else {
3519             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3520              * its opposite and perpendiculars). */
3521             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3523             // Snap to the closest.
3524             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3525                        ? a_snapped
3526                        : a_ortho );
3527         }
3528     }
3530     if (state & GDK_MOD1_MASK) {
3531         // lock handle length
3532         rnew.r = me->origin_radial.r;
3533     }
3535     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3536         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3537         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3538         rother.a += rnew.a - rme.a;
3539         other->pos = NR::Point(rother) + n->pos;
3540         if (other->knot) {
3541             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3542             sp_knot_moveto(other->knot, &other->pos);
3543         }
3544     }
3546     me->pos = NR::Point(rnew) + n->pos;
3547     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3549     // move knot, but without emitting the signal:
3550     // we cannot emit a "moved" signal because we're now processing it
3551     sp_knot_moveto(me->knot, &(me->pos));
3553     update_object(n->subpath->nodepath);
3555     /* status text */
3556     SPDesktop *desktop = n->subpath->nodepath->desktop;
3557     if (!desktop) return;
3558     SPEventContext *ec = desktop->event_context;
3559     if (!ec) return;
3560     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3561     if (!mc) return;
3563     double degrees = 180 / M_PI * rnew.a;
3564     if (degrees > 180) degrees -= 360;
3565     if (degrees < -180) degrees += 360;
3566     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3567         degrees = angle_to_compass (degrees);
3569     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3571     mc->setF(Inkscape::NORMAL_MESSAGE,
3572          _("<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);
3574     g_string_free(length, TRUE);
3577 /**
3578  * Node handle event callback.
3579  */
3580 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3582     gboolean ret = FALSE;
3583     switch (event->type) {
3584         case GDK_KEY_PRESS:
3585             switch (get_group0_keyval (&event->key)) {
3586                 case GDK_space:
3587                     if (event->key.state & GDK_BUTTON1_MASK) {
3588                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3589                         stamp_repr(nodepath);
3590                         ret = TRUE;
3591                     }
3592                     break;
3593                 default:
3594                     break;
3595             }
3596             break;
3597         case GDK_ENTER_NOTIFY:
3598             // we use an experimentally determined threshold that seems to work fine
3599             if (NR::L2(n->pos - knot->pos) < 0.75)
3600                 Inkscape::NodePath::Path::active_node = n;
3601             break;
3602         case GDK_LEAVE_NOTIFY:
3603             // we use an experimentally determined threshold that seems to work fine
3604             if (NR::L2(n->pos - knot->pos) < 0.75)
3605                 Inkscape::NodePath::Path::active_node = NULL;
3606             break;
3607         default:
3608             break;
3609     }
3611     return ret;
3614 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3615                                  Radial &rme, Radial &rother, gboolean const both)
3617     rme.a += angle;
3618     if ( both
3619          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3620          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3621     {
3622         rother.a += angle;
3623     }
3626 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3627                                         Radial &rme, Radial &rother, gboolean const both)
3629     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3631     gdouble r;
3632     if ( both
3633          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3634          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3635     {
3636         r = MAX(rme.r, rother.r);
3637     } else {
3638         r = rme.r;
3639     }
3641     gdouble const weird_angle = atan2(norm_angle, r);
3642 /* Bulia says norm_angle is just the visible distance that the
3643  * object's end must travel on the screen.  Left as 'angle' for want of
3644  * a better name.*/
3646     rme.a += weird_angle;
3647     if ( both
3648          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3649          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3650     {
3651         rother.a += weird_angle;
3652     }
3655 /**
3656  * Rotate one node.
3657  */
3658 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3660     Inkscape::NodePath::NodeSide *me, *other;
3661     bool both = false;
3663     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3664     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3666     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3667         me = &(n->p);
3668         other = &(n->n);
3669     } else if (!n->p.other) {
3670         me = &(n->n);
3671         other = &(n->p);
3672     } else {
3673         if (which > 0) { // right handle
3674             if (xn > xp) {
3675                 me = &(n->n);
3676                 other = &(n->p);
3677             } else {
3678                 me = &(n->p);
3679                 other = &(n->n);
3680             }
3681         } else if (which < 0){ // left handle
3682             if (xn <= xp) {
3683                 me = &(n->n);
3684                 other = &(n->p);
3685             } else {
3686                 me = &(n->p);
3687                 other = &(n->n);
3688             }
3689         } else { // both handles
3690             me = &(n->n);
3691             other = &(n->p);
3692             both = true;
3693         }
3694     }
3696     Radial rme(me->pos - n->pos);
3697     Radial rother(other->pos - n->pos);
3699     if (screen) {
3700         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3701     } else {
3702         node_rotate_one_internal (*n, angle, rme, rother, both);
3703     }
3705     me->pos = n->pos + NR::Point(rme);
3707     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3708         other->pos =  n->pos + NR::Point(rother);
3709     }
3711     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3712     // so here we just move all the knots without emitting move signals, for speed
3713     sp_node_update_handles(n, false);
3716 /**
3717  * Rotate selected nodes.
3718  */
3719 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3721     if (!nodepath || !nodepath->selected) return;
3723     if (g_list_length(nodepath->selected) == 1) {
3724        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3725         node_rotate_one (n, angle, which, screen);
3726     } else {
3727        // rotate as an object:
3729         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3730         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3731         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3732             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3733             box.expandTo (n->pos); // contain all selected nodes
3734         }
3736         gdouble rot;
3737         if (screen) {
3738             gdouble const zoom = nodepath->desktop->current_zoom();
3739             gdouble const zmove = angle / zoom;
3740             gdouble const r = NR::L2(box.max() - box.midpoint());
3741             rot = atan2(zmove, r);
3742         } else {
3743             rot = angle;
3744         }
3746         NR::Point rot_center;
3747         if (Inkscape::NodePath::Path::active_node == NULL)
3748             rot_center = box.midpoint();
3749         else
3750             rot_center = Inkscape::NodePath::Path::active_node->pos;
3752         NR::Matrix t =
3753             NR::Matrix (NR::translate(-rot_center)) *
3754             NR::Matrix (NR::rotate(rot)) *
3755             NR::Matrix (NR::translate(rot_center));
3757         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3758             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3759             n->pos *= t;
3760             n->n.pos *= t;
3761             n->p.pos *= t;
3762             sp_node_update_handles(n, false);
3763         }
3764     }
3766     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3769 /**
3770  * Scale one node.
3771  */
3772 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3774     bool both = false;
3775     Inkscape::NodePath::NodeSide *me, *other;
3777     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3778     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3780     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3781         me = &(n->p);
3782         other = &(n->n);
3783         n->code = NR_CURVETO;
3784     } else if (!n->p.other) {
3785         me = &(n->n);
3786         other = &(n->p);
3787         if (n->n.other)
3788             n->n.other->code = NR_CURVETO;
3789     } else {
3790         if (which > 0) { // right handle
3791             if (xn > xp) {
3792                 me = &(n->n);
3793                 other = &(n->p);
3794                 if (n->n.other)
3795                     n->n.other->code = NR_CURVETO;
3796             } else {
3797                 me = &(n->p);
3798                 other = &(n->n);
3799                 n->code = NR_CURVETO;
3800             }
3801         } else if (which < 0){ // left handle
3802             if (xn <= xp) {
3803                 me = &(n->n);
3804                 other = &(n->p);
3805                 if (n->n.other)
3806                     n->n.other->code = NR_CURVETO;
3807             } else {
3808                 me = &(n->p);
3809                 other = &(n->n);
3810                 n->code = NR_CURVETO;
3811             }
3812         } else { // both handles
3813             me = &(n->n);
3814             other = &(n->p);
3815             both = true;
3816             n->code = NR_CURVETO;
3817             if (n->n.other)
3818                 n->n.other->code = NR_CURVETO;
3819         }
3820     }
3822     Radial rme(me->pos - n->pos);
3823     Radial rother(other->pos - n->pos);
3825     rme.r += grow;
3826     if (rme.r < 0) rme.r = 0;
3827     if (rme.a == HUGE_VAL) {
3828         if (me->other) { // if direction is unknown, initialize it towards the next node
3829             Radial rme_next(me->other->pos - n->pos);
3830             rme.a = rme_next.a;
3831         } else { // if there's no next, initialize to 0
3832             rme.a = 0;
3833         }
3834     }
3835     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3836         rother.r += grow;
3837         if (rother.r < 0) rother.r = 0;
3838         if (rother.a == HUGE_VAL) {
3839             rother.a = rme.a + M_PI;
3840         }
3841     }
3843     me->pos = n->pos + NR::Point(rme);
3845     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3846         other->pos = n->pos + NR::Point(rother);
3847     }
3849     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3850     // so here we just move all the knots without emitting move signals, for speed
3851     sp_node_update_handles(n, false);
3854 /**
3855  * Scale selected nodes.
3856  */
3857 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3859     if (!nodepath || !nodepath->selected) return;
3861     if (g_list_length(nodepath->selected) == 1) {
3862         // scale handles of the single selected node
3863         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3864         node_scale_one (n, grow, which);
3865     } else {
3866         // scale nodes as an "object":
3868         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3869         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3870         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3871             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3872             box.expandTo (n->pos); // contain all selected nodes
3873         }
3875         double scale = (box.maxExtent() + grow)/box.maxExtent();
3877         NR::Point scale_center;
3878         if (Inkscape::NodePath::Path::active_node == NULL)
3879             scale_center = box.midpoint();
3880         else
3881             scale_center = Inkscape::NodePath::Path::active_node->pos;
3883         NR::Matrix t =
3884             NR::Matrix (NR::translate(-scale_center)) *
3885             NR::Matrix (NR::scale(scale, scale)) *
3886             NR::Matrix (NR::translate(scale_center));
3888         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3889             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3890             n->pos *= t;
3891             n->n.pos *= t;
3892             n->p.pos *= t;
3893             sp_node_update_handles(n, false);
3894         }
3895     }
3897     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3900 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3902     if (!nodepath) return;
3903     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3906 /**
3907  * Flip selected nodes horizontally/vertically.
3908  */
3909 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
3911     if (!nodepath || !nodepath->selected) return;
3913     if (g_list_length(nodepath->selected) == 1 && !center) {
3914         // flip handles of the single selected node
3915         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3916         double temp = n->p.pos[axis];
3917         n->p.pos[axis] = n->n.pos[axis];
3918         n->n.pos[axis] = temp;
3919         sp_node_update_handles(n, false);
3920     } else {
3921         // scale nodes as an "object":
3923         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3924         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3925         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3926             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3927             box.expandTo (n->pos); // contain all selected nodes
3928         }
3930         if (!center) {
3931             center = box.midpoint();
3932         }
3933         NR::Matrix t =
3934             NR::Matrix (NR::translate(- *center)) *
3935             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3936             NR::Matrix (NR::translate(*center));
3938         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3939             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3940             n->pos *= t;
3941             n->n.pos *= t;
3942             n->p.pos *= t;
3943             sp_node_update_handles(n, false);
3944         }
3945     }
3947     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3950 //-----------------------------------------------
3951 /**
3952  * Return new subpath under given nodepath.
3953  */
3954 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3956     g_assert(nodepath);
3957     g_assert(nodepath->desktop);
3959    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3961     s->nodepath = nodepath;
3962     s->closed = FALSE;
3963     s->nodes = NULL;
3964     s->first = NULL;
3965     s->last = NULL;
3967     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3968     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3969     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3971     return s;
3974 /**
3975  * Destroy nodes in subpath, then subpath itself.
3976  */
3977 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3979     g_assert(subpath);
3980     g_assert(subpath->nodepath);
3981     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3983     while (subpath->nodes) {
3984         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3985     }
3987     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3989     g_free(subpath);
3992 /**
3993  * Link head to tail in subpath.
3994  */
3995 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3997     g_assert(!sp->closed);
3998     g_assert(sp->last != sp->first);
3999     g_assert(sp->first->code == NR_MOVETO);
4001     sp->closed = TRUE;
4003     //Link the head to the tail
4004     sp->first->p.other = sp->last;
4005     sp->last->n.other  = sp->first;
4006     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4007     sp->first          = sp->last;
4009     //Remove the extra end node
4010     sp_nodepath_node_destroy(sp->last->n.other);
4013 /**
4014  * Open closed (loopy) subpath at node.
4015  */
4016 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4018     g_assert(sp->closed);
4019     g_assert(n->subpath == sp);
4020     g_assert(sp->first == sp->last);
4022     /* We create new startpoint, current node will become last one */
4024    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4025                                                 &n->pos, &n->pos, &n->n.pos);
4028     sp->closed        = FALSE;
4030     //Unlink to make a head and tail
4031     sp->first         = new_path;
4032     sp->last          = n;
4033     n->n.other        = NULL;
4034     new_path->p.other = NULL;
4037 /**
4038  * Returns area in triangle given by points; may be negative.
4039  */
4040 inline double
4041 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
4043     return (p1[NR::X]*p2[NR::Y] + p1[NR::Y]*p3[NR::X] + p2[NR::X]*p3[NR::Y] - p2[NR::Y]*p3[NR::X] - p1[NR::Y]*p2[NR::X] - p1[NR::X]*p3[NR::Y]);
4046 /**
4047  * Return new node in subpath with given properties.
4048  * \param pos Position of node.
4049  * \param ppos Handle position in previous direction
4050  * \param npos Handle position in previous direction
4051  */
4052 Inkscape::NodePath::Node *
4053 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)
4055     g_assert(sp);
4056     g_assert(sp->nodepath);
4057     g_assert(sp->nodepath->desktop);
4059     if (nodechunk == NULL)
4060         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4062     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4064     n->subpath  = sp;
4066     if (type != Inkscape::NodePath::NODE_NONE) {
4067         // use the type from sodipodi:nodetypes
4068         n->type = type;
4069     } else {
4070         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4071             // points are (almost) collinear
4072             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4073                 // endnode, or a node with a retracted handle
4074                 n->type = Inkscape::NodePath::NODE_CUSP;
4075             } else {
4076                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4077             }
4078         } else {
4079             n->type = Inkscape::NodePath::NODE_CUSP;
4080         }
4081     }
4083     n->code     = code;
4084     n->selected = FALSE;
4085     n->pos      = *pos;
4086     n->p.pos    = *ppos;
4087     n->n.pos    = *npos;
4089     n->dragging_out = NULL;
4091     Inkscape::NodePath::Node *prev;
4092     if (next) {
4093         //g_assert(g_list_find(sp->nodes, next));
4094         prev = next->p.other;
4095     } else {
4096         prev = sp->last;
4097     }
4099     if (prev)
4100         prev->n.other = n;
4101     else
4102         sp->first = n;
4104     if (next)
4105         next->p.other = n;
4106     else
4107         sp->last = n;
4109     n->p.other = prev;
4110     n->n.other = next;
4112     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"));
4113     sp_knot_set_position(n->knot, pos, 0);
4115     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4116     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4117     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4118     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4119     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4120     sp_knot_update_ctrl(n->knot);
4122     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4123     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4124     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4125     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4126     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4127     sp_knot_show(n->knot);
4129     // We only create handle knots and lines on demand
4130     n->p.knot = NULL;
4131     n->p.line = NULL;
4132     n->n.knot = NULL;
4133     n->n.line = NULL;
4135     sp->nodes = g_list_prepend(sp->nodes, n);
4137     return n;
4140 /**
4141  * Destroy node and its knots, link neighbors in subpath.
4142  */
4143 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4145     g_assert(node);
4146     g_assert(node->subpath);
4147     g_assert(SP_IS_KNOT(node->knot));
4149    Inkscape::NodePath::SubPath *sp = node->subpath;
4151     if (node->selected) { // first, deselect
4152         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4153         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4154     }
4156     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4158     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4159     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4160     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4161     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4162     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4163     g_object_unref(G_OBJECT(node->knot));
4165     if (node->p.knot) {
4166         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4167         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4168         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4169         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4170         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4171         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4172         g_object_unref(G_OBJECT(node->p.knot));
4173         node->p.knot = NULL;
4174     }
4176     if (node->n.knot) {
4177         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4178         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4179         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4180         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4181         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4182         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4183         g_object_unref(G_OBJECT(node->n.knot));
4184         node->n.knot = NULL;
4185     }
4187     if (node->p.line)
4188         gtk_object_destroy(GTK_OBJECT(node->p.line));
4189     if (node->n.line)
4190         gtk_object_destroy(GTK_OBJECT(node->n.line));
4192     if (sp->nodes) { // there are others nodes on the subpath
4193         if (sp->closed) {
4194             if (sp->first == node) {
4195                 g_assert(sp->last == node);
4196                 sp->first = node->n.other;
4197                 sp->last = sp->first;
4198             }
4199             node->p.other->n.other = node->n.other;
4200             node->n.other->p.other = node->p.other;
4201         } else {
4202             if (sp->first == node) {
4203                 sp->first = node->n.other;
4204                 sp->first->code = NR_MOVETO;
4205             }
4206             if (sp->last == node) sp->last = node->p.other;
4207             if (node->p.other) node->p.other->n.other = node->n.other;
4208             if (node->n.other) node->n.other->p.other = node->p.other;
4209         }
4210     } else { // this was the last node on subpath
4211         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4212     }
4214     g_mem_chunk_free(nodechunk, node);
4217 /**
4218  * Returns one of the node's two sides.
4219  * \param which Indicates which side.
4220  * \return Pointer to previous node side if which==-1, next if which==1.
4221  */
4222 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4224     g_assert(node);
4226     switch (which) {
4227         case -1:
4228             return &node->p;
4229         case 1:
4230             return &node->n;
4231         default:
4232             break;
4233     }
4235     g_assert_not_reached();
4237     return NULL;
4240 /**
4241  * Return the other side of the node, given one of its sides.
4242  */
4243 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4245     g_assert(node);
4247     if (me == &node->p) return &node->n;
4248     if (me == &node->n) return &node->p;
4250     g_assert_not_reached();
4252     return NULL;
4255 /**
4256  * Return NRPathcode on the given side of the node.
4257  */
4258 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4260     g_assert(node);
4262     if (me == &node->p) {
4263         if (node->p.other) return (NRPathcode)node->code;
4264         return NR_MOVETO;
4265     }
4267     if (me == &node->n) {
4268         if (node->n.other) return (NRPathcode)node->n.other->code;
4269         return NR_MOVETO;
4270     }
4272     g_assert_not_reached();
4274     return NR_END;
4277 /**
4278  * Return node with the given index
4279  */
4280 Inkscape::NodePath::Node *
4281 sp_nodepath_get_node_by_index(int index)
4283     Inkscape::NodePath::Node *e = NULL;
4285     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4286     if (!nodepath) {
4287         return e;
4288     }
4290     //find segment
4291     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4293         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4294         int n = g_list_length(sp->nodes);
4295         if (sp->closed) {
4296             n++;
4297         }
4299         //if the piece belongs to this subpath grab it
4300         //otherwise move onto the next subpath
4301         if (index < n) {
4302             e = sp->first;
4303             for (int i = 0; i < index; ++i) {
4304                 e = e->n.other;
4305             }
4306             break;
4307         } else {
4308             if (sp->closed) {
4309                 index -= (n+1);
4310             } else {
4311                 index -= n;
4312             }
4313         }
4314     }
4316     return e;
4319 /**
4320  * Returns plain text meaning of node type.
4321  */
4322 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4324     unsigned retracted = 0;
4325     bool endnode = false;
4327     for (int which = -1; which <= 1; which += 2) {
4328         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4329         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4330             retracted ++;
4331         if (!side->other)
4332             endnode = true;
4333     }
4335     if (retracted == 0) {
4336         if (endnode) {
4337                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4338                 return _("end node");
4339         } else {
4340             switch (node->type) {
4341                 case Inkscape::NodePath::NODE_CUSP:
4342                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4343                     return _("cusp");
4344                 case Inkscape::NodePath::NODE_SMOOTH:
4345                     // TRANSLATORS: "smooth" is an adjective here
4346                     return _("smooth");
4347                 case Inkscape::NodePath::NODE_SYMM:
4348                     return _("symmetric");
4349             }
4350         }
4351     } else if (retracted == 1) {
4352         if (endnode) {
4353             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4354             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4355         } else {
4356             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4357         }
4358     } else {
4359         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4360     }
4362     return NULL;
4365 /**
4366  * Handles content of statusbar as long as node tool is active.
4367  */
4368 void
4369 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4371     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");
4372     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4374     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4375     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4376     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4377     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4379     SPDesktop *desktop = NULL;
4380     if (nodepath) {
4381         desktop = nodepath->desktop;
4382     } else {
4383         desktop = SP_ACTIVE_DESKTOP;
4384     }
4386     SPEventContext *ec = desktop->event_context;
4387     if (!ec) return;
4388     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4389     if (!mc) return;
4391     if (selected_nodes == 0) {
4392         Inkscape::Selection *sel = desktop->selection;
4393         if (!sel || sel->isEmpty()) {
4394             mc->setF(Inkscape::NORMAL_MESSAGE,
4395                      _("Select a single object to edit its nodes or handles."));
4396         } else {
4397             if (nodepath) {
4398             mc->setF(Inkscape::NORMAL_MESSAGE,
4399                      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.",
4400                               "<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.",
4401                               total_nodes),
4402                      total_nodes);
4403             } else {
4404                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4405                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4406                 } else {
4407                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4408                 }
4409             }
4410         }
4411     } else if (nodepath && selected_nodes == 1) {
4412         mc->setF(Inkscape::NORMAL_MESSAGE,
4413                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4414                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4415                           total_nodes),
4416                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4417     } else {
4418         if (selected_subpaths > 1) {
4419             mc->setF(Inkscape::NORMAL_MESSAGE,
4420                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4421                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4422                               total_nodes),
4423                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4424         } else {
4425             mc->setF(Inkscape::NORMAL_MESSAGE,
4426                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4427                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4428                               total_nodes),
4429                      selected_nodes, total_nodes, when_selected);
4430         }
4431     }
4434 /*
4435  * returns a *copy* of the curve of that object.
4436  */
4437 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4438     if (!object)
4439         return NULL;
4441     SPCurve *curve = NULL;
4442     if (SP_IS_PATH(object)) {
4443         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4444         curve = sp_curve_copy(curve_new);
4445     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4446         const gchar *svgd = object->repr->attribute(key);
4447         if (svgd) {
4448             NArtBpath *bpath = sp_svg_read_path(svgd);
4449             SPCurve *curve_new = sp_curve_new_from_bpath(bpath);
4450             if (curve_new) {
4451                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4452             } else {
4453                 g_free(bpath);
4454             }
4455         }
4456     }
4458     return curve;
4461 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4462     if (!np || !np->object || !curve)
4463         return;
4465     if (SP_IS_PATH(np->object)) {
4466         if (SP_SHAPE(np->object)->path_effect_href) {
4467             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4468         } else {
4469             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4470         }
4471     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4472         // FIXME: this writing to string and then reading from string is bound to be slow.
4473         // create a method to convert from curve directly to 2geom...
4474         gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(np->curve));
4475         LIVEPATHEFFECT(np->object)->lpe->setParameter(np->repr_key, svgpath);
4476         g_free(svgpath);
4478         np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4479     }
4482 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4483     np->show_helperpath = show;
4486 /* this function does not work yet */
4487 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4488     np->straight_path = true;
4489     np->show_handles = false;
4490     g_message("add code to make the path straight.");
4491     // do sp_nodepath_convert_node_type on all nodes?
4492     // search for this text !!!   "Make selected segments lines"
4496 /*
4497   Local Variables:
4498   mode:c++
4499   c-file-style:"stroustrup"
4500   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4501   indent-tabs-mode:nil
4502   fill-column:99
4503   End:
4504 */
4505 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :