Code

I don't have this function, must be a recent addition to glib? replaced by a more...
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/canvas-bpath.h"
19 #include "display/curve.h"
20 #include "display/sp-ctrlline.h"
21 #include "display/sodipodi-ctrl.h"
22 #include "display/sp-canvas-util.h"
23 #include <glibmm/i18n.h>
24 #include <2geom/pathvector.h>
25 #include <2geom/sbasis-to-bezier.h>
26 #include <2geom/bezier-curve.h>
27 #include <2geom/hvlinesegment.h>
28 #include "helper/units.h"
29 #include "helper/geom.h"
30 #include "knot.h"
31 #include "inkscape.h"
32 #include "document.h"
33 #include "sp-namedview.h"
34 #include "desktop.h"
35 #include "desktop-handles.h"
36 #include "snap.h"
37 #include "message-stack.h"
38 #include "message-context.h"
39 #include "node-context.h"
40 #include "lpe-tool-context.h"
41 #include "shape-editor.h"
42 #include "selection-chemistry.h"
43 #include "selection.h"
44 #include "xml/repr.h"
45 #include "prefs-utils.h"
46 #include "sp-metrics.h"
47 #include "sp-path.h"
48 #include "libnr/nr-matrix-ops.h"
49 #include "svg/svg.h"
50 #include "verbs.h"
51 #include "display/bezier-utils.h"
52 #include <vector>
53 #include <algorithm>
54 #include <cstring>
55 #include <cmath>
56 #include <string>
57 #include "live_effects/lpeobject.h"
58 #include "live_effects/lpeobject-reference.h"
59 #include "live_effects/effect.h"
60 #include "live_effects/parameter/parameter.h"
61 #include "live_effects/parameter/path.h"
62 #include "util/mathfns.h"
63 #include "display/snap-indicator.h"
64 #include "snapped-point.h"
66 class NR::Matrix;
68 /// \todo
69 /// evil evil evil. FIXME: conflict of two different Path classes!
70 /// There is a conflict in the namespace between two classes named Path.
71 /// #include "sp-flowtext.h"
72 /// #include "sp-flowregion.h"
74 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
75 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
76 GType sp_flowregion_get_type (void);
77 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
78 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
79 GType sp_flowtext_get_type (void);
80 // end evil workaround
82 #include "helper/stlport.h"
85 /// \todo fixme: Implement these via preferences */
87 #define NODE_FILL          0xbfbfbf00
88 #define NODE_STROKE        0x000000ff
89 #define NODE_FILL_HI       0xff000000
90 #define NODE_STROKE_HI     0x000000ff
91 #define NODE_FILL_SEL      0x0000ffff
92 #define NODE_STROKE_SEL    0x000000ff
93 #define NODE_FILL_SEL_HI   0xff000000
94 #define NODE_STROKE_SEL_HI 0x000000ff
95 #define KNOT_FILL          0xffffffff
96 #define KNOT_STROKE        0x000000ff
97 #define KNOT_FILL_HI       0xff000000
98 #define KNOT_STROKE_HI     0x000000ff
100 static GMemChunk *nodechunk = NULL;
102 /* Creation from object */
104 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
105 static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
107 /* Object updating */
109 static void stamp_repr(Inkscape::NodePath::Path *np);
110 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
111 static gchar *create_typestr(Inkscape::NodePath::Path *np);
113 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
115 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
117 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
119 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
121 /* Adjust handle placement, if the node or the other handle is moved */
122 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
123 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
125 /* Node event callbacks */
126 static void node_clicked(SPKnot *knot, guint state, gpointer data);
127 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
128 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
129 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
131 /* Handle event callbacks */
132 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
133 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
134 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
135 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
136 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
137 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
139 /* Constructors and destructors */
141 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
142 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
143 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
144 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
145 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
146                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
147 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
149 /* Helpers */
151 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
152 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
153 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
155 static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
156 static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
158 // active_node indicates mouseover node
159 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
161 static SPCanvasItem *
162 sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false) {
163     SPCurve *helper_curve = curve->copy();
164     helper_curve->transform(np->i2d);
165     SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
166     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
167     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
168     sp_canvas_item_move_to_z(helper_path, 0);
169     if (show) {
170         sp_canvas_item_show(helper_path);
171     }
172     helper_curve->unref();
173     return helper_path;
176 static SPCanvasItem *
177 canvasitem_from_pathvec(Inkscape::NodePath::Path *np, Geom::PathVector const &pathv, bool show) {
178     SPCurve *helper_curve = new SPCurve(pathv);
179     return sp_nodepath_make_helper_item(np, helper_curve, show);
182 static void
183 sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
184     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
185     if (!SP_IS_LPE_ITEM(np->item)) {
186         g_print ("Only LPEItems can have helperpaths!\n");
187         return;
188     }
190     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
191     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
192     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
193         Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
194         Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->lpe;
195         // create new canvas items from the effect's helper paths
196         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
197         for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
198             (*np->helper_path_vec)[lpe].push_back(canvasitem_from_pathvec(np, *j, true));
199         }
200     }
203 void
204 sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
205     //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> >* helper_path_vec;
206     if (!SP_IS_LPE_ITEM(np->item)) {
207         g_print ("Only LPEItems can have helperpaths!\n");
208         return;
209     }
211     SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
212     PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
213     for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
214         Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->lpe;
215         /* update canvas items from the effect's helper paths; note that this code relies on the
216          * fact that getHelperPaths() will always return the same number of helperpaths in the same
217          * order as during their creation in sp_nodepath_create_helperpaths
218          */
219         std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
220         for (unsigned int j = 0; j < hpaths.size(); ++j) {
221             SPCurve *curve = new SPCurve(hpaths[j]);
222             curve->transform(np->i2d);
223             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(((*np->helper_path_vec)[lpe])[j]), curve);
224             curve = curve->unref();
225         }
226     }
229 static void
230 sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
231     for (HelperPathList::iterator i = np->helper_path_vec->begin(); i != np->helper_path_vec->end(); ++i) {
232         for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
233             GtkObject *temp = *j;
234             *j = NULL;
235             gtk_object_destroy(temp);
236         }
237     }
241 /**
242  * \brief Creates new nodepath from item
243  */
244 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
246     Inkscape::XML::Node *repr = object->repr;
248     /** \todo
249      * FIXME: remove this. We don't want to edit paths inside flowtext.
250      * Instead we will build our flowtext with cloned paths, so that the
251      * real paths are outside the flowtext and thus editable as usual.
252      */
253     if (SP_IS_FLOWTEXT(object)) {
254         for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
255             if SP_IS_FLOWREGION(child) {
256                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
257                 if (grandchild && SP_IS_PATH(grandchild)) {
258                     object = SP_ITEM(grandchild);
259                     break;
260                 }
261             }
262         }
263     }
265     SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
267     if (curve == NULL) {
268         return NULL;
269     }
271     if (curve->get_segment_count() < 1) {
272         curve->unref();
273         return NULL; // prevent crash for one-node paths
274     }
276     //Create new nodepath
277     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
278     if (!np) {
279         curve->unref();
280         return NULL;
281     }
283     // Set defaults
284     np->desktop     = desktop;
285     np->object      = object;
286     np->subpaths    = NULL;
287     np->selected    = NULL;
288     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
289     np->local_change = 0;
290     np->show_handles = show_handles;
291     np->helper_path = NULL;
292     np->helper_path_vec = new HelperPathList;
293     np->helperpath_rgba = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
294     np->helperpath_width = 1.0;
295     np->curve = curve->copy();
296     np->show_helperpath = (prefs_get_int_attribute ("tools.nodes", "show_helperpath",  0) == 1);
297     if (SP_IS_LPE_ITEM(object)) {
298         Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
299         if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
300             np->show_helperpath = true;
301         }            
302     }
303     np->straight_path = false;
304     if (IS_LIVEPATHEFFECT(object) && item) {
305         np->item = item;
306     } else {
307         np->item = SP_ITEM(object);
308     }
310     // we need to update item's transform from the repr here,
311     // because they may be out of sync when we respond
312     // to a change in repr by regenerating nodepath     --bb
313     sp_object_read_attr(SP_OBJECT(np->item), "transform");
315     np->i2d  = sp_item_i2d_affine(np->item);
316     np->d2i  = np->i2d.inverse();
318     np->repr = repr;
319     if (repr_key_in) { // apparantly the object is an LPEObject
320         np->repr_key = g_strdup(repr_key_in);
321         np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
322         Inkscape::LivePathEffect::Parameter *lpeparam = LIVEPATHEFFECT(object)->lpe->getParameter(repr_key_in);
323         if (lpeparam) {
324             lpeparam->param_setup_nodepath(np);
325         }
326     } else {
327         np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
328         if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
329             np->repr_key = g_strdup("inkscape:original-d");
331             Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
332             if (lpe) {
333                 lpe->setup_nodepath(np);
334             }
335         } else {
336             np->repr_key = g_strdup("d");
337         }
338     }
340     /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
341      * So for example a closed rectangle has a nodetypestring of length 5.
342      * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
343     Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
344     np->curve->set_pathvector(pathv_sanitized);
345     guint length = np->curve->get_segment_count();
346     for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
347         length += pit->empty() ? 0 : 1;
348     }
350     gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
351     Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
353     // create the subpath(s) from the bpath
354     subpaths_from_pathvector(np, pathv_sanitized, typestr);
356     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
357     np->subpaths = g_list_reverse(np->subpaths);
359     delete[] typestr;
360     curve->unref();
362     // Draw helper curve
363     if (np->show_helperpath) {
364         np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true);
365     }
367     sp_nodepath_create_helperpaths(np);
369     return np;
372 /**
373  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
374  */
375 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
377     if (!np) {  //soft fail, like delete
378         return;
379     }
381     while (np->subpaths) {
382         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
383     }
385     //Inform the ShapeEditor that made me, if any, that I am gone.
386     if (np->shape_editor)
387         np->shape_editor->nodepath_destroyed();
389     g_assert(!np->selected);
391     if (np->helper_path) {
392         GtkObject *temp = np->helper_path;
393         np->helper_path = NULL;
394         gtk_object_destroy(temp);
395     }
396     if (np->curve) {
397         np->curve->unref();
398         np->curve = NULL;
399     }
401     if (np->repr_key) {
402         g_free(np->repr_key);
403         np->repr_key = NULL;
404     }
405     if (np->repr_nodetypes_key) {
406         g_free(np->repr_nodetypes_key);
407         np->repr_nodetypes_key = NULL;
408     }
410     sp_nodepath_destroy_helperpaths(np);
411     delete np->helper_path_vec;
412     np->helper_path_vec = NULL;
414     np->desktop = NULL;
416     g_free(np);
419 /**
420  *  Return the node count of a given NodeSubPath.
421  */
422 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
424     int nodeCount = 0;
426     if (subpath) {
427         nodeCount = g_list_length(subpath->nodes);
428     }
430     return nodeCount;
433 /**
434  *  Return the node count of a given NodePath.
435  */
436 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
438     gint nodeCount = 0;
439     if (np) {
440         for (GList *item = np->subpaths ; item ; item=item->next) {
441             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
442             nodeCount += g_list_length(subpath->nodes);
443         }
444     }
445     return nodeCount;
448 /**
449  *  Return the subpath count of a given NodePath.
450  */
451 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
453     gint nodeCount = 0;
454     if (np) {
455         nodeCount = g_list_length(np->subpaths);
456     }
457     return nodeCount;
460 /**
461  *  Return the selected node count of a given NodePath.
462  */
463 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
465     gint nodeCount = 0;
466     if (np) {
467         nodeCount = g_list_length(np->selected);
468     }
469     return nodeCount;
472 /**
473  *  Return the number of subpaths where nodes are selected in a given NodePath.
474  */
475 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
477     gint nodeCount = 0;
478     if (np && np->selected) {
479         if (!np->selected->next) {
480             nodeCount = 1;
481         } else {
482             for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
483                 Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
484                 for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
485                     Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
486                     if (node->selected) {
487                         nodeCount++;
488                         break;
489                     }
490                 }
491             }
492         }
493     }
494     return nodeCount;
497 /**
498  * Clean up a nodepath after editing.
499  *
500  * Currently we are deleting trivial subpaths.
501  */
502 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
504     GList *badSubPaths = NULL;
506     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
507     for (GList *l = nodepath->subpaths; l ; l=l->next) {
508        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
509        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
510             badSubPaths = g_list_append(badSubPaths, sp);
511     }
513     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
514     //also removes the subpath from nodepath->subpaths
515     for (GList *l = badSubPaths; l ; l=l->next) {
516        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
517         sp_nodepath_subpath_destroy(sp);
518     }
520     g_list_free(badSubPaths);
523 /**
524  * Create new nodepaths from pathvector, make it subpaths of np.
525  * \param t The node type array.
526  */
527 static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
529     guint i = 0;  // index into node type array
530     for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
531         if (pit->empty())
532             continue;  // don't add single knot paths
534         Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
536         NR::Point ppos = pit->initialPoint() * (Geom::Matrix)np->i2d;
537         NRPathcode pcode = NR_MOVETO;
539         /* Johan: Note that this is pretty arcane code. I am pretty sure it is working correctly, be very certain to change it! (better to just rewrite this whole method)*/
540         for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
541             if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
542                 dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
543                 dynamic_cast<Geom::VLineSegment const*>(&*cit) )
544             {
545                 NR::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
546                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
548                 ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
549                 pcode = NR_LINETO;
550             }
551             else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
552                 std::vector<Geom::Point> points = cubic_bezier->points();
553                 NR::Point pos = points[0] * (Geom::Matrix)np->i2d;
554                 NR::Point npos = points[1] * (Geom::Matrix)np->i2d;
555                 sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
557                 ppos = points[2] * (Geom::Matrix)np->i2d;
558                 pcode = NR_CURVETO;
559             }
560         }
562         if (pit->closed()) {
563             // Add last knot (because sp_nodepath_subpath_close kills the last knot)
564             /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
565              * If the length is zero, don't add it to the nodepath. */
566             Geom::Curve const &closing_seg = pit->back_closed();
567             if ( ! closing_seg.isDegenerate() ) {
568                 NR::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
569                 sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
570             }
572             sp_nodepath_subpath_close(sp);
573         }
574     }
577 /**
578  * Convert from sodipodi:nodetypes to new style type array.
579  */
580 static
581 Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
583     Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
585     guint pos = 0;
587     if (types) {
588         for (guint i = 0; types[i] && ( i < length ); i++) {
589             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
590             if (types[i] != '\0') {
591                 switch (types[i]) {
592                     case 's':
593                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
594                         break;
595                     case 'z':
596                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
597                         break;
598                     case 'c':
599                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
600                         break;
601                     default:
602                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
603                         break;
604                 }
605             }
606         }
607     }
609     while (pos < length) {
610         typestr[pos++] = Inkscape::NodePath::NODE_NONE;
611     }
613     return typestr;
616 /**
617  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
618  * updated but repr is not (for speed). Used during curve and node drag.
619  */
620 static void update_object(Inkscape::NodePath::Path *np)
622     g_assert(np);
624     np->curve->unref();
625     np->curve = create_curve(np);
627     sp_nodepath_set_curve(np, np->curve);
629     if (np->show_helperpath) {
630         SPCurve * helper_curve = np->curve->copy();
631         helper_curve->transform(np->i2d);
632         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
633         helper_curve->unref();
634     }
636     // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
637     //sp_nodepath_update_helperpaths(np);
639     // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
640     // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
641     np->shape_editor->update_knotholder();
644 /**
645  * Update XML path node with data from path object.
646  */
647 static void update_repr_internal(Inkscape::NodePath::Path *np)
649     g_assert(np);
651     Inkscape::XML::Node *repr = np->object->repr;
653     np->curve->unref();
654     np->curve = create_curve(np);
656     gchar *typestr = create_typestr(np);
657     gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
659     // determine if path has an effect applied and write to correct "d" attribute.
660     if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
661         np->local_change++;
662         repr->setAttribute(np->repr_key, svgpath);
663     }
665     if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
666         np->local_change++;
667         repr->setAttribute(np->repr_nodetypes_key, typestr);
668     }
670     g_free(svgpath);
671     g_free(typestr);
673     if (np->show_helperpath) {
674         SPCurve * helper_curve = np->curve->copy();
675         helper_curve->transform(np->i2d);
676         sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
677         helper_curve->unref();
678     }
680     // TODO: do we need this call here? after all, update_object() should have been called just before
681     //sp_nodepath_update_helperpaths(np);
684 /**
685  * Update XML path node with data from path object, commit changes forever.
686  */
687 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
689     //fixme: np can be NULL, so check before proceeding
690     g_return_if_fail(np != NULL);
692     update_repr_internal(np);
693     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
695     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
696                      annotation);
699 /**
700  * Update XML path node with data from path object, commit changes with undo.
701  */
702 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
704     update_repr_internal(np);
705     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
706                            annotation);
709 /**
710  * Make duplicate of path, replace corresponding XML node in tree, commit.
711  */
712 static void stamp_repr(Inkscape::NodePath::Path *np)
714     g_assert(np);
716     Inkscape::XML::Node *old_repr = np->object->repr;
717     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
719     // remember the position of the item
720     gint pos = old_repr->position();
721     // remember parent
722     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
724     SPCurve *curve = create_curve(np);
725     gchar *typestr = create_typestr(np);
727     gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
729     new_repr->setAttribute(np->repr_key, svgpath);
730     new_repr->setAttribute(np->repr_nodetypes_key, typestr);
732     // add the new repr to the parent
733     parent->appendChild(new_repr);
734     // move to the saved position
735     new_repr->setPosition(pos > 0 ? pos : 0);
737     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
738                      _("Stamp"));
740     Inkscape::GC::release(new_repr);
741     g_free(svgpath);
742     g_free(typestr);
743     curve->unref();
746 /**
747  * Create curve from path.
748  */
749 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
751     SPCurve *curve = new SPCurve();
753     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
754        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
755         curve->moveto(sp->first->pos * np->d2i);
756        Inkscape::NodePath::Node *n = sp->first->n.other;
757         while (n) {
758             NR::Point const end_pt = n->pos * np->d2i;
759             switch (n->code) {
760                 case NR_LINETO:
761                     curve->lineto(end_pt);
762                     break;
763                 case NR_CURVETO:
764                     curve->curveto(n->p.other->n.pos * np->d2i,
765                                      n->p.pos * np->d2i,
766                                      end_pt);
767                     break;
768                 default:
769                     g_assert_not_reached();
770                     break;
771             }
772             if (n != sp->last) {
773                 n = n->n.other;
774             } else {
775                 n = NULL;
776             }
777         }
778         if (sp->closed) {
779             curve->closepath();
780         }
781     }
783     return curve;
786 /**
787  * Convert path type string to sodipodi:nodetypes style.
788  */
789 static gchar *create_typestr(Inkscape::NodePath::Path *np)
791     gchar *typestr = g_new(gchar, 32);
792     gint len = 32;
793     gint pos = 0;
795     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
796        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
798         if (pos >= len) {
799             typestr = g_renew(gchar, typestr, len + 32);
800             len += 32;
801         }
803         typestr[pos++] = 'c';
805        Inkscape::NodePath::Node *n;
806         n = sp->first->n.other;
807         while (n) {
808             gchar code;
810             switch (n->type) {
811                 case Inkscape::NodePath::NODE_CUSP:
812                     code = 'c';
813                     break;
814                 case Inkscape::NodePath::NODE_SMOOTH:
815                     code = 's';
816                     break;
817                 case Inkscape::NodePath::NODE_SYMM:
818                     code = 'z';
819                     break;
820                 default:
821                     g_assert_not_reached();
822                     code = '\0';
823                     break;
824             }
826             if (pos >= len) {
827                 typestr = g_renew(gchar, typestr, len + 32);
828                 len += 32;
829             }
831             typestr[pos++] = code;
833             if (n != sp->last) {
834                 n = n->n.other;
835             } else {
836                 n = NULL;
837             }
838         }
839     }
841     if (pos >= len) {
842         typestr = g_renew(gchar, typestr, len + 1);
843         len += 1;
844     }
846     typestr[pos++] = '\0';
848     return typestr;
851 // Returns different message contexts depending on the current context. This function should only
852 // be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
853 // other cases.
854 static Inkscape::MessageContext *
855 get_message_context(SPEventContext *ec)
857     Inkscape::MessageContext *mc = 0;
859     if (SP_IS_NODE_CONTEXT(ec)) {
860         mc = SP_NODE_CONTEXT(ec)->_node_message_context;
861     } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
862         mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
863     } else {
864         g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
865     }
867     return mc;
870 /**
871  \brief Fills node and handle positions for three nodes, splitting line
872   marked by end at distance t.
873  */
874 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
876     g_assert(new_path != NULL);
877     g_assert(end      != NULL);
879     g_assert(end->p.other == new_path);
880    Inkscape::NodePath::Node *start = new_path->p.other;
881     g_assert(start);
883     if (end->code == NR_LINETO) {
884         new_path->type =Inkscape::NodePath::NODE_CUSP;
885         new_path->code = NR_LINETO;
886         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
887     } else {
888         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
889         new_path->code = NR_CURVETO;
890         gdouble s      = 1 - t;
891         for (int dim = 0; dim < 2; dim++) {
892             NR::Coord const f000 = start->pos[dim];
893             NR::Coord const f001 = start->n.pos[dim];
894             NR::Coord const f011 = end->p.pos[dim];
895             NR::Coord const f111 = end->pos[dim];
896             NR::Coord const f00t = s * f000 + t * f001;
897             NR::Coord const f01t = s * f001 + t * f011;
898             NR::Coord const f11t = s * f011 + t * f111;
899             NR::Coord const f0tt = s * f00t + t * f01t;
900             NR::Coord const f1tt = s * f01t + t * f11t;
901             NR::Coord const fttt = s * f0tt + t * f1tt;
902             start->n.pos[dim]    = f00t;
903             new_path->p.pos[dim] = f0tt;
904             new_path->pos[dim]   = fttt;
905             new_path->n.pos[dim] = f1tt;
906             end->p.pos[dim]      = f11t;
907         }
908     }
911 /**
912  * Adds new node on direct line between two nodes, activates handles of all
913  * three nodes.
914  */
915 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
917     g_assert(end);
918     g_assert(end->subpath);
919     g_assert(g_list_find(end->subpath->nodes, end));
921    Inkscape::NodePath::Node *start = end->p.other;
922     g_assert( start->n.other == end );
923    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
924                                                end,
925                                                (NRPathcode)end->code == NR_LINETO?
926                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
927                                                (NRPathcode)end->code,
928                                                &start->pos, &start->pos, &start->n.pos);
929     sp_nodepath_line_midpoint(newnode, end, t);
931     sp_node_adjust_handles(start);
932     sp_node_update_handles(start);
933     sp_node_update_handles(newnode);
934     sp_node_adjust_handles(end);
935     sp_node_update_handles(end);
937     return newnode;
940 /**
941 \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
942 */
943 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
945     g_assert(node);
946     g_assert(node->subpath);
947     g_assert(g_list_find(node->subpath->nodes, node));
949     Inkscape::NodePath::Node* result = 0;
950     Inkscape::NodePath::SubPath *sp = node->subpath;
951     Inkscape::NodePath::Path *np    = sp->nodepath;
953     if (sp->closed) {
954         sp_nodepath_subpath_open(sp, node);
955         result = sp->first;
956     } else if ( (node == sp->first) || (node == sp->last ) ){
957         // no break for end nodes
958         result = 0;
959     } else {
960         // create a new subpath
961         Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
963         // duplicate the break node as start of the new subpath
964         Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
965                                                                  static_cast<Inkscape::NodePath::NodeType>(node->type),
966                                                                  NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
968         // attach rest of curve to new node
969         g_assert(node->n.other);
970         newnode->n.other = node->n.other; node->n.other = NULL;
971         newnode->n.other->p.other = newnode;
972         newsubpath->last = sp->last;
973         sp->last = node;
974         node = newnode;
975         while (node->n.other) {
976             node = node->n.other;
977             node->subpath = newsubpath;
978             sp->nodes = g_list_remove(sp->nodes, node);
979             newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
980         }
983         result = newnode;
984     }
985     return result;
988 /**
989  * Duplicate node and connect to neighbours.
990  */
991 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
993     g_assert(node);
994     g_assert(node->subpath);
995     g_assert(g_list_find(node->subpath->nodes, node));
997    Inkscape::NodePath::SubPath *sp = node->subpath;
999     NRPathcode code = (NRPathcode) node->code;
1000     if (code == NR_MOVETO) { // if node is the endnode,
1001         node->code = NR_LINETO; // new one is inserted before it, so change that to line
1002     }
1004     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
1006     if (!node->n.other || !node->p.other) { // if node is an endnode, select it
1007         return node;
1008     } else {
1009         return newnode; // otherwise select the newly created node
1010     }
1013 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
1015     node->p.pos = (node->pos + (node->pos - node->n.pos));
1018 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
1020     node->n.pos = (node->pos + (node->pos - node->p.pos));
1023 /**
1024  * Change line type at node, with side effects on neighbours.
1025  */
1026 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
1028     g_assert(end);
1029     g_assert(end->subpath);
1030     g_assert(end->p.other);
1032     if (end->code != static_cast<guint>(code) ) {
1033         Inkscape::NodePath::Node *start = end->p.other;
1035         end->code = code;
1037         if (code == NR_LINETO) {
1038             if (start->code == NR_LINETO) {
1039                 sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
1040             }
1041             if (end->n.other) {
1042                 if (end->n.other->code == NR_LINETO) {
1043                     sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
1044                 }
1045             }
1046         } else {
1047             NR::Point delta = end->pos - start->pos;
1048             start->n.pos = start->pos + delta / 3;
1049             end->p.pos = end->pos - delta / 3;
1050             sp_node_adjust_handle(start, 1);
1051             sp_node_adjust_handle(end, -1);
1052         }
1054         sp_node_update_handles(start);
1055         sp_node_update_handles(end);
1056     }
1059 /**
1060  * Change node type, and its handles accordingly.
1061  */
1062 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1064     g_assert(node);
1065     g_assert(node->subpath);
1067     if ((node->p.other != NULL) && (node->n.other != NULL)) {
1068         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
1069             type =Inkscape::NodePath::NODE_CUSP;
1070         }
1071     }
1073     node->type = type;
1075     if (node->type == Inkscape::NodePath::NODE_CUSP) {
1076         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
1077         node->knot->setSize (node->selected? 11 : 9);
1078         sp_knot_update_ctrl(node->knot);
1079     } else {
1080         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
1081         node->knot->setSize (node->selected? 9 : 7);
1082         sp_knot_update_ctrl(node->knot);
1083     }
1085     // if one of handles is mouseovered, preserve its position
1086     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
1087         sp_node_adjust_handle(node, 1);
1088     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
1089         sp_node_adjust_handle(node, -1);
1090     } else {
1091         sp_node_adjust_handles(node);
1092     }
1094     sp_node_update_handles(node);
1096     sp_nodepath_update_statusbar(node->subpath->nodepath);
1098     return node;
1101 bool
1102 sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1104 // TODO clean up multiple returns
1105         Inkscape::NodePath::Node *othernode = side->other;
1106         if (!othernode)
1107             return false;
1108         NRPathcode const code = sp_node_path_code_from_side(node, side);
1109         if (code == NR_LINETO)
1110             return true;
1111         Inkscape::NodePath::NodeSide *other_to_me = NULL;
1112         if (&node->p == side) {
1113             other_to_me = &othernode->n;
1114         } else if (&node->n == side) {
1115             other_to_me = &othernode->p;
1116         } 
1117         if (!other_to_me)
1118             return false;
1119         bool is_line = 
1120              (NR::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
1121               NR::L2(node->pos - side->pos) < 1e-6);
1122         return is_line;
1125 /**
1126  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
1127  * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
1128  * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too. 
1129  * If already cusp and set to cusp, retracts handles.
1130 */
1131 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
1133     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
1135 /* 
1136   Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
1137  
1138         if (two_handles) {
1139             // do nothing, adjust_handles called via set_node_type will line them up
1140         } else if (one_handle) {
1141             if (opposite_to_handle_is_line) {
1142                 if (lined_up) {
1143                     // already half-smooth; pull opposite handle too making it fully smooth
1144                 } else {
1145                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1146                 }
1147             } else {
1148                 // pull opposite handle in line with the existing one
1149             }
1150         } else if (no_handles) {
1151             if (both_segments_are_lines OR both_segments_are_curves) {
1152                 //pull both handles
1153             } else {
1154                 // pull the handle opposite to line segment, making node half-smooth
1155             }
1156         }
1157 */
1158         bool p_has_handle = (NR::L2(node->pos  - node->p.pos) > 1e-6);
1159         bool n_has_handle = (NR::L2(node->pos  - node->n.pos) > 1e-6);
1160         bool p_is_line = sp_node_side_is_line(node, &node->p);
1161         bool n_is_line = sp_node_side_is_line(node, &node->n);
1163         if (p_has_handle && n_has_handle) {
1164             // do nothing, adjust_handles will line them up
1165         } else if (p_has_handle || n_has_handle) {
1166             if (p_has_handle && n_is_line) {
1167                 Radial line (node->n.other->pos - node->pos);
1168                 Radial handle (node->pos - node->p.pos);
1169                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1170                     // already half-smooth; pull opposite handle too making it fully smooth
1171                     node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
1172                 } else {
1173                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1174                 }
1175             } else if (n_has_handle && p_is_line) {
1176                 Radial line (node->p.other->pos - node->pos);
1177                 Radial handle (node->pos - node->n.pos);
1178                 if (fabs(line.a - handle.a) < 1e-3) { // lined up
1179                     // already half-smooth; pull opposite handle too making it fully smooth
1180                     node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
1181                 } else {
1182                     // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
1183                 }
1184             } else if (p_has_handle && node->n.other) {
1185                 // pull n handle
1186                 node->n.other->code = NR_CURVETO;
1187                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1188                     NR::L2(node->p.pos - node->pos) :
1189                     NR::L2(node->n.other->pos - node->pos) / 3;
1190                 node->n.pos = node->pos - (len / NR::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
1191             } else if (n_has_handle && node->p.other) {
1192                 // pull p handle
1193                 node->code = NR_CURVETO;
1194                 double len =  (type == Inkscape::NodePath::NODE_SYMM)?
1195                     NR::L2(node->n.pos - node->pos) :
1196                     NR::L2(node->p.other->pos - node->pos) / 3;
1197                 node->p.pos = node->pos - (len / NR::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
1198             }
1199         } else if (!p_has_handle && !n_has_handle) {
1200             if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
1201                 // no handles, but both segments are either lnes or curves:
1202                 //pull both handles
1204                 // convert both to curves:
1205                 node->code = NR_CURVETO;
1206                 node->n.other->code = NR_CURVETO;
1208                 NR::Point leg_prev = node->pos - node->p.other->pos;
1209                 NR::Point leg_next = node->pos - node->n.other->pos;
1211                 double norm_leg_prev = L2(leg_prev);
1212                 double norm_leg_next = L2(leg_next);
1214                 NR::Point delta;
1215                 if (norm_leg_next > 0.0) {
1216                     delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
1217                     (&delta)->normalize();
1218                 }
1220                 if (type == Inkscape::NodePath::NODE_SYMM) {
1221                     double norm_leg_avg = (norm_leg_prev + norm_leg_next) / 2;
1222                     node->p.pos = node->pos + 0.3 * norm_leg_avg * delta;
1223                     node->n.pos = node->pos - 0.3 * norm_leg_avg * delta;
1224                 } else {
1225                     // length of handle is proportional to distance to adjacent node
1226                     node->p.pos = node->pos + 0.3 * norm_leg_prev * delta;
1227                     node->n.pos = node->pos - 0.3 * norm_leg_next * delta;
1228                 }
1230             } else {
1231                 // pull the handle opposite to line segment, making it half-smooth
1232                 if (p_is_line && node->n.other) {
1233                     if (type != Inkscape::NodePath::NODE_SYMM) {
1234                         // pull n handle
1235                         node->n.other->code = NR_CURVETO;
1236                         double len =  NR::L2(node->n.other->pos - node->pos) / 3;
1237                         node->n.pos = node->pos + (len / NR::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
1238                     }
1239                 } else if (n_is_line && node->p.other) {
1240                     if (type != Inkscape::NodePath::NODE_SYMM) {
1241                         // pull p handle
1242                         node->code = NR_CURVETO;
1243                         double len =  NR::L2(node->p.other->pos - node->pos) / 3;
1244                         node->p.pos = node->pos + (len / NR::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
1245                     }
1246                 }
1247             }
1248         }
1249     } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
1250         // cusping a cusp: retract nodes
1251         node->p.pos = node->pos;
1252         node->n.pos = node->pos;
1253     }
1255     sp_nodepath_set_node_type (node, type);
1258 /**
1259  * Move node to point, and adjust its and neighbouring handles.
1260  */
1261 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
1263     NR::Point delta = p - node->pos;
1264     node->pos = p;
1266     node->p.pos += delta;
1267     node->n.pos += delta;
1269     Inkscape::NodePath::Node *node_p = NULL;
1270     Inkscape::NodePath::Node *node_n = NULL;
1272     if (node->p.other) {
1273         if (node->code == NR_LINETO) {
1274             sp_node_adjust_handle(node, 1);
1275             sp_node_adjust_handle(node->p.other, -1);
1276             node_p = node->p.other;
1277         }
1278     }
1279     if (node->n.other) {
1280         if (node->n.other->code == NR_LINETO) {
1281             sp_node_adjust_handle(node, -1);
1282             sp_node_adjust_handle(node->n.other, 1);
1283             node_n = node->n.other;
1284         }
1285     }
1287     // this function is only called from batch movers that will update display at the end
1288     // themselves, so here we just move all the knots without emitting move signals, for speed
1289     sp_node_update_handles(node, false);
1290     if (node_n) {
1291         sp_node_update_handles(node_n, false);
1292     }
1293     if (node_p) {
1294         sp_node_update_handles(node_p, false);
1295     }
1298 /**
1299  * Call sp_node_moveto() for node selection and handle possible snapping.
1300  */
1301 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
1302                                             bool const snap, bool constrained = false, 
1303                                             Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
1305     NR::Coord best = NR_HUGE;
1306     NR::Point delta(dx, dy);
1307     NR::Point best_pt = delta;
1308     Inkscape::SnappedPoint best_abs;
1309     
1310     if (snap) {    
1311         /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
1312          * not to itself. The snapper however can not tell which nodes are selected and which are not, so we 
1313          * must provide that information. */
1314           
1315         // Build a list of the unselected nodes to which the snapper should snap 
1316         std::vector<Geom::Point> unselected_nodes;
1317         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1318             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1319             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1320                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1321                 if (!node->selected) {
1322                     unselected_nodes.push_back(to_2geom(node->pos));
1323                 }    
1324             }
1325         }        
1326         
1327         SnapManager &m = nodepath->desktop->namedview->snap_manager;
1328         
1329         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1330             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1331             m.setup(nodepath->desktop, false, SP_PATH(n->subpath->nodepath->item), &unselected_nodes);
1332             Inkscape::SnappedPoint s;
1333             if (constrained) {
1334                 Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
1335                 dedicated_constraint.setPoint(n->pos);
1336                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta), dedicated_constraint);
1337             } else {
1338                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(n->pos + delta));
1339             }            
1340             if (s.getSnapped() && (s.getDistance() < best)) {
1341                 best = s.getDistance();
1342                 best_abs = s;
1343                 best_pt = from_2geom(s.getPoint()) - n->pos;
1344             }
1345         }
1346                         
1347         if (best_abs.getSnapped()) {
1348             nodepath->desktop->snapindicator->set_new_snappoint(best_abs);
1349         } else {
1350             nodepath->desktop->snapindicator->remove_snappoint();    
1351         }
1352     }
1354     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1355         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1356         sp_node_moveto(n, n->pos + best_pt);
1357     }
1359     // do not update repr here so that node dragging is acceptably fast
1360     update_object(nodepath);
1363 /**
1364 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1365 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1366 near x = 0.
1367  */
1368 double
1369 sculpt_profile (double x, double alpha, guint profile)
1371     double result = 1;
1373     if (x >= 1) {
1374         result = 0;
1375     } else if (x <= 0) {
1376         result = 1;
1377     } else {
1378         switch (profile) {
1379             case SCULPT_PROFILE_LINEAR:
1380                 result = 1 - x;
1381                 break;
1382             case SCULPT_PROFILE_BELL:
1383                 result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1384                 break;
1385             case SCULPT_PROFILE_ELLIPTIC:
1386                 result = sqrt(1 - x*x);
1387                 break;
1388             default:
1389                 g_assert_not_reached();
1390         }
1391     }
1393     return result;
1396 double
1397 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1399     // extremely primitive for now, don't have time to look for the real one
1400     double lower = NR::L2(b - a);
1401     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1402     return (lower + upper)/2;
1405 void
1406 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1408     n->pos = n->origin + delta;
1409     n->n.pos = n->n.origin + delta_n;
1410     n->p.pos = n->p.origin + delta_p;
1411     sp_node_adjust_handles(n);
1412     sp_node_update_handles(n, false);
1415 /**
1416  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1417  * on how far they are from the dragged node n.
1418  */
1419 static void
1420 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1422     g_assert (n);
1423     g_assert (nodepath);
1424     g_assert (n->subpath->nodepath == nodepath);
1426     double pressure = n->knot->pressure;
1427     if (pressure == 0)
1428         pressure = 0.5; // default
1429     pressure = CLAMP (pressure, 0.2, 0.8);
1431     // map pressure to alpha = 1/5 ... 5
1432     double alpha = 1 - 2 * fabs(pressure - 0.5);
1433     if (pressure > 0.5)
1434         alpha = 1/alpha;
1436     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1438     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1439         // Only one subpath has selected nodes:
1440         // use linear mode, where the distance from n to node being dragged is calculated along the path
1442         double n_sel_range = 0, p_sel_range = 0;
1443         guint n_nodes = 0, p_nodes = 0;
1444         guint n_sel_nodes = 0, p_sel_nodes = 0;
1446         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1447         {
1448             double n_range = 0, p_range = 0;
1449             bool n_going = true, p_going = true;
1450             Inkscape::NodePath::Node *n_node = n;
1451             Inkscape::NodePath::Node *p_node = n;
1452             do {
1453                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1454                 if (n_node && n_going)
1455                     n_node = n_node->n.other;
1456                 if (n_node == NULL) {
1457                     n_going = false;
1458                 } else {
1459                     n_nodes ++;
1460                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1461                     if (n_node->selected) {
1462                         n_sel_nodes ++;
1463                         n_sel_range = n_range;
1464                     }
1465                     if (n_node == p_node) {
1466                         n_going = false;
1467                         p_going = false;
1468                     }
1469                 }
1470                 if (p_node && p_going)
1471                     p_node = p_node->p.other;
1472                 if (p_node == NULL) {
1473                     p_going = false;
1474                 } else {
1475                     p_nodes ++;
1476                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1477                     if (p_node->selected) {
1478                         p_sel_nodes ++;
1479                         p_sel_range = p_range;
1480                     }
1481                     if (p_node == n_node) {
1482                         n_going = false;
1483                         p_going = false;
1484                     }
1485                 }
1486             } while (n_going || p_going);
1487         }
1489         // Second pass: actually move nodes in this subpath
1490         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1491         {
1492             double n_range = 0, p_range = 0;
1493             bool n_going = true, p_going = true;
1494             Inkscape::NodePath::Node *n_node = n;
1495             Inkscape::NodePath::Node *p_node = n;
1496             do {
1497                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1498                 if (n_node && n_going)
1499                     n_node = n_node->n.other;
1500                 if (n_node == NULL) {
1501                     n_going = false;
1502                 } else {
1503                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1504                     if (n_node->selected) {
1505                         sp_nodepath_move_node_and_handles (n_node,
1506                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1507                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1508                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1509                     }
1510                     if (n_node == p_node) {
1511                         n_going = false;
1512                         p_going = false;
1513                     }
1514                 }
1515                 if (p_node && p_going)
1516                     p_node = p_node->p.other;
1517                 if (p_node == NULL) {
1518                     p_going = false;
1519                 } else {
1520                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1521                     if (p_node->selected) {
1522                         sp_nodepath_move_node_and_handles (p_node,
1523                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1524                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1525                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1526                     }
1527                     if (p_node == n_node) {
1528                         n_going = false;
1529                         p_going = false;
1530                     }
1531                 }
1532             } while (n_going || p_going);
1533         }
1535     } else {
1536         // Multiple subpaths have selected nodes:
1537         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1538         // TODO: correct these distances taking into account their angle relative to the bisector, so as to
1539         // fix the pear-like shape when sculpting e.g. a ring
1541         // First pass: calculate range
1542         gdouble direct_range = 0;
1543         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1544             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1545             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1546                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1547                 if (node->selected) {
1548                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1549                 }
1550             }
1551         }
1553         // Second pass: actually move nodes
1554         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1555             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1556             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1557                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1558                 if (node->selected) {
1559                     if (direct_range > 1e-6) {
1560                         sp_nodepath_move_node_and_handles (node,
1561                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1562                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1563                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1564                     } else {
1565                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1566                     }
1568                 }
1569             }
1570         }
1571     }
1573     // do not update repr here so that node dragging is acceptably fast
1574     update_object(nodepath);
1578 /**
1579  * Move node selection to point, adjust its and neighbouring handles,
1580  * handle possible snapping, and commit the change with possible undo.
1581  */
1582 void
1583 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1585     if (!nodepath) return;
1587     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1589     if (dx == 0) {
1590         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1591     } else if (dy == 0) {
1592         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1593     } else {
1594         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1595     }
1598 /**
1599  * Move node selection off screen and commit the change.
1600  */
1601 void
1602 sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1604     // borrowed from sp_selection_move_screen in selection-chemistry.c
1605     // we find out the current zoom factor and divide deltas by it
1607     gdouble zoom = desktop->current_zoom();
1608     gdouble zdx = dx / zoom;
1609     gdouble zdy = dy / zoom;
1611     if (!nodepath) return;
1613     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1615     if (dx == 0) {
1616         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1617     } else if (dy == 0) {
1618         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1619     } else {
1620         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1621     }
1624 /**
1625  * Move selected nodes to the absolute position given
1626  */
1627 void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
1629     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1630         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1631         Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
1632         sp_node_moveto(n, npos);
1633     }
1635     sp_nodepath_update_repr(nodepath, _("Move nodes"));
1638 /**
1639  * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return NR::Nothing
1640  */
1641 boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
1643     boost::optional<Geom::Coord> no_coord;
1644     g_return_val_if_fail(nodepath->selected, no_coord);
1646     // determine coordinate of first selected node
1647     GList *nsel = nodepath->selected;
1648     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
1649     NR::Coord coord = n->pos[axis];
1650     bool coincide = true;
1652     // compare it to the coordinates of all the other selected nodes
1653     for (GList *l = nsel->next; l != NULL; l = l->next) {
1654         n = (Inkscape::NodePath::Node *) l->data;
1655         if (n->pos[axis] != coord) {
1656             coincide = false;
1657         }
1658     }
1659     if (coincide) {
1660         return coord;
1661     } else {
1662         Geom::Rect bbox = sp_node_selected_bbox(nodepath);
1663         // currently we return the coordinate of the bounding box midpoint because I don't know how
1664         // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
1665         return bbox.midpoint()[axis];
1666     }
1669 /** If they don't yet exist, creates knot and line for the given side of the node */
1670 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1672     if (!side->knot) {
1673         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"));
1675         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1676         side->knot->setSize (7);
1677         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1678         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1679         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1680         sp_knot_update_ctrl(side->knot);
1682         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1683         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1684         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1685         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1686         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1687         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1688     }
1690     if (!side->line) {
1691         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1692                                         SP_TYPE_CTRLLINE, NULL);
1693     }
1696 /**
1697  * Ensure the given handle of the node is visible/invisible, update its screen position
1698  */
1699 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1701     g_assert(node != NULL);
1703    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1704     NRPathcode code = sp_node_path_code_from_side(node, side);
1706     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1708     if (show_handle) {
1709         if (!side->knot) { // No handle knot at all
1710             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1711             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1712             side->knot->pos = side->pos;
1713             if (side->knot->item)
1714                 SP_CTRL(side->knot->item)->moveto(side->pos);
1715             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1716             sp_knot_show(side->knot);
1717         } else {
1718             if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
1719                 if (fire_move_signals) {
1720                     sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
1721                 } else {
1722                     sp_knot_moveto(side->knot, side->pos);
1723                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1724                 }
1725             }
1726             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1727                 sp_knot_show(side->knot);
1728             }
1729         }
1730         sp_canvas_item_show(side->line);
1731     } else {
1732         if (side->knot) {
1733             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1734                 sp_knot_hide(side->knot);
1735             }
1736         }
1737         if (side->line) {
1738             sp_canvas_item_hide(side->line);
1739         }
1740     }
1743 /**
1744  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1745  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1746  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1747  * updated; otherwise, just move the knots silently (used in batch moves).
1748  */
1749 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1751     g_assert(node != NULL);
1753     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1754         sp_knot_show(node->knot);
1755     }
1757     if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
1758         if (fire_move_signals)
1759             sp_knot_set_position(node->knot, node->pos, 0);
1760         else
1761             sp_knot_moveto(node->knot, node->pos);
1762     }
1764     gboolean show_handles = node->selected;
1765     if (node->p.other != NULL) {
1766         if (node->p.other->selected) show_handles = TRUE;
1767     }
1768     if (node->n.other != NULL) {
1769         if (node->n.other->selected) show_handles = TRUE;
1770     }
1772     if (node->subpath->nodepath->show_handles == false)
1773         show_handles = FALSE;
1775     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1776     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1779 /**
1780  * Call sp_node_update_handles() for all nodes on subpath.
1781  */
1782 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1784     g_assert(subpath != NULL);
1786     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1787         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1788     }
1791 /**
1792  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1793  */
1794 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1796     g_assert(nodepath != NULL);
1798     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1799         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1800     }
1803 void
1804 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1806     if (nodepath) {
1807         nodepath->show_handles = show;
1808         sp_nodepath_update_handles(nodepath);
1809     }
1812 /**
1813  * Adds all selected nodes in nodepath to list.
1814  */
1815 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1817     StlConv<Node *>::list(l, selected);
1818 /// \todo this adds a copying, rework when the selection becomes a stl list
1821 /**
1822  * Align selected nodes on the specified axis.
1823  */
1824 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1826     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1827         return;
1828     }
1830     if ( !nodepath->selected->next ) { // only one node selected
1831         return;
1832     }
1833    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1834     NR::Point dest(pNode->pos);
1835     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1836         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1837         if (pNode) {
1838             dest[axis] = pNode->pos[axis];
1839             sp_node_moveto(pNode, dest);
1840         }
1841     }
1843     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1846 /// Helper struct.
1847 struct NodeSort
1849    Inkscape::NodePath::Node *_node;
1850     NR::Coord _coord;
1851     /// \todo use vectorof pointers instead of calling copy ctor
1852     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1853         _node(node), _coord(node->pos[axis])
1854     {}
1856 };
1858 static bool operator<(NodeSort const &a, NodeSort const &b)
1860     return (a._coord < b._coord);
1863 /**
1864  * Distribute selected nodes on the specified axis.
1865  */
1866 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1868     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1869         return;
1870     }
1872     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1873         return;
1874     }
1876    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1877     std::vector<NodeSort> sorted;
1878     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1879         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1880         if (pNode) {
1881             NodeSort n(pNode, axis);
1882             sorted.push_back(n);
1883             //dest[axis] = pNode->pos[axis];
1884             //sp_node_moveto(pNode, dest);
1885         }
1886     }
1887     std::sort(sorted.begin(), sorted.end());
1888     unsigned int len = sorted.size();
1889     //overall bboxes span
1890     float dist = (sorted.back()._coord -
1891                   sorted.front()._coord);
1892     //new distance between each bbox
1893     float step = (dist) / (len - 1);
1894     float pos = sorted.front()._coord;
1895     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1896           it < sorted.end();
1897           it ++ )
1898     {
1899         NR::Point dest((*it)._node->pos);
1900         dest[axis] = pos;
1901         sp_node_moveto((*it)._node, dest);
1902         pos += step;
1903     }
1905     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1909 /**
1910  * Call sp_nodepath_line_add_node() for all selected segments.
1911  */
1912 void
1913 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1915     if (!nodepath) {
1916         return;
1917     }
1919     GList *nl = NULL;
1921     int n_added = 0;
1923     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1924        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1925         g_assert(t->selected);
1926         if (t->p.other && t->p.other->selected) {
1927             nl = g_list_prepend(nl, t);
1928         }
1929     }
1931     while (nl) {
1932        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1933        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1934        sp_nodepath_node_select(n, TRUE, FALSE);
1935        n_added ++;
1936        nl = g_list_remove(nl, t);
1937     }
1939     /** \todo fixme: adjust ? */
1940     sp_nodepath_update_handles(nodepath);
1942     if (n_added > 1) {
1943         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1944     } else if (n_added > 0) {
1945         sp_nodepath_update_repr(nodepath, _("Add node"));
1946     }
1948     sp_nodepath_update_statusbar(nodepath);
1951 /**
1952  * Select segment nearest to point
1953  */
1954 void
1955 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1957     if (!nodepath) {
1958         return;
1959     }
1961     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
1962     Geom::PathVector const &pathv = curve->get_pathvector();
1963     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
1965     // calculate index for nodepath's representation.
1966     unsigned int segment_index = floor(pvpos.t) + 1;
1967     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
1968         segment_index += pathv[i].size() + 1;
1969         if (pathv[i].closed()) {
1970             segment_index += 1;
1971         }
1972     }
1974     curve->unref();
1976     //find segment to segment
1977     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
1979     //fixme: this can return NULL, so check before proceeding.
1980     g_return_if_fail(e != NULL);
1982     gboolean force = FALSE;
1983     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1984         force = TRUE;
1985     }
1986     sp_nodepath_node_select(e, (gboolean) toggle, force);
1987     if (e->p.other)
1988         sp_nodepath_node_select(e->p.other, TRUE, force);
1990     sp_nodepath_update_handles(nodepath);
1992     sp_nodepath_update_statusbar(nodepath);
1995 /**
1996  * Add a node nearest to point
1997  */
1998 void
1999 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
2001     if (!nodepath) {
2002         return;
2003     }
2005     SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
2006     Geom::PathVector const &pathv = curve->get_pathvector();
2007     Geom::PathVectorPosition pvpos = Geom::nearestPoint(pathv, p);
2009     // calculate index for nodepath's representation.
2010     double int_part;
2011     double t = std::modf(pvpos.t, &int_part);
2012     unsigned int segment_index = (unsigned int)int_part + 1;
2013     for (unsigned int i = 0; i < pvpos.path_nr; ++i) {
2014         segment_index += pathv[i].size() + 1;
2015         if (pathv[i].closed()) {
2016             segment_index += 1;
2017         }
2018     }
2020     curve->unref();
2022     //find segment to split
2023     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
2025     //don't know why but t seems to flip for lines
2026     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
2027         t = 1.0 - t;
2028     }
2030     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
2031     sp_nodepath_node_select(n, FALSE, TRUE);
2033     /* fixme: adjust ? */
2034     sp_nodepath_update_handles(nodepath);
2036     sp_nodepath_update_repr(nodepath, _("Add node"));
2038     sp_nodepath_update_statusbar(nodepath);
2041 /*
2042  * Adjusts a segment so that t moves by a certain delta for dragging
2043  * converts lines to curves
2044  *
2045  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
2046  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
2047  */
2048 void
2049 sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, NR::Point delta)
2051     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
2053     //fixme: e and e->p can be NULL, so check for those before proceeding
2054     g_return_if_fail(e != NULL);
2055     g_return_if_fail(&e->p != NULL);
2057     /* feel good is an arbitrary parameter that distributes the delta between handles
2058      * if t of the drag point is less than 1/6 distance form the endpoint only
2059      * the corresponding hadle is adjusted. This matches the behavior in GIMP
2060      */
2061     double feel_good;
2062     if (t <= 1.0 / 6.0)
2063         feel_good = 0;
2064     else if (t <= 0.5)
2065         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
2066     else if (t <= 5.0 / 6.0)
2067         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
2068     else
2069         feel_good = 1;
2071     //if we're dragging a line convert it to a curve
2072     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
2073         sp_nodepath_set_line_type(e, NR_CURVETO);
2074     }
2076     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
2077     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
2078     e->p.other->n.pos += offsetcoord0;
2079     e->p.pos += offsetcoord1;
2081     // adjust handles of adjacent nodes where necessary
2082     sp_node_adjust_handle(e,1);
2083     sp_node_adjust_handle(e->p.other,-1);
2085     sp_nodepath_update_handles(e->subpath->nodepath);
2087     update_object(e->subpath->nodepath);
2089     sp_nodepath_update_statusbar(e->subpath->nodepath);
2093 /**
2094  * Call sp_nodepath_break() for all selected segments.
2095  */
2096 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
2098     if (!nodepath) return;
2100     GList *tempin = g_list_copy(nodepath->selected);
2101     GList *temp = NULL;
2102     for (GList *l = tempin; l != NULL; l = l->next) {
2103        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2104        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
2105         if (nn == NULL) continue; // no break, no new node
2106         temp = g_list_prepend(temp, nn);
2107     }
2108     g_list_free(tempin);
2110     if (temp) {
2111         sp_nodepath_deselect(nodepath);
2112     }
2113     for (GList *l = temp; l != NULL; l = l->next) {
2114         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2115     }
2117     sp_nodepath_update_handles(nodepath);
2119     sp_nodepath_update_repr(nodepath, _("Break path"));
2122 /**
2123  * Duplicate the selected node(s).
2124  */
2125 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
2127     if (!nodepath) {
2128         return;
2129     }
2131     GList *temp = NULL;
2132     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2133        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2134        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
2135         if (nn == NULL) continue; // could not duplicate
2136         temp = g_list_prepend(temp, nn);
2137     }
2139     if (temp) {
2140         sp_nodepath_deselect(nodepath);
2141     }
2142     for (GList *l = temp; l != NULL; l = l->next) {
2143         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
2144     }
2146     sp_nodepath_update_handles(nodepath);
2148     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
2151 /**
2152  *  Internal function to join two nodes by merging them into one.
2153  */
2154 static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2156     /* a and b are endpoints */
2158     // if one of the two nodes is mouseovered, fix its position
2159     NR::Point c;
2160     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
2161         c = a->pos;
2162     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
2163         c = b->pos;
2164     } else {
2165         // otherwise, move joined node to the midpoint
2166         c = (a->pos + b->pos) / 2;
2167     }
2169     if (a->subpath == b->subpath) {
2170        Inkscape::NodePath::SubPath *sp = a->subpath;
2171         sp_nodepath_subpath_close(sp);
2172         sp_node_moveto (sp->first, c);
2174         sp_nodepath_update_handles(sp->nodepath);
2175         sp_nodepath_update_repr(nodepath, _("Close subpath"));
2176         return;
2177     }
2179     /* a and b are separate subpaths */
2180     Inkscape::NodePath::SubPath *sa = a->subpath;
2181     Inkscape::NodePath::SubPath *sb = b->subpath;
2182     NR::Point p;
2183     Inkscape::NodePath::Node *n;
2184     NRPathcode code;
2185     if (a == sa->first) {
2186         // we will now reverse sa, so that a is its last node, not first, and drop that node
2187         p = sa->first->n.pos;
2188         code = (NRPathcode)sa->first->n.other->code;
2189         // create new subpath
2190        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2191        // create a first moveto node on it
2192         n = sa->last;
2193         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2194         n = n->p.other;
2195         if (n == sa->first) n = NULL;
2196         while (n) {
2197             // copy the rest of the nodes from sa to t, going backwards
2198             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2199             n = n->p.other;
2200             if (n == sa->first) n = NULL;
2201         }
2202         // replace sa with t
2203         sp_nodepath_subpath_destroy(sa);
2204         sa = t;
2205     } else if (a == sa->last) {
2206         // a is already last, just drop it
2207         p = sa->last->p.pos;
2208         code = (NRPathcode)sa->last->code;
2209         sp_nodepath_node_destroy(sa->last);
2210     } else {
2211         code = NR_END;
2212         g_assert_not_reached();
2213     }
2215     if (b == sb->first) {
2216         // copy all nodes from b to a, forward 
2217         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
2218         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
2219             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2220         }
2221     } else if (b == sb->last) {
2222         // copy all nodes from b to a, backward 
2223         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
2224         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
2225             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2226         }
2227     } else {
2228         g_assert_not_reached();
2229     }
2230     /* and now destroy sb */
2232     sp_nodepath_subpath_destroy(sb);
2234     sp_nodepath_update_handles(sa->nodepath);
2236     sp_nodepath_update_repr(nodepath, _("Join nodes"));
2238     sp_nodepath_update_statusbar(nodepath);
2241 /**
2242  *  Internal function to join two nodes by adding a segment between them.
2243  */
2244 static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
2246     if (a->subpath == b->subpath) {
2247        Inkscape::NodePath::SubPath *sp = a->subpath;
2249         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
2250         sp->closed = TRUE;
2252         sp->first->p.other = sp->last;
2253         sp->last->n.other  = sp->first;
2255         sp_node_handle_mirror_p_to_n(sp->last);
2256         sp_node_handle_mirror_n_to_p(sp->first);
2258         sp->first->code = sp->last->code;
2259         sp->first       = sp->last;
2261         sp_nodepath_update_handles(sp->nodepath);
2263         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
2265         return;
2266     }
2268     /* a and b are separate subpaths */
2269     Inkscape::NodePath::SubPath *sa = a->subpath;
2270     Inkscape::NodePath::SubPath *sb = b->subpath;
2272     Inkscape::NodePath::Node *n;
2273     NR::Point p;
2274     NRPathcode code;
2275     if (a == sa->first) {
2276         code = (NRPathcode) sa->first->n.other->code;
2277        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
2278         n = sa->last;
2279         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
2280         for (n = n->p.other; n != NULL; n = n->p.other) {
2281             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2282         }
2283         sp_nodepath_subpath_destroy(sa);
2284         sa = t;
2285     } else if (a == sa->last) {
2286         code = (NRPathcode)sa->last->code;
2287     } else {
2288         code = NR_END;
2289         g_assert_not_reached();
2290     }
2292     if (b == sb->first) {
2293         n = sb->first;
2294         sp_node_handle_mirror_p_to_n(sa->last);
2295         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
2296         sp_node_handle_mirror_n_to_p(sa->last);
2297         for (n = n->n.other; n != NULL; n = n->n.other) {
2298             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
2299         }
2300     } else if (b == sb->last) {
2301         n = sb->last;
2302         sp_node_handle_mirror_p_to_n(sa->last);
2303         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
2304         sp_node_handle_mirror_n_to_p(sa->last);
2305         for (n = n->p.other; n != NULL; n = n->p.other) {
2306             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
2307         }
2308     } else {
2309         g_assert_not_reached();
2310     }
2311     /* and now destroy sb */
2313     sp_nodepath_subpath_destroy(sb);
2315     sp_nodepath_update_handles(sa->nodepath);
2317     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
2320 enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
2322 /**
2323  * Internal function to handle joining two nodes.
2324  */
2325 static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
2327     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2329     if (g_list_length(nodepath->selected) != 2) {
2330         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2331         return;
2332     }
2334     Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2335     Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2337     g_assert(a != b);
2338     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
2339         // someone tried to join an orphan node (i.e. a single-node subpath).
2340         // this is not worth an error message, just fail silently.
2341         return;
2342     }
2344     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
2345         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
2346         return;
2347     }
2349     switch(mode) {
2350         case NODE_JOIN_ENDPOINTS:
2351             do_node_selected_join(nodepath, a, b);
2352             break;
2353         case NODE_JOIN_SEGMENT:
2354             do_node_selected_join_segment(nodepath, a, b);
2355             break;
2356     }
2359 /**
2360  *  Join two nodes by merging them into one.
2361  */
2362 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
2364     node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
2367 /**
2368  *  Join two nodes by adding a segment between them.
2369  */
2370 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
2372     node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
2375 /**
2376  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
2377  */
2378 void sp_node_delete_preserve(GList *nodes_to_delete)
2380     GSList *nodepaths = NULL;
2382     while (nodes_to_delete) {
2383         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
2384         Inkscape::NodePath::SubPath *sp = node->subpath;
2385         Inkscape::NodePath::Path *nodepath = sp->nodepath;
2386         Inkscape::NodePath::Node *sample_cursor = NULL;
2387         Inkscape::NodePath::Node *sample_end = NULL;
2388         Inkscape::NodePath::Node *delete_cursor = node;
2389         bool just_delete = false;
2391         //find the start of this contiguous selection
2392         //move left to the first node that is not selected
2393         //or the start of the non-closed path
2394         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
2395             delete_cursor = curr;
2396         }
2398         //just delete at the beginning of an open path
2399         if (!delete_cursor->p.other) {
2400             sample_cursor = delete_cursor;
2401             just_delete = true;
2402         } else {
2403             sample_cursor = delete_cursor->p.other;
2404         }
2406         //calculate points for each segment
2407         int rate = 5;
2408         float period = 1.0 / rate;
2409         std::vector<NR::Point> data;
2410         if (!just_delete) {
2411             data.push_back(sample_cursor->pos);
2412             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
2413                 //just delete at the end of an open path
2414                 if (!sp->closed && curr == sp->last) {
2415                     just_delete = true;
2416                     break;
2417                 }
2419                 //sample points on the contiguous selected segment
2420                 NR::Point *bez;
2421                 bez = new NR::Point [4];
2422                 bez[0] = curr->pos;
2423                 bez[1] = curr->n.pos;
2424                 bez[2] = curr->n.other->p.pos;
2425                 bez[3] = curr->n.other->pos;
2426                 for (int i=1; i<rate; i++) {
2427                     gdouble t = i * period;
2428                     NR::Point p = bezier_pt(3, bez, t);
2429                     data.push_back(p);
2430                 }
2431                 data.push_back(curr->n.other->pos);
2433                 sample_end = curr->n.other;
2434                 //break if we've come full circle or hit the end of the selection
2435                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2436                     break;
2437                 }
2438             }
2439         }
2441         if (!just_delete) {
2442             //calculate the best fitting single segment and adjust the endpoints
2443             NR::Point *adata;
2444             adata = new NR::Point [data.size()];
2445             copy(data.begin(), data.end(), adata);
2447             NR::Point *bez;
2448             bez = new NR::Point [4];
2449             //would decreasing error create a better fitting approximation?
2450             gdouble error = 1.0;
2451             gint ret;
2452             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2454             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2455             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2456             //the resulting nodes behave as expected.
2457             if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
2458                 sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2459             if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
2460                 sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2462             //adjust endpoints
2463             sample_cursor->n.pos = bez[1];
2464             sample_end->p.pos = bez[2];
2465         }
2467         //destroy this contiguous selection
2468         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2469             Inkscape::NodePath::Node *temp = delete_cursor;
2470             if (delete_cursor->n.other == delete_cursor) {
2471                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2472                 delete_cursor = NULL;
2473             } else {
2474                 delete_cursor = delete_cursor->n.other;
2475             }
2476             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2477             sp_nodepath_node_destroy(temp);
2478         }
2480         sp_nodepath_update_handles(nodepath);
2482         if (!g_slist_find(nodepaths, nodepath))
2483             nodepaths = g_slist_prepend (nodepaths, nodepath);
2484     }
2486     for (GSList *i = nodepaths; i; i = i->next) {
2487         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2488         // different nodepaths will give us one undo event per nodepath
2489         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2491         // if the entire nodepath is removed, delete the selected object.
2492         if (nodepath->subpaths == NULL ||
2493             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2494             //at least 2
2495             sp_nodepath_get_node_count(nodepath) < 2) {
2496             SPDocument *document = sp_desktop_document (nodepath->desktop);
2497             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2498             //delete this nodepath's object, not the entire selection! (though at this time, this
2499             //does not matter)
2500             sp_selection_delete(nodepath->desktop);
2501             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2502                               _("Delete nodes"));
2503         } else {
2504             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2505             sp_nodepath_update_statusbar(nodepath);
2506         }
2507     }
2509     g_slist_free (nodepaths);
2512 /**
2513  * Delete one or more selected nodes.
2514  */
2515 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2517     if (!nodepath) return;
2518     if (!nodepath->selected) return;
2520     /** \todo fixme: do it the right way */
2521     while (nodepath->selected) {
2522        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2523         sp_nodepath_node_destroy(node);
2524     }
2527     //clean up the nodepath (such as for trivial subpaths)
2528     sp_nodepath_cleanup(nodepath);
2530     sp_nodepath_update_handles(nodepath);
2532     // if the entire nodepath is removed, delete the selected object.
2533     if (nodepath->subpaths == NULL ||
2534         sp_nodepath_get_node_count(nodepath) < 2) {
2535         SPDocument *document = sp_desktop_document (nodepath->desktop);
2536         sp_selection_delete(nodepath->desktop);
2537         sp_document_done (document, SP_VERB_CONTEXT_NODE,
2538                           _("Delete nodes"));
2539         return;
2540     }
2542     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2544     sp_nodepath_update_statusbar(nodepath);
2547 /**
2548  * Delete one or more segments between two selected nodes.
2549  * This is the code for 'split'.
2550  */
2551 void
2552 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2554    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2555    Inkscape::NodePath::Node *curr, *next;     //Iterators
2557     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2559     if (g_list_length(nodepath->selected) != 2) {
2560         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2561                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2562         return;
2563     }
2565     //Selected nodes, not inclusive
2566    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2567    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2569     if ( ( a==b)                       ||  //same node
2570          (a->subpath  != b->subpath )  ||  //not the same path
2571          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2572          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2573     {
2574         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2575                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2576         return;
2577     }
2579     //###########################################
2580     //# BEGIN EDITS
2581     //###########################################
2582     //##################################
2583     //# CLOSED PATH
2584     //##################################
2585     if (a->subpath->closed) {
2588         gboolean reversed = FALSE;
2590         //Since we can go in a circle, we need to find the shorter distance.
2591         //  a->b or b->a
2592         start = end = NULL;
2593         int distance    = 0;
2594         int minDistance = 0;
2595         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2596             if (curr==b) {
2597                 //printf("a to b:%d\n", distance);
2598                 start = a;//go from a to b
2599                 end   = b;
2600                 minDistance = distance;
2601                 //printf("A to B :\n");
2602                 break;
2603             }
2604             distance++;
2605         }
2607         //try again, the other direction
2608         distance = 0;
2609         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2610             if (curr==a) {
2611                 //printf("b to a:%d\n", distance);
2612                 if (distance < minDistance) {
2613                     start    = b;  //we go from b to a
2614                     end      = a;
2615                     reversed = TRUE;
2616                     //printf("B to A\n");
2617                 }
2618                 break;
2619             }
2620             distance++;
2621         }
2624         //Copy everything from 'end' to 'start' to a new subpath
2625        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2626         for (curr=end ; curr ; curr=curr->n.other) {
2627             NRPathcode code = (NRPathcode) curr->code;
2628             if (curr == end)
2629                 code = NR_MOVETO;
2630             sp_nodepath_node_new(t, NULL,
2631                                  (Inkscape::NodePath::NodeType)curr->type, code,
2632                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2633             if (curr == start)
2634                 break;
2635         }
2636         sp_nodepath_subpath_destroy(a->subpath);
2639     }
2643     //##################################
2644     //# OPEN PATH
2645     //##################################
2646     else {
2648         //We need to get the direction of the list between A and B
2649         //Can we walk from a to b?
2650         start = end = NULL;
2651         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2652             if (curr==b) {
2653                 start = a;  //did it!  we go from a to b
2654                 end   = b;
2655                 //printf("A to B\n");
2656                 break;
2657             }
2658         }
2659         if (!start) {//didn't work?  let's try the other direction
2660             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2661                 if (curr==a) {
2662                     start = b;  //did it!  we go from b to a
2663                     end   = a;
2664                     //printf("B to A\n");
2665                     break;
2666                 }
2667             }
2668         }
2669         if (!start) {
2670             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2671                                                      _("Cannot find path between nodes."));
2672             return;
2673         }
2677         //Copy everything after 'end' to a new subpath
2678        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2679         for (curr=end ; curr ; curr=curr->n.other) {
2680             NRPathcode code = (NRPathcode) curr->code;
2681             if (curr == end)
2682                 code = NR_MOVETO;
2683             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2684                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2685         }
2687         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2688         for (curr = start->n.other ; curr  ; curr=next) {
2689             next = curr->n.other;
2690             sp_nodepath_node_destroy(curr);
2691         }
2693     }
2694     //###########################################
2695     //# END EDITS
2696     //###########################################
2698     //clean up the nodepath (such as for trivial subpaths)
2699     sp_nodepath_cleanup(nodepath);
2701     sp_nodepath_update_handles(nodepath);
2703     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2705     sp_nodepath_update_statusbar(nodepath);
2708 /**
2709  * Call sp_nodepath_set_line() for all selected segments.
2710  */
2711 void
2712 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2714     if (nodepath == NULL) return;
2716     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2717        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2718         g_assert(n->selected);
2719         if (n->p.other && n->p.other->selected) {
2720             sp_nodepath_set_line_type(n, code);
2721         }
2722     }
2724     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2727 /**
2728  * Call sp_nodepath_convert_node_type() for all selected nodes.
2729  */
2730 void
2731 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2733     if (nodepath == NULL) return;
2735     if (nodepath->straight_path) return; // don't change type when it is a straight path!
2737     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2738         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2739     }
2741     sp_nodepath_update_repr(nodepath, _("Change node type"));
2744 /**
2745  * Change select status of node, update its own and neighbour handles.
2746  */
2747 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2749     node->selected = selected;
2751     if (selected) {
2752         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2753         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2754         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2755         sp_knot_update_ctrl(node->knot);
2756     } else {
2757         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2758         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2759         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2760         sp_knot_update_ctrl(node->knot);
2761     }
2763     sp_node_update_handles(node);
2764     if (node->n.other) sp_node_update_handles(node->n.other);
2765     if (node->p.other) sp_node_update_handles(node->p.other);
2768 /**
2769 \brief Select a node
2770 \param node     The node to select
2771 \param incremental   If true, add to selection, otherwise deselect others
2772 \param override   If true, always select this node, otherwise toggle selected status
2773 */
2774 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2776     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2778     if (incremental) {
2779         if (override) {
2780             if (!g_list_find(nodepath->selected, node)) {
2781                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2782             }
2783             sp_node_set_selected(node, TRUE);
2784         } else { // toggle
2785             if (node->selected) {
2786                 g_assert(g_list_find(nodepath->selected, node));
2787                 nodepath->selected = g_list_remove(nodepath->selected, node);
2788             } else {
2789                 g_assert(!g_list_find(nodepath->selected, node));
2790                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2791             }
2792             sp_node_set_selected(node, !node->selected);
2793         }
2794     } else {
2795         sp_nodepath_deselect(nodepath);
2796         nodepath->selected = g_list_prepend(nodepath->selected, node);
2797         sp_node_set_selected(node, TRUE);
2798     }
2800     sp_nodepath_update_statusbar(nodepath);
2804 /**
2805 \brief Deselect all nodes in the nodepath
2806 */
2807 void
2808 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2810     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2812     while (nodepath->selected) {
2813         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2814         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2815     }
2816     sp_nodepath_update_statusbar(nodepath);
2819 /**
2820 \brief Select or invert selection of all nodes in the nodepath
2821 */
2822 void
2823 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2825     if (!nodepath) return;
2827     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2828        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2829         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2830            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2831            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2832         }
2833     }
2836 /**
2837  * If nothing selected, does the same as sp_nodepath_select_all();
2838  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2839  * (i.e., similar to "select all in layer", with the "selected" subpaths
2840  * being treated as "layers" in the path).
2841  */
2842 void
2843 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2845     if (!nodepath) return;
2847     if (g_list_length (nodepath->selected) == 0) {
2848         sp_nodepath_select_all (nodepath, invert);
2849         return;
2850     }
2852     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2853     GSList *subpaths = NULL;
2855     for (GList *l = copy; l != NULL; l = l->next) {
2856         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2857         Inkscape::NodePath::SubPath *subpath = n->subpath;
2858         if (!g_slist_find (subpaths, subpath))
2859             subpaths = g_slist_prepend (subpaths, subpath);
2860     }
2862     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2863         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2864         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2865             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2866             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2867         }
2868     }
2870     g_slist_free (subpaths);
2871     g_list_free (copy);
2874 /**
2875  * \brief Select the node after the last selected; if none is selected,
2876  * select the first within path.
2877  */
2878 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2880     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2882    Inkscape::NodePath::Node *last = NULL;
2883     if (nodepath->selected) {
2884         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2885            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2886             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2887             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2888                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2889                 if (node->selected) {
2890                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2891                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2892                             if (spl->next) { // there's a next subpath
2893                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2894                                 last = subpath_next->first;
2895                             } else if (spl->prev) { // there's a previous subpath
2896                                 last = NULL; // to be set later to the first node of first subpath
2897                             } else {
2898                                 last = node->n.other;
2899                             }
2900                         } else {
2901                             last = node->n.other;
2902                         }
2903                     } else {
2904                         if (node->n.other) {
2905                             last = node->n.other;
2906                         } else {
2907                             if (spl->next) { // there's a next subpath
2908                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2909                                 last = subpath_next->first;
2910                             } else if (spl->prev) { // there's a previous subpath
2911                                 last = NULL; // to be set later to the first node of first subpath
2912                             } else {
2913                                 last = (Inkscape::NodePath::Node *) subpath->first;
2914                             }
2915                         }
2916                     }
2917                 }
2918             }
2919         }
2920         sp_nodepath_deselect(nodepath);
2921     }
2923     if (last) { // there's at least one more node after selected
2924         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2925     } else { // no more nodes, select the first one in first subpath
2926        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2927         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2928     }
2931 /**
2932  * \brief Select the node before the first selected; if none is selected,
2933  * select the last within path
2934  */
2935 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2937     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2939    Inkscape::NodePath::Node *last = NULL;
2940     if (nodepath->selected) {
2941         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2942            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2943             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2944                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2945                 if (node->selected) {
2946                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2947                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2948                             if (spl->prev) { // there's a prev subpath
2949                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2950                                 last = subpath_prev->last;
2951                             } else if (spl->next) { // there's a next subpath
2952                                 last = NULL; // to be set later to the last node of last subpath
2953                             } else {
2954                                 last = node->p.other;
2955                             }
2956                         } else {
2957                             last = node->p.other;
2958                         }
2959                     } else {
2960                         if (node->p.other) {
2961                             last = node->p.other;
2962                         } else {
2963                             if (spl->prev) { // there's a prev subpath
2964                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2965                                 last = subpath_prev->last;
2966                             } else if (spl->next) { // there's a next subpath
2967                                 last = NULL; // to be set later to the last node of last subpath
2968                             } else {
2969                                 last = (Inkscape::NodePath::Node *) subpath->last;
2970                             }
2971                         }
2972                     }
2973                 }
2974             }
2975         }
2976         sp_nodepath_deselect(nodepath);
2977     }
2979     if (last) { // there's at least one more node before selected
2980         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2981     } else { // no more nodes, select the last one in last subpath
2982         GList *spl = g_list_last(nodepath->subpaths);
2983        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2984         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2985     }
2988 /**
2989  * \brief Select all nodes that are within the rectangle.
2990  */
2991 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2993     if (!incremental) {
2994         sp_nodepath_deselect(nodepath);
2995     }
2997     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2998        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2999         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3000            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3002             if (b.contains(node->pos)) {
3003                 sp_nodepath_node_select(node, TRUE, TRUE);
3004             }
3005         }
3006     }
3010 void
3011 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3013     g_assert (n);
3014     g_assert (nodepath);
3015     g_assert (n->subpath->nodepath == nodepath);
3017     if (g_list_length (nodepath->selected) == 0) {
3018         if (grow > 0) {
3019             sp_nodepath_node_select(n, TRUE, TRUE);
3020         }
3021         return;
3022     }
3024     if (g_list_length (nodepath->selected) == 1) {
3025         if (grow < 0) {
3026             sp_nodepath_deselect (nodepath);
3027             return;
3028         }
3029     }
3031         double n_sel_range = 0, p_sel_range = 0;
3032             Inkscape::NodePath::Node *farthest_n_node = n;
3033             Inkscape::NodePath::Node *farthest_p_node = n;
3035         // Calculate ranges
3036         {
3037             double n_range = 0, p_range = 0;
3038             bool n_going = true, p_going = true;
3039             Inkscape::NodePath::Node *n_node = n;
3040             Inkscape::NodePath::Node *p_node = n;
3041             do {
3042                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
3043                 if (n_node && n_going)
3044                     n_node = n_node->n.other;
3045                 if (n_node == NULL) {
3046                     n_going = false;
3047                 } else {
3048                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
3049                     if (n_node->selected) {
3050                         n_sel_range = n_range;
3051                         farthest_n_node = n_node;
3052                     }
3053                     if (n_node == p_node) {
3054                         n_going = false;
3055                         p_going = false;
3056                     }
3057                 }
3058                 if (p_node && p_going)
3059                     p_node = p_node->p.other;
3060                 if (p_node == NULL) {
3061                     p_going = false;
3062                 } else {
3063                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
3064                     if (p_node->selected) {
3065                         p_sel_range = p_range;
3066                         farthest_p_node = p_node;
3067                     }
3068                     if (p_node == n_node) {
3069                         n_going = false;
3070                         p_going = false;
3071                     }
3072                 }
3073             } while (n_going || p_going);
3074         }
3076     if (grow > 0) {
3077         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
3078                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
3079         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
3080                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
3081         }
3082     } else {
3083         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
3084                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
3085         } else if (farthest_p_node && farthest_p_node->selected) {
3086                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
3087         }
3088     }
3091 void
3092 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
3094     g_assert (n);
3095     g_assert (nodepath);
3096     g_assert (n->subpath->nodepath == nodepath);
3098     if (g_list_length (nodepath->selected) == 0) {
3099         if (grow > 0) {
3100             sp_nodepath_node_select(n, TRUE, TRUE);
3101         }
3102         return;
3103     }
3105     if (g_list_length (nodepath->selected) == 1) {
3106         if (grow < 0) {
3107             sp_nodepath_deselect (nodepath);
3108             return;
3109         }
3110     }
3112     Inkscape::NodePath::Node *farthest_selected = NULL;
3113     double farthest_dist = 0;
3115     Inkscape::NodePath::Node *closest_unselected = NULL;
3116     double closest_dist = NR_HUGE;
3118     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3119        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3120         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3121            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3122            if (node == n)
3123                continue;
3124            if (node->selected) {
3125                if (NR::L2(node->pos - n->pos) > farthest_dist) {
3126                    farthest_dist = NR::L2(node->pos - n->pos);
3127                    farthest_selected = node;
3128                }
3129            } else {
3130                if (NR::L2(node->pos - n->pos) < closest_dist) {
3131                    closest_dist = NR::L2(node->pos - n->pos);
3132                    closest_unselected = node;
3133                }
3134            }
3135         }
3136     }
3138     if (grow > 0) {
3139         if (closest_unselected) {
3140             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
3141         }
3142     } else {
3143         if (farthest_selected) {
3144             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
3145         }
3146     }
3150 /**
3151 \brief  Saves all nodes' and handles' current positions in their origin members
3152 */
3153 void
3154 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
3156     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3157        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3158         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3159            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
3160            n->origin = n->pos;
3161            n->p.origin = n->p.pos;
3162            n->n.origin = n->n.pos;
3163         }
3164     }
3167 /**
3168 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
3169 */
3170 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
3172     GList *r = NULL;
3173     if (nodepath->selected) {
3174         guint i = 0;
3175         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3176             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3177             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3178                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3179                 i++;
3180                 if (node->selected) {
3181                     r = g_list_append(r, GINT_TO_POINTER(i));
3182                 }
3183             }
3184         }
3185     }
3186     return r;
3189 /**
3190 \brief  Restores selection by selecting nodes whose positions are in the list
3191 */
3192 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
3194     sp_nodepath_deselect(nodepath);
3196     guint i = 0;
3197     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3198        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3199         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
3200            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
3201             i++;
3202             if (g_list_find(r, GINT_TO_POINTER(i))) {
3203                 sp_nodepath_node_select(node, TRUE, TRUE);
3204             }
3205         }
3206     }
3210 /**
3211 \brief Adjusts handle according to node type and line code.
3212 */
3213 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
3215     g_assert(node);
3217    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
3218    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
3220    // nothing to do if we are an end node
3221     if (me->other == NULL) return;
3222     if (other->other == NULL) return;
3224     // nothing to do if we are a cusp node
3225     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3227     // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
3228     NRPathcode mecode;
3229     if (which_adjust == 1) {
3230         mecode = (NRPathcode)me->other->code;
3231     } else {
3232         mecode = (NRPathcode)node->code;
3233     }
3234     if (mecode == NR_LINETO) return;
3236     if (sp_node_side_is_line(node, other)) {
3237         // other is a line, and we are either smooth or symm
3238        Inkscape::NodePath::Node *othernode = other->other;
3239         double len = NR::L2(me->pos - node->pos);
3240         NR::Point delta = node->pos - othernode->pos;
3241         double linelen = NR::L2(delta);
3242         if (linelen < 1e-18)
3243             return;
3244         me->pos = node->pos + (len / linelen)*delta;
3245         return;
3246     }
3248     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3249         // symmetrize 
3250         me->pos = 2 * node->pos - other->pos;
3251         return;
3252     } else {
3253         // smoothify
3254         double len = NR::L2(me->pos - node->pos);
3255         NR::Point delta = other->pos - node->pos;
3256         double otherlen = NR::L2(delta);
3257         if (otherlen < 1e-18) return;
3258         me->pos = node->pos - (len / otherlen) * delta;
3259     }
3262 /**
3263  \brief Adjusts both handles according to node type and line code
3264  */
3265 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
3267     g_assert(node);
3269     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
3271     /* we are either smooth or symm */
3273     if (node->p.other == NULL) return;
3274     if (node->n.other == NULL) return;
3276     if (sp_node_side_is_line(node, &node->p)) {
3277         sp_node_adjust_handle(node, 1);
3278         return;
3279     }
3281     if (sp_node_side_is_line(node, &node->n)) {
3282         sp_node_adjust_handle(node, -1);
3283         return;
3284     }
3286     /* both are curves */
3287     NR::Point const delta( node->n.pos - node->p.pos );
3289     if (node->type == Inkscape::NodePath::NODE_SYMM) {
3290         node->p.pos = node->pos - delta / 2;
3291         node->n.pos = node->pos + delta / 2;
3292         return;
3293     }
3295     /* We are smooth */
3296     double plen = NR::L2(node->p.pos - node->pos);
3297     if (plen < 1e-18) return;
3298     double nlen = NR::L2(node->n.pos - node->pos);
3299     if (nlen < 1e-18) return;
3300     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
3301     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
3304 /**
3305  * Node event callback.
3306  */
3307 static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
3309     gboolean ret = FALSE;
3310     switch (event->type) {
3311         case GDK_ENTER_NOTIFY:
3312             Inkscape::NodePath::Path::active_node = n;
3313             break;
3314         case GDK_LEAVE_NOTIFY:
3315             Inkscape::NodePath::Path::active_node = NULL;
3316             break;
3317         case GDK_SCROLL:
3318             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
3319                 switch (event->scroll.direction) {
3320                     case GDK_SCROLL_UP:
3321                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3322                         break;
3323                     case GDK_SCROLL_DOWN:
3324                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3325                         break;
3326                     default:
3327                         break;
3328                 }
3329                 ret = TRUE;
3330             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
3331                 switch (event->scroll.direction) {
3332                     case GDK_SCROLL_UP:
3333                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3334                         break;
3335                     case GDK_SCROLL_DOWN:
3336                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3337                         break;
3338                     default:
3339                         break;
3340                 }
3341                 ret = TRUE;
3342             }
3343             break;
3344         case GDK_KEY_PRESS:
3345             switch (get_group0_keyval (&event->key)) {
3346                 case GDK_space:
3347                     if (event->key.state & GDK_BUTTON1_MASK) {
3348                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3349                         stamp_repr(nodepath);
3350                         ret = TRUE;
3351                     }
3352                     break;
3353                 case GDK_Page_Up:
3354                     if (event->key.state & GDK_CONTROL_MASK) {
3355                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
3356                     } else {
3357                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
3358                     }
3359                     break;
3360                 case GDK_Page_Down:
3361                     if (event->key.state & GDK_CONTROL_MASK) {
3362                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
3363                     } else {
3364                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
3365                     }
3366                     break;
3367                 default:
3368                     break;
3369             }
3370             break;
3371         default:
3372             break;
3373     }
3375     return ret;
3378 /**
3379  * Handle keypress on node; directly called.
3380  */
3381 gboolean node_key(GdkEvent *event)
3383     Inkscape::NodePath::Path *np;
3385     // there is no way to verify nodes so set active_node to nil when deleting!!
3386     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
3388     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
3389         gint ret = FALSE;
3390         switch (get_group0_keyval (&event->key)) {
3391             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
3392             case GDK_BackSpace:
3393                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
3394                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
3395                 sp_nodepath_update_repr(np, _("Delete node"));
3396                 Inkscape::NodePath::Path::active_node = NULL;
3397                 ret = TRUE;
3398                 break;
3399             case GDK_c:
3400                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
3401                 ret = TRUE;
3402                 break;
3403             case GDK_s:
3404                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
3405                 ret = TRUE;
3406                 break;
3407             case GDK_y:
3408                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
3409                 ret = TRUE;
3410                 break;
3411             case GDK_b:
3412                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3413                 ret = TRUE;
3414                 break;
3415         }
3416         return ret;
3417     }
3418     return FALSE;
3421 /**
3422  * Mouseclick on node callback.
3423  */
3424 static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
3426    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3428     if (state & GDK_CONTROL_MASK) {
3429         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3431         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3432             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3433                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3434             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3435                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3436             } else {
3437                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3438             }
3439             sp_nodepath_update_repr(nodepath, _("Change node type"));
3440             sp_nodepath_update_statusbar(nodepath);
3442         } else { //ctrl+alt+click: delete node
3443             GList *node_to_delete = NULL;
3444             node_to_delete = g_list_append(node_to_delete, n);
3445             sp_node_delete_preserve(node_to_delete);
3446         }
3448     } else {
3449         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3450     }
3453 /**
3454  * Mouse grabbed node callback.
3455  */
3456 static void node_grabbed(SPKnot */*knot*/, guint state, gpointer data)
3458    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3460     if (!n->selected) {
3461         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3462     }
3464     n->is_dragging = true;
3465     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3467     sp_nodepath_remember_origins (n->subpath->nodepath);
3470 /**
3471  * Mouse ungrabbed node callback.
3472  */
3473 static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
3475    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3477    n->dragging_out = NULL;
3478    n->is_dragging = false;
3479    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3481    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3484 /**
3485  * The point on a line, given by its angle, closest to the given point.
3486  * \param p  A point.
3487  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3488  * \param closest  Pointer to the point struct where the result is stored.
3489  * \todo FIXME: use dot product perhaps?
3490  */
3491 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3493     if (a == HUGE_VAL) { // vertical
3494         *closest = NR::Point(0, (*p)[NR::Y]);
3495     } else {
3496         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3497         (*closest)[NR::Y] = a * (*closest)[NR::X];
3498     }
3501 /**
3502  * Distance from the point to a line given by its angle.
3503  * \param p  A point.
3504  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3505  */
3506 static double point_line_distance(NR::Point *p, double a)
3508     NR::Point c;
3509     point_line_closest(p, a, &c);
3510     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]));
3513 /**
3514  * Callback for node "request" signal.
3515  * \todo fixme: This goes to "moved" event? (lauris)
3516  */
3517 static gboolean
3518 node_request(SPKnot */*knot*/, NR::Point *p, guint state, gpointer data)
3520     double yn, xn, yp, xp;
3521     double an, ap, na, pa;
3522     double d_an, d_ap, d_na, d_pa;
3523     gboolean collinear = FALSE;
3524     NR::Point c;
3525     NR::Point pr;
3527     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3529     n->subpath->nodepath->desktop->snapindicator->remove_snappoint();
3531     // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3532     if ( (!n->subpath->nodepath->straight_path) &&
3533          ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
3534            || n->dragging_out ) )
3535     {
3536        NR::Point mouse = (*p);
3538        if (!n->dragging_out) {
3539            // This is the first drag-out event; find out which handle to drag out
3540            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3541            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3543            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3544                return FALSE;
3546            Inkscape::NodePath::NodeSide *opposite;
3547            if (appr_p > appr_n) { // closer to p
3548                n->dragging_out = &n->p;
3549                opposite = &n->n;
3550                n->code = NR_CURVETO;
3551            } else if (appr_p < appr_n) { // closer to n
3552                n->dragging_out = &n->n;
3553                opposite = &n->p;
3554                n->n.other->code = NR_CURVETO;
3555            } else { // p and n nodes are the same
3556                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3557                    n->dragging_out = &n->p;
3558                    opposite = &n->n;
3559                    n->code = NR_CURVETO;
3560                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3561                    n->dragging_out = &n->n;
3562                    opposite = &n->p;
3563                    n->n.other->code = NR_CURVETO;
3564                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3565                    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);
3566                    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);
3567                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3568                        n->dragging_out = &n->n;
3569                        opposite = &n->p;
3570                        n->n.other->code = NR_CURVETO;
3571                    } else { // closer to other's n handle
3572                        n->dragging_out = &n->p;
3573                        opposite = &n->n;
3574                        n->code = NR_CURVETO;
3575                    }
3576                }
3577            }
3579            // if there's another handle, make sure the one we drag out starts parallel to it
3580            if (opposite->pos != n->pos) {
3581                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3582            }
3584            // knots might not be created yet!
3585            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3586            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3587        }
3589        // pass this on to the handle-moved callback
3590        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3591        sp_node_update_handles(n);
3592        return TRUE;
3593    }
3595     if (state & GDK_CONTROL_MASK) { // constrained motion
3597         // calculate relative distances of handles
3598         // n handle:
3599         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3600         xn = n->n.pos[NR::X] - n->pos[NR::X];
3601         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3602         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3603             if (n->n.other) { // if there is the next point
3604                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3605                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3606                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3607             }
3608         }
3609         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3610         if (yn < 0) { xn = -xn; yn = -yn; }
3612         // p handle:
3613         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3614         xp = n->p.pos[NR::X] - n->pos[NR::X];
3615         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3616         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3617             if (n->p.other) {
3618                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3619                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3620                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3621             }
3622         }
3623         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3624         if (yp < 0) { xp = -xp; yp = -yp; }
3626         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3627             // sliding on handles, only if at least one of the handles is non-vertical
3628             // (otherwise it's the same as ctrl+drag anyway)
3630             // calculate angles of the handles
3631             if (xn == 0) {
3632                 if (yn == 0) { // no handle, consider it the continuation of the other one
3633                     an = 0;
3634                     collinear = TRUE;
3635                 }
3636                 else an = 0; // vertical; set the angle to horizontal
3637             } else an = yn/xn;
3639             if (xp == 0) {
3640                 if (yp == 0) { // no handle, consider it the continuation of the other one
3641                     ap = an;
3642                 }
3643                 else ap = 0; // vertical; set the angle to horizontal
3644             } else  ap = yp/xp;
3646             if (collinear) an = ap;
3648             // angles of the perpendiculars; HUGE_VAL means vertical
3649             if (an == 0) na = HUGE_VAL; else na = -1/an;
3650             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3652             // mouse point relative to the node's original pos
3653             pr = (*p) - n->origin;
3655             // distances to the four lines (two handles and two perpendiculars)
3656             d_an = point_line_distance(&pr, an);
3657             d_na = point_line_distance(&pr, na);
3658             d_ap = point_line_distance(&pr, ap);
3659             d_pa = point_line_distance(&pr, pa);
3661             // find out which line is the closest, save its closest point in c
3662             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3663                 point_line_closest(&pr, an, &c);
3664             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3665                 point_line_closest(&pr, ap, &c);
3666             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3667                 point_line_closest(&pr, na, &c);
3668             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3669                 point_line_closest(&pr, pa, &c);
3670             }
3672             // move the node to the closest point
3673             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3674                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3675                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y], 
3676                                             true);
3678         } else {  // constraining to hor/vert
3680             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3681                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3682                                                 (*p)[NR::X] - n->pos[NR::X], 
3683                                                 n->origin[NR::Y] - n->pos[NR::Y],
3684                                                 true, 
3685                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::X]));
3686             } else { // snap to vert
3687                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3688                                                 n->origin[NR::X] - n->pos[NR::X],
3689                                                 (*p)[NR::Y] - n->pos[NR::Y],
3690                                                 true,
3691                                                 true, Inkscape::Snapper::ConstraintLine(component_vectors[NR::Y]));
3692             }
3693         }
3694     } else { // move freely
3695         if (n->is_dragging) {
3696             if (state & GDK_MOD1_MASK) { // sculpt
3697                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3698             } else {
3699                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3700                                             (*p)[NR::X] - n->pos[NR::X],
3701                                             (*p)[NR::Y] - n->pos[NR::Y],
3702                                             (state & GDK_SHIFT_MASK) == 0);
3703             }
3704         }
3705     }
3707     n->subpath->nodepath->desktop->scroll_to_point(p);
3709     return TRUE;
3712 /**
3713  * Node handle clicked callback.
3714  */
3715 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3717    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3719     if (state & GDK_CONTROL_MASK) { // "delete" handle
3720         if (n->p.knot == knot) {
3721             n->p.pos = n->pos;
3722         } else if (n->n.knot == knot) {
3723             n->n.pos = n->pos;
3724         }
3725         sp_node_update_handles(n);
3726         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3727         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3728         sp_nodepath_update_statusbar(nodepath);
3730     } else { // just select or add to selection, depending in Shift
3731         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3732     }
3735 /**
3736  * Node handle grabbed callback.
3737  */
3738 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3740    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3742     if (!n->selected) {
3743         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3744     }
3746     // remember the origin point of the handle
3747     if (n->p.knot == knot) {
3748         n->p.origin_radial = n->p.pos - n->pos;
3749     } else if (n->n.knot == knot) {
3750         n->n.origin_radial = n->n.pos - n->pos;
3751     } else {
3752         g_assert_not_reached();
3753     }
3755     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3758 /**
3759  * Node handle ungrabbed callback.
3760  */
3761 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3763    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3765     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3766     if (n->p.knot == knot) {
3767         n->p.origin_radial.a = 0;
3768         sp_knot_set_position(knot, n->p.pos, state);
3769     } else if (n->n.knot == knot) {
3770         n->n.origin_radial.a = 0;
3771         sp_knot_set_position(knot, n->n.pos, state);
3772     } else {
3773         g_assert_not_reached();
3774     }
3776     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3779 /**
3780  * Node handle "request" signal callback.
3781  */
3782 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3784     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3786     Inkscape::NodePath::NodeSide *me, *opposite;
3787     gint which;
3788     if (n->p.knot == knot) {
3789         me = &n->p;
3790         opposite = &n->n;
3791         which = -1;
3792     } else if (n->n.knot == knot) {
3793         me = &n->n;
3794         opposite = &n->p;
3795         which = 1;
3796     } else {
3797         me = opposite = NULL;
3798         which = 0;
3799         g_assert_not_reached();
3800     }
3802     SPDesktop *desktop = n->subpath->nodepath->desktop;
3803     SnapManager &m = desktop->namedview->snap_manager;
3804     m.setup(desktop, true, n->subpath->nodepath->item);
3805     Inkscape::SnappedPoint s;
3806     
3807     if ((state & GDK_SHIFT_MASK) != 0) {
3808         // We will not try to snap when the shift-key is pressed
3809         // so remove the old snap indicator and don't wait for it to time-out  
3810         desktop->snapindicator->remove_snappoint();     
3811     }
3813     Inkscape::NodePath::Node *othernode = opposite->other;
3814     if (othernode) {
3815         if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
3816             /* We are smooth node adjacent with line */
3817             NR::Point const delta = *p - n->pos;
3818             NR::Coord const len = NR::L2(delta);
3819             Inkscape::NodePath::Node *othernode = opposite->other;
3820             NR::Point const ndelta = n->pos - othernode->pos;
3821             NR::Coord const linelen = NR::L2(ndelta);
3822             if (len > NR_EPSILON && linelen > NR_EPSILON) {
3823                 NR::Coord const scal = dot(delta, ndelta) / linelen;
3824                 (*p) = n->pos + (scal / linelen) * ndelta;
3825             }
3826             if ((state & GDK_SHIFT_MASK) == 0) {
3827                 s = m.constrainedSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p), Inkscape::Snapper::ConstraintLine(*p, ndelta));
3828             }
3829         } else {
3830                 if ((state & GDK_SHIFT_MASK) == 0) {
3831                         s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p));
3832                 }
3833         }
3834     } else {
3835         if ((state & GDK_SHIFT_MASK) == 0) {
3836                 s = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, to_2geom(*p));
3837         }
3838     }
3839     
3840     Geom::Point pt2g = *p;
3841     s.getPoint(pt2g);
3842     *p = pt2g;
3843     
3844     sp_node_adjust_handle(n, -which);
3846     return FALSE;
3849 /**
3850  * Node handle moved callback.
3851  */
3852 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3854    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3856    Inkscape::NodePath::NodeSide *me;
3857    Inkscape::NodePath::NodeSide *other;
3858     if (n->p.knot == knot) {
3859         me = &n->p;
3860         other = &n->n;
3861     } else if (n->n.knot == knot) {
3862         me = &n->n;
3863         other = &n->p;
3864     } else {
3865         me = NULL;
3866         other = NULL;
3867         g_assert_not_reached();
3868     }
3870     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3871     Radial rme(me->pos - n->pos);
3872     Radial rother(other->pos - n->pos);
3873     Radial rnew(*p - n->pos);
3875     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3876         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3877         /* 0 interpreted as "no snapping". */
3879         // 1. Snap to the closest PI/snaps angle, starting from zero.
3880         double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3882         // 2. Snap to the original angle, its opposite and perpendiculars
3883         if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
3884             /* The closest PI/2 angle, starting from original angle */
3885             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3887             // Snap to the closest.
3888             a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3889                        ? a_snapped
3890                        : a_ortho );
3891         }
3893         // 3. Snap to the angle of the opposite line, if any
3894         Inkscape::NodePath::Node *othernode = other->other;
3895         if (othernode) {
3896             NR::Point other_to_snap(0,0);
3897             if (sp_node_side_is_line(n, other)) {
3898                 other_to_snap = othernode->pos - n->pos;
3899             } else {
3900                 other_to_snap = other->pos - n->pos;
3901             }
3902             if (NR::L2(other_to_snap) > 1e-3) {
3903                 Radial rother_to_snap(other_to_snap);
3904                 /* The closest PI/2 angle, starting from the angle of the opposite line segment */
3905                 double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
3907                 // Snap to the closest.
3908                 a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
3909                        ? a_snapped
3910                        : a_oppo );
3911             }
3912         }
3914         rnew.a = a_snapped;
3915     }
3917     if (state & GDK_MOD1_MASK) {
3918         // lock handle length
3919         rnew.r = me->origin_radial.r;
3920     }
3922     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3923         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3924         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3925         rother.a += rnew.a - rme.a;
3926         other->pos = NR::Point(rother) + n->pos;
3927         if (other->knot) {
3928             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3929             sp_knot_moveto(other->knot, other->pos);
3930         }
3931     }
3933     me->pos = NR::Point(rnew) + n->pos;
3934     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3936     // move knot, but without emitting the signal:
3937     // we cannot emit a "moved" signal because we're now processing it
3938     sp_knot_moveto(me->knot, me->pos);
3940     update_object(n->subpath->nodepath);
3942     /* status text */
3943     SPDesktop *desktop = n->subpath->nodepath->desktop;
3944     if (!desktop) return;
3945     SPEventContext *ec = desktop->event_context;
3946     if (!ec) return;
3948     Inkscape::MessageContext *mc = get_message_context(ec);
3950     if (!mc) return;
3952     double degrees = 180 / M_PI * rnew.a;
3953     if (degrees > 180) degrees -= 360;
3954     if (degrees < -180) degrees += 360;
3955     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3956         degrees = angle_to_compass (degrees);
3958     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3960     mc->setF(Inkscape::IMMEDIATE_MESSAGE,
3961          _("<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);
3963     g_string_free(length, TRUE);
3966 /**
3967  * Node handle event callback.
3968  */
3969 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3971     gboolean ret = FALSE;
3972     switch (event->type) {
3973         case GDK_KEY_PRESS:
3974             switch (get_group0_keyval (&event->key)) {
3975                 case GDK_space:
3976                     if (event->key.state & GDK_BUTTON1_MASK) {
3977                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3978                         stamp_repr(nodepath);
3979                         ret = TRUE;
3980                     }
3981                     break;
3982                 default:
3983                     break;
3984             }
3985             break;
3986         case GDK_ENTER_NOTIFY:
3987             // we use an experimentally determined threshold that seems to work fine
3988             if (NR::L2(n->pos - knot->pos) < 0.75)
3989                 Inkscape::NodePath::Path::active_node = n;
3990             break;
3991         case GDK_LEAVE_NOTIFY:
3992             // we use an experimentally determined threshold that seems to work fine
3993             if (NR::L2(n->pos - knot->pos) < 0.75)
3994                 Inkscape::NodePath::Path::active_node = NULL;
3995             break;
3996         default:
3997             break;
3998     }
4000     return ret;
4003 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
4004                                  Radial &rme, Radial &rother, gboolean const both)
4006     rme.a += angle;
4007     if ( both
4008          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4009          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4010     {
4011         rother.a += angle;
4012     }
4015 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
4016                                         Radial &rme, Radial &rother, gboolean const both)
4018     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
4020     gdouble r;
4021     if ( both
4022          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4023          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4024     {
4025         r = MAX(rme.r, rother.r);
4026     } else {
4027         r = rme.r;
4028     }
4030     gdouble const weird_angle = atan2(norm_angle, r);
4031 /* Bulia says norm_angle is just the visible distance that the
4032  * object's end must travel on the screen.  Left as 'angle' for want of
4033  * a better name.*/
4035     rme.a += weird_angle;
4036     if ( both
4037          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
4038          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
4039     {
4040         rother.a += weird_angle;
4041     }
4044 /**
4045  * Rotate one node.
4046  */
4047 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
4049     Inkscape::NodePath::NodeSide *me, *other;
4050     bool both = false;
4052     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4053     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4055     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4056         me = &(n->p);
4057         other = &(n->n);
4058     } else if (!n->p.other) {
4059         me = &(n->n);
4060         other = &(n->p);
4061     } else {
4062         if (which > 0) { // right handle
4063             if (xn > xp) {
4064                 me = &(n->n);
4065                 other = &(n->p);
4066             } else {
4067                 me = &(n->p);
4068                 other = &(n->n);
4069             }
4070         } else if (which < 0){ // left handle
4071             if (xn <= xp) {
4072                 me = &(n->n);
4073                 other = &(n->p);
4074             } else {
4075                 me = &(n->p);
4076                 other = &(n->n);
4077             }
4078         } else { // both handles
4079             me = &(n->n);
4080             other = &(n->p);
4081             both = true;
4082         }
4083     }
4085     Radial rme(me->pos - n->pos);
4086     Radial rother(other->pos - n->pos);
4088     if (screen) {
4089         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
4090     } else {
4091         node_rotate_one_internal (*n, angle, rme, rother, both);
4092     }
4094     me->pos = n->pos + NR::Point(rme);
4096     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
4097         other->pos =  n->pos + NR::Point(rother);
4098     }
4100     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
4101     // so here we just move all the knots without emitting move signals, for speed
4102     sp_node_update_handles(n, false);
4105 /**
4106  * Rotate selected nodes.
4107  */
4108 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
4110     if (!nodepath || !nodepath->selected) return;
4112     if (g_list_length(nodepath->selected) == 1) {
4113        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4114         node_rotate_one (n, angle, which, screen);
4115     } else {
4116        // rotate as an object:
4118         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4119         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4120         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4121             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4122             box.expandTo (n->pos); // contain all selected nodes
4123         }
4125         gdouble rot;
4126         if (screen) {
4127             gdouble const zoom = nodepath->desktop->current_zoom();
4128             gdouble const zmove = angle / zoom;
4129             gdouble const r = NR::L2(box.max() - box.midpoint());
4130             rot = atan2(zmove, r);
4131         } else {
4132             rot = angle;
4133         }
4135         NR::Point rot_center;
4136         if (Inkscape::NodePath::Path::active_node == NULL)
4137             rot_center = box.midpoint();
4138         else
4139             rot_center = Inkscape::NodePath::Path::active_node->pos;
4141         NR::Matrix t =
4142             NR::Matrix (NR::translate(-rot_center)) *
4143             NR::Matrix (NR::rotate(rot)) *
4144             NR::Matrix (NR::translate(rot_center));
4146         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4147             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4148             n->pos *= t;
4149             n->n.pos *= t;
4150             n->p.pos *= t;
4151             sp_node_update_handles(n, false);
4152         }
4153     }
4155     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
4158 /**
4159  * Scale one node.
4160  */
4161 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
4163     bool both = false;
4164     Inkscape::NodePath::NodeSide *me, *other;
4166     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
4167     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
4169     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
4170         me = &(n->p);
4171         other = &(n->n);
4172         n->code = NR_CURVETO;
4173     } else if (!n->p.other) {
4174         me = &(n->n);
4175         other = &(n->p);
4176         if (n->n.other)
4177             n->n.other->code = NR_CURVETO;
4178     } else {
4179         if (which > 0) { // right handle
4180             if (xn > xp) {
4181                 me = &(n->n);
4182                 other = &(n->p);
4183                 if (n->n.other)
4184                     n->n.other->code = NR_CURVETO;
4185             } else {
4186                 me = &(n->p);
4187                 other = &(n->n);
4188                 n->code = NR_CURVETO;
4189             }
4190         } else if (which < 0){ // left handle
4191             if (xn <= xp) {
4192                 me = &(n->n);
4193                 other = &(n->p);
4194                 if (n->n.other)
4195                     n->n.other->code = NR_CURVETO;
4196             } else {
4197                 me = &(n->p);
4198                 other = &(n->n);
4199                 n->code = NR_CURVETO;
4200             }
4201         } else { // both handles
4202             me = &(n->n);
4203             other = &(n->p);
4204             both = true;
4205             n->code = NR_CURVETO;
4206             if (n->n.other)
4207                 n->n.other->code = NR_CURVETO;
4208         }
4209     }
4211     Radial rme(me->pos - n->pos);
4212     Radial rother(other->pos - n->pos);
4214     rme.r += grow;
4215     if (rme.r < 0) rme.r = 0;
4216     if (rme.a == HUGE_VAL) {
4217         if (me->other) { // if direction is unknown, initialize it towards the next node
4218             Radial rme_next(me->other->pos - n->pos);
4219             rme.a = rme_next.a;
4220         } else { // if there's no next, initialize to 0
4221             rme.a = 0;
4222         }
4223     }
4224     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4225         rother.r += grow;
4226         if (rother.r < 0) rother.r = 0;
4227         if (rother.a == HUGE_VAL) {
4228             rother.a = rme.a + M_PI;
4229         }
4230     }
4232     me->pos = n->pos + NR::Point(rme);
4234     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
4235         other->pos = n->pos + NR::Point(rother);
4236     }
4238     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
4239     // so here we just move all the knots without emitting move signals, for speed
4240     sp_node_update_handles(n, false);
4243 /**
4244  * Scale selected nodes.
4245  */
4246 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4248     if (!nodepath || !nodepath->selected) return;
4250     if (g_list_length(nodepath->selected) == 1) {
4251         // scale handles of the single selected node
4252         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4253         node_scale_one (n, grow, which);
4254     } else {
4255         // scale nodes as an "object":
4257         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4258         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4259         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4260             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4261             box.expandTo (n->pos); // contain all selected nodes
4262         }
4264         double scale = (box.maxExtent() + grow)/box.maxExtent();
4266         NR::Point scale_center;
4267         if (Inkscape::NodePath::Path::active_node == NULL)
4268             scale_center = box.midpoint();
4269         else
4270             scale_center = Inkscape::NodePath::Path::active_node->pos;
4272         NR::Matrix t =
4273             NR::Matrix (NR::translate(-scale_center)) *
4274             NR::Matrix (NR::scale(scale, scale)) *
4275             NR::Matrix (NR::translate(scale_center));
4277         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4278             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4279             n->pos *= t;
4280             n->n.pos *= t;
4281             n->p.pos *= t;
4282             sp_node_update_handles(n, false);
4283         }
4284     }
4286     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
4289 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
4291     if (!nodepath) return;
4292     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
4295 /**
4296  * Flip selected nodes horizontally/vertically.
4297  */
4298 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, boost::optional<NR::Point> center)
4300     if (!nodepath || !nodepath->selected) return;
4302     if (g_list_length(nodepath->selected) == 1 && !center) {
4303         // flip handles of the single selected node
4304         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
4305         double temp = n->p.pos[axis];
4306         n->p.pos[axis] = n->n.pos[axis];
4307         n->n.pos[axis] = temp;
4308         sp_node_update_handles(n, false);
4309     } else {
4310         // scale nodes as an "object":
4312         Geom::Rect box = sp_node_selected_bbox (nodepath);
4313         if (!center) {
4314             center = box.midpoint();
4315         }
4316         NR::Matrix t =
4317             NR::Matrix (NR::translate(- *center)) *
4318             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
4319             NR::Matrix (NR::translate(*center));
4321         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4322             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4323             n->pos *= t;
4324             n->n.pos *= t;
4325             n->p.pos *= t;
4326             sp_node_update_handles(n, false);
4327         }
4328     }
4330     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
4333 Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
4335     g_assert (nodepath->selected);
4337     Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
4338     Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
4339     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
4340         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
4341         box.expandTo (n->pos); // contain all selected nodes
4342     }
4343     return box;
4346 //-----------------------------------------------
4347 /**
4348  * Return new subpath under given nodepath.
4349  */
4350 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
4352     g_assert(nodepath);
4353     g_assert(nodepath->desktop);
4355    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
4357     s->nodepath = nodepath;
4358     s->closed = FALSE;
4359     s->nodes = NULL;
4360     s->first = NULL;
4361     s->last = NULL;
4363     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
4364     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
4365     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
4367     return s;
4370 /**
4371  * Destroy nodes in subpath, then subpath itself.
4372  */
4373 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
4375     g_assert(subpath);
4376     g_assert(subpath->nodepath);
4377     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
4379     while (subpath->nodes) {
4380         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
4381     }
4383     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
4385     g_free(subpath);
4388 /**
4389  * Link head to tail in subpath.
4390  */
4391 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
4393     g_assert(!sp->closed);
4394     g_assert(sp->last != sp->first);
4395     g_assert(sp->first->code == NR_MOVETO);
4397     sp->closed = TRUE;
4399     //Link the head to the tail
4400     sp->first->p.other = sp->last;
4401     sp->last->n.other  = sp->first;
4402     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
4403     sp->first          = sp->last;
4405     //Remove the extra end node
4406     sp_nodepath_node_destroy(sp->last->n.other);
4409 /**
4410  * Open closed (loopy) subpath at node.
4411  */
4412 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
4414     g_assert(sp->closed);
4415     g_assert(n->subpath == sp);
4416     g_assert(sp->first == sp->last);
4418     /* We create new startpoint, current node will become last one */
4420    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
4421                                                 &n->pos, &n->pos, &n->n.pos);
4424     sp->closed        = FALSE;
4426     //Unlink to make a head and tail
4427     sp->first         = new_path;
4428     sp->last          = n;
4429     n->n.other        = NULL;
4430     new_path->p.other = NULL;
4433 /**
4434  * Return new node in subpath with given properties.
4435  * \param pos Position of node.
4436  * \param ppos Handle position in previous direction
4437  * \param npos Handle position in previous direction
4438  */
4439 Inkscape::NodePath::Node *
4440 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)
4442     g_assert(sp);
4443     g_assert(sp->nodepath);
4444     g_assert(sp->nodepath->desktop);
4446     if (nodechunk == NULL)
4447         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
4449     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
4451     n->subpath  = sp;
4453     if (type != Inkscape::NodePath::NODE_NONE) {
4454         // use the type from sodipodi:nodetypes
4455         n->type = type;
4456     } else {
4457         if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
4458             // points are (almost) collinear
4459             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
4460                 // endnode, or a node with a retracted handle
4461                 n->type = Inkscape::NodePath::NODE_CUSP;
4462             } else {
4463                 n->type = Inkscape::NodePath::NODE_SMOOTH;
4464             }
4465         } else {
4466             n->type = Inkscape::NodePath::NODE_CUSP;
4467         }
4468     }
4470     n->code     = code;
4471     n->selected = FALSE;
4472     n->pos      = *pos;
4473     n->p.pos    = *ppos;
4474     n->n.pos    = *npos;
4476     n->dragging_out = NULL;
4478     Inkscape::NodePath::Node *prev;
4479     if (next) {
4480         //g_assert(g_list_find(sp->nodes, next));
4481         prev = next->p.other;
4482     } else {
4483         prev = sp->last;
4484     }
4486     if (prev)
4487         prev->n.other = n;
4488     else
4489         sp->first = n;
4491     if (next)
4492         next->p.other = n;
4493     else
4494         sp->last = n;
4496     n->p.other = prev;
4497     n->n.other = next;
4499     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"));
4500     sp_knot_set_position(n->knot, *pos, 0);
4502     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4503     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4504     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4505     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4506     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4507     sp_knot_update_ctrl(n->knot);
4509     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4510     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4511     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4512     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4513     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4514     sp_knot_show(n->knot);
4516     // We only create handle knots and lines on demand
4517     n->p.knot = NULL;
4518     n->p.line = NULL;
4519     n->n.knot = NULL;
4520     n->n.line = NULL;
4522     sp->nodes = g_list_prepend(sp->nodes, n);
4524     return n;
4527 /**
4528  * Destroy node and its knots, link neighbors in subpath.
4529  */
4530 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4532     g_assert(node);
4533     g_assert(node->subpath);
4534     g_assert(SP_IS_KNOT(node->knot));
4536    Inkscape::NodePath::SubPath *sp = node->subpath;
4538     if (node->selected) { // first, deselect
4539         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4540         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4541     }
4543     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4545     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4546     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4547     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4548     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4549     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4550     g_object_unref(G_OBJECT(node->knot));
4552     if (node->p.knot) {
4553         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4554         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4555         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4556         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4557         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4558         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4559         g_object_unref(G_OBJECT(node->p.knot));
4560         node->p.knot = NULL;
4561     }
4563     if (node->n.knot) {
4564         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4565         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4566         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4567         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4568         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4569         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4570         g_object_unref(G_OBJECT(node->n.knot));
4571         node->n.knot = NULL;
4572     }
4574     if (node->p.line)
4575         gtk_object_destroy(GTK_OBJECT(node->p.line));
4576     if (node->n.line)
4577         gtk_object_destroy(GTK_OBJECT(node->n.line));
4579     if (sp->nodes) { // there are others nodes on the subpath
4580         if (sp->closed) {
4581             if (sp->first == node) {
4582                 g_assert(sp->last == node);
4583                 sp->first = node->n.other;
4584                 sp->last = sp->first;
4585             }
4586             node->p.other->n.other = node->n.other;
4587             node->n.other->p.other = node->p.other;
4588         } else {
4589             if (sp->first == node) {
4590                 sp->first = node->n.other;
4591                 sp->first->code = NR_MOVETO;
4592             }
4593             if (sp->last == node) sp->last = node->p.other;
4594             if (node->p.other) node->p.other->n.other = node->n.other;
4595             if (node->n.other) node->n.other->p.other = node->p.other;
4596         }
4597     } else { // this was the last node on subpath
4598         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4599     }
4601     g_mem_chunk_free(nodechunk, node);
4604 /**
4605  * Returns one of the node's two sides.
4606  * \param which Indicates which side.
4607  * \return Pointer to previous node side if which==-1, next if which==1.
4608  */
4609 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4611     g_assert(node);
4612     Inkscape::NodePath::NodeSide * result = 0;
4613     switch (which) {
4614         case -1:
4615             result = &node->p;
4616             break;
4617         case 1:
4618             result = &node->n;
4619             break;
4620         default:
4621             g_assert_not_reached();
4622     }
4624     return result;
4627 /**
4628  * Return the other side of the node, given one of its sides.
4629  */
4630 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4632     g_assert(node);
4633     Inkscape::NodePath::NodeSide *result = 0;
4635     if (me == &node->p) {
4636         result = &node->n;
4637     } else if (me == &node->n) {
4638         result = &node->p;
4639     } else {
4640         g_assert_not_reached();
4641     }
4643     return result;
4646 /**
4647  * Return NRPathcode on the given side of the node.
4648  */
4649 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4651     g_assert(node);
4653     NRPathcode result = NR_END;
4654     if (me == &node->p) {
4655         if (node->p.other) {
4656             result = (NRPathcode)node->code;
4657         } else {
4658             result = NR_MOVETO;
4659         }
4660     } else if (me == &node->n) {
4661         if (node->n.other) {
4662             result = (NRPathcode)node->n.other->code;
4663         } else {
4664             result = NR_MOVETO;
4665         }
4666     } else {
4667         g_assert_not_reached();
4668     }
4670     return result;
4673 /**
4674  * Return node with the given index
4675  */
4676 Inkscape::NodePath::Node *
4677 sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
4679     Inkscape::NodePath::Node *e = NULL;
4681     if (!nodepath) {
4682         return e;
4683     }
4685     //find segment
4686     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4688         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4689         int n = g_list_length(sp->nodes);
4690         if (sp->closed) {
4691             n++;
4692         }
4694         //if the piece belongs to this subpath grab it
4695         //otherwise move onto the next subpath
4696         if (index < n) {
4697             e = sp->first;
4698             for (int i = 0; i < index; ++i) {
4699                 e = e->n.other;
4700             }
4701             break;
4702         } else {
4703             if (sp->closed) {
4704                 index -= (n+1);
4705             } else {
4706                 index -= n;
4707             }
4708         }
4709     }
4711     return e;
4714 /**
4715  * Returns plain text meaning of node type.
4716  */
4717 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4719     unsigned retracted = 0;
4720     bool endnode = false;
4722     for (int which = -1; which <= 1; which += 2) {
4723         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4724         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4725             retracted ++;
4726         if (!side->other)
4727             endnode = true;
4728     }
4730     if (retracted == 0) {
4731         if (endnode) {
4732                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4733                 return _("end node");
4734         } else {
4735             switch (node->type) {
4736                 case Inkscape::NodePath::NODE_CUSP:
4737                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4738                     return _("cusp");
4739                 case Inkscape::NodePath::NODE_SMOOTH:
4740                     // TRANSLATORS: "smooth" is an adjective here
4741                     return _("smooth");
4742                 case Inkscape::NodePath::NODE_SYMM:
4743                     return _("symmetric");
4744             }
4745         }
4746     } else if (retracted == 1) {
4747         if (endnode) {
4748             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4749             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4750         } else {
4751             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4752         }
4753     } else {
4754         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4755     }
4757     return NULL;
4760 /**
4761  * Handles content of statusbar as long as node tool is active.
4762  */
4763 void
4764 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4766     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");
4767     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4769     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4770     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4771     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4772     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4774     SPDesktop *desktop = NULL;
4775     if (nodepath) {
4776         desktop = nodepath->desktop;
4777     } else {
4778         desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
4779     }
4781     SPEventContext *ec = desktop->event_context;
4782     if (!ec) return;
4784     Inkscape::MessageContext *mc = get_message_context(ec);
4785     if (!mc) return;
4787     inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
4789     if (selected_nodes == 0) {
4790         Inkscape::Selection *sel = desktop->selection;
4791         if (!sel || sel->isEmpty()) {
4792             mc->setF(Inkscape::NORMAL_MESSAGE,
4793                      _("Select a single object to edit its nodes or handles."));
4794         } else {
4795             if (nodepath) {
4796             mc->setF(Inkscape::NORMAL_MESSAGE,
4797                      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.",
4798                               "<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.",
4799                               total_nodes),
4800                      total_nodes);
4801             } else {
4802                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4803                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4804                 } else {
4805                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4806                 }
4807             }
4808         }
4809     } else if (nodepath && selected_nodes == 1) {
4810         mc->setF(Inkscape::NORMAL_MESSAGE,
4811                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4812                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4813                           total_nodes),
4814                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4815     } else {
4816         if (selected_subpaths > 1) {
4817             mc->setF(Inkscape::NORMAL_MESSAGE,
4818                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4819                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4820                               total_nodes),
4821                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4822         } else {
4823             mc->setF(Inkscape::NORMAL_MESSAGE,
4824                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4825                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4826                               total_nodes),
4827                      selected_nodes, total_nodes, when_selected);
4828         }
4829     }
4832 /*
4833  * returns a *copy* of the curve of that object.
4834  */
4835 SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
4836     if (!object)
4837         return NULL;
4839     SPCurve *curve = NULL;
4840     if (SP_IS_PATH(object)) {
4841         SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
4842         curve = curve_new->copy();
4843     } else if ( IS_LIVEPATHEFFECT(object) && key) {
4844         const gchar *svgd = object->repr->attribute(key);
4845         if (svgd) {
4846             Geom::PathVector pv = sp_svg_read_pathv(svgd);
4847             SPCurve *curve_new = new SPCurve(pv);
4848             if (curve_new) {
4849                 curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
4850             }
4851         }
4852     }
4854     return curve;
4857 void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
4858     if (!np || !np->object || !curve)
4859         return;
4861     if (SP_IS_PATH(np->object)) {
4862         if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
4863             sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
4864         } else {
4865             sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
4866         }
4867     } else if ( IS_LIVEPATHEFFECT(np->object) ) {
4868         Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( LIVEPATHEFFECT(np->object)->lpe->getParameter(np->repr_key) );
4869         if (pathparam) {
4870             pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
4871             np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
4872         }
4873     }
4876 /**
4877 SPCanvasItem *
4878 sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
4879     return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
4881 **/
4883 /**
4884 SPCanvasItem *
4885 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const SPItem *item, guint32 color = 0xff0000ff) {
4886     SPCurve *flash_curve = curve->copy();
4887     Geom::Matrix i2d = item ? sp_item_i2d_affine(item) : Geom::identity();
4888     flash_curve->transform(i2d);
4889     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4890     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4891     // unless we also flash the nodes...
4892     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4893     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4894     sp_canvas_item_show(canvasitem);
4895     flash_curve->unref();
4896     return canvasitem;
4899 SPCanvasItem *
4900 sp_nodepath_generate_helperpath(SPDesktop *desktop, SPPath *path) {
4901     return sp_nodepath_generate_helperpath(desktop, sp_path_get_curve_for_edit(path), SP_ITEM(path),
4902                                            prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff));
4904 **/
4906 SPCanvasItem *
4907 sp_nodepath_helperpath_from_path(SPDesktop *desktop, SPPath *path) {
4908     SPCurve *flash_curve = sp_path_get_curve_for_edit(path)->copy();
4909     Geom::Matrix i2d = sp_item_i2d_affine(SP_ITEM(path));
4910     flash_curve->transform(i2d);
4911     SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
4912     // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
4913     // unless we also flash the nodes...
4914     guint32 color = prefs_get_int_attribute("tools.nodes", "highlight_color", 0xff0000ff);
4915     sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
4916     sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
4917     sp_canvas_item_show(canvasitem);
4918     flash_curve->unref();
4919     return canvasitem;
4922 // TODO: Merge this with sp_nodepath_make_helper_item()!
4923 void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
4924     np->show_helperpath = show;
4926     if (show) {
4927         SPCurve *helper_curve = np->curve->copy();
4928         helper_curve->transform(np->i2d);
4929         if (!np->helper_path) {
4930             //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
4932             np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
4933             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);
4934             sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
4935             sp_canvas_item_move_to_z(np->helper_path, 0);
4936             sp_canvas_item_show(np->helper_path);
4937         } else {
4938             sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
4939         }
4940         helper_curve->unref();
4941     } else {
4942         if (np->helper_path) {
4943             GtkObject *temp = np->helper_path;
4944             np->helper_path = NULL;
4945             gtk_object_destroy(temp);
4946         }
4947     }
4950 /* sp_nodepath_make_straight_path:
4951  *   Prevents user from curving the path by dragging a segment or activating handles etc.
4952  *   The resulting path is a linear interpolation between nodal points, with only straight segments.
4953  * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
4954  */
4955 void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
4956     np->straight_path = true;
4957     np->show_handles = false;
4958     g_message("add code to make the path straight.");
4959     // do sp_nodepath_convert_node_type on all nodes?
4960     // coding tip: search for this text : "Make selected segments lines"
4963 /*
4964   Local Variables:
4965   mode:c++
4966   c-file-style:"stroustrup"
4967   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4968   indent-tabs-mode:nil
4969   fill-column:99
4970   End:
4971 */
4972 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :