Code

Initial reworking of forced redraw to work better with interruptible display
[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/curve.h"
19 #include "display/sp-ctrlline.h"
20 #include "display/sodipodi-ctrl.h"
21 #include <glibmm/i18n.h>
22 #include "libnr/n-art-bpath.h"
23 #include "helper/units.h"
24 #include "knot.h"
25 #include "inkscape.h"
26 #include "document.h"
27 #include "sp-namedview.h"
28 #include "desktop.h"
29 #include "desktop-handles.h"
30 #include "snap.h"
31 #include "message-stack.h"
32 #include "message-context.h"
33 #include "node-context.h"
34 #include "selection-chemistry.h"
35 #include "selection.h"
36 #include "xml/repr.h"
37 #include "prefs-utils.h"
38 #include "sp-metrics.h"
39 #include "sp-path.h"
40 #include "libnr/nr-matrix-ops.h"
41 #include "splivarot.h"
42 #include "svg/svg.h"
43 #include "verbs.h"
44 #include "display/bezier-utils.h"
45 #include <vector>
46 #include <algorithm>
48 class NR::Matrix;
50 /// \todo
51 /// evil evil evil. FIXME: conflict of two different Path classes!
52 /// There is a conflict in the namespace between two classes named Path.
53 /// #include "sp-flowtext.h"
54 /// #include "sp-flowregion.h"
56 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
57 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
58 GType sp_flowregion_get_type (void);
59 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
60 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
61 GType sp_flowtext_get_type (void);
62 // end evil workaround
64 #include "helper/stlport.h"
67 /// \todo fixme: Implement these via preferences */
69 #define NODE_FILL          0xbfbfbf00
70 #define NODE_STROKE        0x000000ff
71 #define NODE_FILL_HI       0xff000000
72 #define NODE_STROKE_HI     0x000000ff
73 #define NODE_FILL_SEL      0x0000ffff
74 #define NODE_STROKE_SEL    0x000000ff
75 #define NODE_FILL_SEL_HI   0xff000000
76 #define NODE_STROKE_SEL_HI 0x000000ff
77 #define KNOT_FILL          0xffffffff
78 #define KNOT_STROKE        0x000000ff
79 #define KNOT_FILL_HI       0xff000000
80 #define KNOT_STROKE_HI     0x000000ff
82 static GMemChunk *nodechunk = NULL;
84 /* Creation from object */
86 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
87 static gchar *parse_nodetypes(gchar const *types, gint length);
89 /* Object updating */
91 static void stamp_repr(Inkscape::NodePath::Path *np);
92 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
93 static gchar *create_typestr(Inkscape::NodePath::Path *np);
95 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
97 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
99 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
101 /* Adjust handle placement, if the node or the other handle is moved */
102 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
103 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
105 /* Node event callbacks */
106 static void node_clicked(SPKnot *knot, guint state, gpointer data);
107 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
108 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
109 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
111 /* Handle event callbacks */
112 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
113 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
114 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
115 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
116 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
117 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
119 /* Constructors and destructors */
121 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
122 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
123 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
124 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
125 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
126                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
127 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
129 /* Helpers */
131 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
132 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
133 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
135 // active_node indicates mouseover node
136 static Inkscape::NodePath::Node *active_node = NULL;
138 /**
139  * \brief Creates new nodepath from item
140  */
141 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool show_handles)
143     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
145     /** \todo
146      * FIXME: remove this. We don't want to edit paths inside flowtext.
147      * Instead we will build our flowtext with cloned paths, so that the
148      * real paths are outside the flowtext and thus editable as usual.
149      */
150     if (SP_IS_FLOWTEXT(item)) {
151         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
152             if SP_IS_FLOWREGION(child) {
153                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
154                 if (grandchild && SP_IS_PATH(grandchild)) {
155                     item = SP_ITEM(grandchild);
156                     break;
157                 }
158             }
159         }
160     }
162     if (!SP_IS_PATH(item))
163         return NULL;
164     SPPath *path = SP_PATH(item);
165     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
166     if (curve == NULL)
167         return NULL;
169     NArtBpath *bpath = sp_curve_first_bpath(curve);
170     gint length = curve->end;
171     if (length == 0)
172         return NULL; // prevent crash for one-node paths
174     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
175     gchar *typestr = parse_nodetypes(nodetypes, length);
177     //Create new nodepath
178     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
179     if (!np)
180         return NULL;
182     // Set defaults
183     np->desktop     = desktop;
184     np->path        = path;
185     np->subpaths    = NULL;
186     np->selected    = NULL;
187     np->nodeContext = NULL; //Let the context that makes this set it
188     np->livarot_path = NULL;
189     np->local_change = 0;
190     np->show_handles = show_handles;
192     // we need to update item's transform from the repr here,
193     // because they may be out of sync when we respond
194     // to a change in repr by regenerating nodepath     --bb
195     sp_object_read_attr(SP_OBJECT(item), "transform");
197     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
198     np->d2i  = np->i2d.inverse();
199     np->repr = repr;
201     // create the subpath(s) from the bpath
202     NArtBpath *b = bpath;
203     while (b->code != NR_END) {
204         b = subpath_from_bpath(np, b, typestr + (b - bpath));
205     }
207     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
208     np->subpaths = g_list_reverse(np->subpaths);
210     g_free(typestr);
211     sp_curve_unref(curve);
213     // create the livarot representation from the same item
214     np->livarot_path = Path_for_item(item, true, true);
215     if (np->livarot_path)
216         np->livarot_path->ConvertWithBackData(0.01);
218     return np;
221 /**
222  * Destroys nodepath's subpaths, then itself, also tell context about it.
223  */
224 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
226     if (!np)  //soft fail, like delete
227         return;
229     while (np->subpaths) {
230         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
231     }
233     //Inform the context that made me, if any, that I am gone.
234     if (np->nodeContext)
235         np->nodeContext->nodepath = NULL;
237     g_assert(!np->selected);
239     if (np->livarot_path) {
240         delete np->livarot_path;
241         np->livarot_path = NULL;
242     }
244     np->desktop = NULL;
246     g_free(np);
250 /**
251  *  Return the node count of a given NodeSubPath.
252  */
253 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
255     if (!subpath)
256         return 0;
257     gint nodeCount = g_list_length(subpath->nodes);
258     return nodeCount;
261 /**
262  *  Return the node count of a given NodePath.
263  */
264 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
266     if (!np)
267         return 0;
268     gint nodeCount = 0;
269     for (GList *item = np->subpaths ; item ; item=item->next) {
270        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
271         nodeCount += g_list_length(subpath->nodes);
272     }
273     return nodeCount;
276 /**
277  *  Return the subpath count of a given NodePath.
278  */
279 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
281     if (!np)
282         return 0;
283     return g_list_length (np->subpaths);
286 /**
287  *  Return the selected node count of a given NodePath.
288  */
289 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
291     if (!np)
292         return 0;
293     return g_list_length (np->selected);
296 /**
297  *  Return the number of subpaths where nodes are selected in a given NodePath.
298  */
299 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
301     if (!np)
302         return 0;
303     if (!np->selected)
304         return 0;
305     if (!np->selected->next)
306         return 1;
307     gint count = 0;
308     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
309         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
310         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
311             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
312             if (node->selected) {
313                 count ++;
314                 break;
315             }
316         }
317     }
318     return count;
320  
321 /**
322  * Clean up a nodepath after editing.
323  *
324  * Currently we are deleting trivial subpaths.
325  */
326 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
328     GList *badSubPaths = NULL;
330     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
331     for (GList *l = nodepath->subpaths; l ; l=l->next) {
332        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
333        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
334             badSubPaths = g_list_append(badSubPaths, sp);
335     }
337     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
338     //also removes the subpath from nodepath->subpaths
339     for (GList *l = badSubPaths; l ; l=l->next) {
340        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
341         sp_nodepath_subpath_destroy(sp);
342     }
344     g_list_free(badSubPaths);
347 /**
348  * Create new nodepath from b, make it subpath of np.
349  * \param t The node type.
350  * \todo Fixme: t should be a proper type, rather than gchar
351  */
352 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
354     NR::Point ppos, pos, npos;
356     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
358     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
359     bool const closed = (b->code == NR_MOVETO);
361     pos = NR::Point(b->x3, b->y3) * np->i2d;
362     if (b[1].code == NR_CURVETO) {
363         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
364     } else {
365         npos = pos;
366     }
367     Inkscape::NodePath::Node *n;
368     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
369     g_assert(sp->first == n);
370     g_assert(sp->last  == n);
372     b++;
373     t++;
374     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
375         pos = NR::Point(b->x3, b->y3) * np->i2d;
376         if (b->code == NR_CURVETO) {
377             ppos = NR::Point(b->x2, b->y2) * np->i2d;
378         } else {
379             ppos = pos;
380         }
381         if (b[1].code == NR_CURVETO) {
382             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
383         } else {
384             npos = pos;
385         }
386         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
387         b++;
388         t++;
389     }
391     if (closed) sp_nodepath_subpath_close(sp);
393     return b;
396 /**
397  * Convert from sodipodi:nodetypes to new style type string.
398  */
399 static gchar *parse_nodetypes(gchar const *types, gint length)
401     g_assert(length > 0);
403     gchar *typestr = g_new(gchar, length + 1);
405     gint pos = 0;
407     if (types) {
408         for (gint i = 0; types[i] && ( i < length ); i++) {
409             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
410             if (types[i] != '\0') {
411                 switch (types[i]) {
412                     case 's':
413                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
414                         break;
415                     case 'z':
416                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
417                         break;
418                     case 'c':
419                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
420                         break;
421                     default:
422                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
423                         break;
424                 }
425             }
426         }
427     }
429     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
431     return typestr;
434 /**
435  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
436  * updated but repr is not (for speed). Used during curve and node drag.
437  */
438 static void update_object(Inkscape::NodePath::Path *np)
440     g_assert(np);
442     SPCurve *curve = create_curve(np);
444     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
446     sp_curve_unref(curve);
449 /**
450  * Update XML path node with data from path object.
451  */
452 static void update_repr_internal(Inkscape::NodePath::Path *np)
454     g_assert(np);
456     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
458     SPCurve *curve = create_curve(np);
459     gchar *typestr = create_typestr(np);
460     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
462     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
463         np->local_change++;
464         repr->setAttribute("d", svgpath);
465     }
467     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
468         np->local_change++;
469         repr->setAttribute("sodipodi:nodetypes", typestr);
470     }
472     g_free(svgpath);
473     g_free(typestr);
474     sp_curve_unref(curve);
477 /**
478  * Update XML path node with data from path object, commit changes forever.
479  */
480 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
482     //fixme: np can be NULL, so check before proceeding
483     g_return_if_fail(np != NULL);
485     update_repr_internal(np);
486     //sp_canvas_end_forced_full_redraws(np->desktop->canvas);
487     
488     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
489                      annotation);
491     if (np->livarot_path) {
492         delete np->livarot_path;
493         np->livarot_path = NULL;
494     }
496     if (np->path && SP_IS_ITEM(np->path)) {
497         np->livarot_path = Path_for_item (np->path, true, true);
498         if (np->livarot_path)
499             np->livarot_path->ConvertWithBackData(0.01);
500     }
504 /**
505  * Update XML path node with data from path object, commit changes with undo.
506  */
507 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
509     update_repr_internal(np);
510     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
511                            annotation);
513     if (np->livarot_path) {
514         delete np->livarot_path;
515         np->livarot_path = NULL;
516     }
518     if (np->path && SP_IS_ITEM(np->path)) {
519         np->livarot_path = Path_for_item (np->path, true, true);
520         if (np->livarot_path)
521             np->livarot_path->ConvertWithBackData(0.01);
522     }
525 /**
526  * Make duplicate of path, replace corresponding XML node in tree, commit.
527  */
528 static void stamp_repr(Inkscape::NodePath::Path *np)
530     g_assert(np);
532     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
533     Inkscape::XML::Node *new_repr = old_repr->duplicate();
535     // remember the position of the item
536     gint pos = old_repr->position();
537     // remember parent
538     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
540     SPCurve *curve = create_curve(np);
541     gchar *typestr = create_typestr(np);
543     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
545     new_repr->setAttribute("d", svgpath);
546     new_repr->setAttribute("sodipodi:nodetypes", typestr);
548     // add the new repr to the parent
549     parent->appendChild(new_repr);
550     // move to the saved position
551     new_repr->setPosition(pos > 0 ? pos : 0);
553     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
554                      _("Stamp"));
556     Inkscape::GC::release(new_repr);
557     g_free(svgpath);
558     g_free(typestr);
559     sp_curve_unref(curve);
562 /**
563  * Create curve from path.
564  */
565 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
567     SPCurve *curve = sp_curve_new();
569     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
570        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
571         sp_curve_moveto(curve,
572                         sp->first->pos * np->d2i);
573        Inkscape::NodePath::Node *n = sp->first->n.other;
574         while (n) {
575             NR::Point const end_pt = n->pos * np->d2i;
576             switch (n->code) {
577                 case NR_LINETO:
578                     sp_curve_lineto(curve, end_pt);
579                     break;
580                 case NR_CURVETO:
581                     sp_curve_curveto(curve,
582                                      n->p.other->n.pos * np->d2i,
583                                      n->p.pos * np->d2i,
584                                      end_pt);
585                     break;
586                 default:
587                     g_assert_not_reached();
588                     break;
589             }
590             if (n != sp->last) {
591                 n = n->n.other;
592             } else {
593                 n = NULL;
594             }
595         }
596         if (sp->closed) {
597             sp_curve_closepath(curve);
598         }
599     }
601     return curve;
604 /**
605  * Convert path type string to sodipodi:nodetypes style.
606  */
607 static gchar *create_typestr(Inkscape::NodePath::Path *np)
609     gchar *typestr = g_new(gchar, 32);
610     gint len = 32;
611     gint pos = 0;
613     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
614        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
616         if (pos >= len) {
617             typestr = g_renew(gchar, typestr, len + 32);
618             len += 32;
619         }
621         typestr[pos++] = 'c';
623        Inkscape::NodePath::Node *n;
624         n = sp->first->n.other;
625         while (n) {
626             gchar code;
628             switch (n->type) {
629                 case Inkscape::NodePath::NODE_CUSP:
630                     code = 'c';
631                     break;
632                 case Inkscape::NodePath::NODE_SMOOTH:
633                     code = 's';
634                     break;
635                 case Inkscape::NodePath::NODE_SYMM:
636                     code = 'z';
637                     break;
638                 default:
639                     g_assert_not_reached();
640                     code = '\0';
641                     break;
642             }
644             if (pos >= len) {
645                 typestr = g_renew(gchar, typestr, len + 32);
646                 len += 32;
647             }
649             typestr[pos++] = code;
651             if (n != sp->last) {
652                 n = n->n.other;
653             } else {
654                 n = NULL;
655             }
656         }
657     }
659     if (pos >= len) {
660         typestr = g_renew(gchar, typestr, len + 1);
661         len += 1;
662     }
664     typestr[pos++] = '\0';
666     return typestr;
669 /**
670  * Returns current path in context.
671  */
672 static Inkscape::NodePath::Path *sp_nodepath_current()
674     if (!SP_ACTIVE_DESKTOP) {
675         return NULL;
676     }
678     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
680     if (!SP_IS_NODE_CONTEXT(event_context)) {
681         return NULL;
682     }
684     return SP_NODE_CONTEXT(event_context)->nodepath;
689 /**
690  \brief Fills node and handle positions for three nodes, splitting line
691   marked by end at distance t.
692  */
693 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
695     g_assert(new_path != NULL);
696     g_assert(end      != NULL);
698     g_assert(end->p.other == new_path);
699    Inkscape::NodePath::Node *start = new_path->p.other;
700     g_assert(start);
702     if (end->code == NR_LINETO) {
703         new_path->type =Inkscape::NodePath::NODE_CUSP;
704         new_path->code = NR_LINETO;
705         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
706     } else {
707         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
708         new_path->code = NR_CURVETO;
709         gdouble s      = 1 - t;
710         for (int dim = 0; dim < 2; dim++) {
711             NR::Coord const f000 = start->pos[dim];
712             NR::Coord const f001 = start->n.pos[dim];
713             NR::Coord const f011 = end->p.pos[dim];
714             NR::Coord const f111 = end->pos[dim];
715             NR::Coord const f00t = s * f000 + t * f001;
716             NR::Coord const f01t = s * f001 + t * f011;
717             NR::Coord const f11t = s * f011 + t * f111;
718             NR::Coord const f0tt = s * f00t + t * f01t;
719             NR::Coord const f1tt = s * f01t + t * f11t;
720             NR::Coord const fttt = s * f0tt + t * f1tt;
721             start->n.pos[dim]    = f00t;
722             new_path->p.pos[dim] = f0tt;
723             new_path->pos[dim]   = fttt;
724             new_path->n.pos[dim] = f1tt;
725             end->p.pos[dim]      = f11t;
726         }
727     }
730 /**
731  * Adds new node on direct line between two nodes, activates handles of all
732  * three nodes.
733  */
734 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
736     g_assert(end);
737     g_assert(end->subpath);
738     g_assert(g_list_find(end->subpath->nodes, end));
740    Inkscape::NodePath::Node *start = end->p.other;
741     g_assert( start->n.other == end );
742    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
743                                                end,
744                                               Inkscape::NodePath::NODE_SMOOTH,
745                                                (NRPathcode)end->code,
746                                                &start->pos, &start->pos, &start->n.pos);
747     sp_nodepath_line_midpoint(newnode, end, t);
749     sp_node_update_handles(start);
750     sp_node_update_handles(newnode);
751     sp_node_update_handles(end);
753     return newnode;
756 /**
757 \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
758 */
759 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
761     g_assert(node);
762     g_assert(node->subpath);
763     g_assert(g_list_find(node->subpath->nodes, node));
765    Inkscape::NodePath::SubPath *sp = node->subpath;
766     Inkscape::NodePath::Path *np    = sp->nodepath;
768     if (sp->closed) {
769         sp_nodepath_subpath_open(sp, node);
770         return sp->first;
771     } else {
772         // no break for end nodes
773         if (node == sp->first) return NULL;
774         if (node == sp->last ) return NULL;
776         // create a new subpath
777        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
779         // duplicate the break node as start of the new subpath
780        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
782         while (node->n.other) { // copy the remaining nodes into the new subpath
783            Inkscape::NodePath::Node *n  = node->n.other;
784            Inkscape::NodePath::Node *nn = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
785             if (n->selected) {
786                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
787             }
788             sp_nodepath_node_destroy(n); // remove the point on the original subpath
789         }
791         return newnode;
792     }
795 /**
796  * Duplicate node and connect to neighbours.
797  */
798 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
800     g_assert(node);
801     g_assert(node->subpath);
802     g_assert(g_list_find(node->subpath->nodes, node));
804    Inkscape::NodePath::SubPath *sp = node->subpath;
806     NRPathcode code = (NRPathcode) node->code;
807     if (code == NR_MOVETO) { // if node is the endnode,
808         node->code = NR_LINETO; // new one is inserted before it, so change that to line
809     }
811     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
813     if (!node->n.other || !node->p.other) // if node is an endnode, select it
814         return node;
815     else
816         return newnode; // otherwise select the newly created node
819 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
821     node->p.pos = (node->pos + (node->pos - node->n.pos));
824 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
826     node->n.pos = (node->pos + (node->pos - node->p.pos));
829 /**
830  * Change line type at node, with side effects on neighbours.
831  */
832 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
834     g_assert(end);
835     g_assert(end->subpath);
836     g_assert(end->p.other);
838     if (end->code == static_cast< guint > ( code ) )
839         return;
841    Inkscape::NodePath::Node *start = end->p.other;
843     end->code = code;
845     if (code == NR_LINETO) {
846         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
847         if (end->n.other) {
848             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
849         }
850         sp_node_adjust_handle(start, -1);
851         sp_node_adjust_handle(end, 1);
852     } else {
853         NR::Point delta = end->pos - start->pos;
854         start->n.pos = start->pos + delta / 3;
855         end->p.pos = end->pos - delta / 3;
856         sp_node_adjust_handle(start, 1);
857         sp_node_adjust_handle(end, -1);
858     }
860     sp_node_update_handles(start);
861     sp_node_update_handles(end);
864 /**
865  * Change node type, and its handles accordingly.
866  */
867 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
869     g_assert(node);
870     g_assert(node->subpath);
872     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
873         return node;
875     if ((node->p.other != NULL) && (node->n.other != NULL)) {
876         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
877             type =Inkscape::NodePath::NODE_CUSP;
878         }
879     }
881     node->type = type;
883     if (node->type == Inkscape::NodePath::NODE_CUSP) {
884         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
885         node->knot->setSize (node->selected? 11 : 9);
886         sp_knot_update_ctrl(node->knot);
887     } else {
888         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
889         node->knot->setSize (node->selected? 9 : 7);
890         sp_knot_update_ctrl(node->knot);
891     }
893     // if one of handles is mouseovered, preserve its position
894     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
895         sp_node_adjust_handle(node, 1);
896     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
897         sp_node_adjust_handle(node, -1);
898     } else {
899         sp_node_adjust_handles(node);
900     }
902     sp_node_update_handles(node);
904     sp_nodepath_update_statusbar(node->subpath->nodepath);
906     return node;
909 /**
910  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
911  * adjacent segments from lines to curves.
912 */
913 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
915     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
916         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
917             // convert adjacent segment BEFORE to curve
918             node->code = NR_CURVETO;
919             NR::Point delta;
920             if (node->n.other != NULL)
921                 delta = node->n.other->pos - node->p.other->pos;
922             else
923                 delta = node->pos - node->p.other->pos;
924             node->p.pos = node->pos - delta / 4;
925             sp_node_update_handles(node);
926         }
928         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
929             // convert adjacent segment AFTER to curve
930             node->n.other->code = NR_CURVETO;
931             NR::Point delta;
932             if (node->p.other != NULL)
933                 delta = node->p.other->pos - node->n.other->pos;
934             else
935                 delta = node->pos - node->n.other->pos;
936             node->n.pos = node->pos - delta / 4;
937             sp_node_update_handles(node);
938         }
939     }
941     sp_nodepath_set_node_type (node, type);
944 /**
945  * Move node to point, and adjust its and neighbouring handles.
946  */
947 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
949     NR::Point delta = p - node->pos;
950     node->pos = p;
952     node->p.pos += delta;
953     node->n.pos += delta;
955     if (node->p.other) {
956         if (node->code == NR_LINETO) {
957             sp_node_adjust_handle(node, 1);
958             sp_node_adjust_handle(node->p.other, -1);
959         }
960     }
961     if (node->n.other) {
962         if (node->n.other->code == NR_LINETO) {
963             sp_node_adjust_handle(node, -1);
964             sp_node_adjust_handle(node->n.other, 1);
965         }
966     }
968     // this function is only called from batch movers that will update display at the end
969     // themselves, so here we just move all the knots without emitting move signals, for speed
970     sp_node_update_handles(node, false);
973 /**
974  * Call sp_node_moveto() for node selection and handle possible snapping.
975  */
976 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
977                                             bool const snap = true)
979     NR::Coord best = NR_HUGE;
980     NR::Point delta(dx, dy);
981     NR::Point best_pt = delta;
983     if (snap) {
984         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
985         
986         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
987             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
988             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
989             if (s.getDistance() < best) {
990                 best = s.getDistance();
991                 best_pt = s.getPoint() - n->pos;
992             }
993         }
994     }
996     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
997         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
998         sp_node_moveto(n, n->pos + best_pt);
999     }
1001     // do not update repr here so that node dragging is acceptably fast
1002     update_object(nodepath);
1005 /**
1006 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1007 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1008 near x = 0.
1009  */
1010 double
1011 sculpt_profile (double x, double alpha, guint profile)
1013     if (x >= 1)
1014         return 0;
1015     if (x <= 0)
1016         return 1;
1018     switch (profile) {
1019         case SCULPT_PROFILE_LINEAR:
1020         return 1 - x;
1021         case SCULPT_PROFILE_BELL:
1022         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1023         case SCULPT_PROFILE_ELLIPTIC:
1024         return sqrt(1 - x*x);
1025     }
1027     return 1;
1030 double
1031 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1033     // extremely primitive for now, don't have time to look for the real one
1034     double lower = NR::L2(b - a);
1035     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1036     return (lower + upper)/2;
1039 void
1040 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1042     n->pos = n->origin + delta;
1043     n->n.pos = n->n.origin + delta_n;
1044     n->p.pos = n->p.origin + delta_p;
1045     sp_node_adjust_handles(n);
1046     sp_node_update_handles(n, false);
1049 /**
1050  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1051  * on how far they are from the dragged node n.
1052  */
1053 static void 
1054 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1056     g_assert (n);
1057     g_assert (nodepath);
1058     g_assert (n->subpath->nodepath == nodepath);
1060     double pressure = n->knot->pressure;
1061     if (pressure == 0)
1062         pressure = 0.5; // default
1063     pressure = CLAMP (pressure, 0.2, 0.8);
1065     // map pressure to alpha = 1/5 ... 5
1066     double alpha = 1 - 2 * fabs(pressure - 0.5);
1067     if (pressure > 0.5)
1068         alpha = 1/alpha;
1070     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1072     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1073         // Only one subpath has selected nodes:
1074         // use linear mode, where the distance from n to node being dragged is calculated along the path
1076         double n_sel_range = 0, p_sel_range = 0;
1077         guint n_nodes = 0, p_nodes = 0;
1078         guint n_sel_nodes = 0, p_sel_nodes = 0;
1080         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1081         {
1082             double n_range = 0, p_range = 0;
1083             bool n_going = true, p_going = true;
1084             Inkscape::NodePath::Node *n_node = n;
1085             Inkscape::NodePath::Node *p_node = n;
1086             do {
1087                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1088                 if (n_node && n_going)
1089                     n_node = n_node->n.other;
1090                 if (n_node == NULL) {
1091                     n_going = false;
1092                 } else {
1093                     n_nodes ++;
1094                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1095                     if (n_node->selected) {
1096                         n_sel_nodes ++;
1097                         n_sel_range = n_range;
1098                     }
1099                     if (n_node == p_node) {
1100                         n_going = false;
1101                         p_going = false;
1102                     }
1103                 }
1104                 if (p_node && p_going)
1105                     p_node = p_node->p.other;
1106                 if (p_node == NULL) {
1107                     p_going = false;
1108                 } else {
1109                     p_nodes ++;
1110                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1111                     if (p_node->selected) {
1112                         p_sel_nodes ++;
1113                         p_sel_range = p_range;
1114                     }
1115                     if (p_node == n_node) {
1116                         n_going = false;
1117                         p_going = false;
1118                     }
1119                 }
1120             } while (n_going || p_going);
1121         }
1123         // Second pass: actually move nodes in this subpath
1124         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1125         {
1126             double n_range = 0, p_range = 0;
1127             bool n_going = true, p_going = true;
1128             Inkscape::NodePath::Node *n_node = n;
1129             Inkscape::NodePath::Node *p_node = n;
1130             do {
1131                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1132                 if (n_node && n_going)
1133                     n_node = n_node->n.other;
1134                 if (n_node == NULL) {
1135                     n_going = false;
1136                 } else {
1137                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1138                     if (n_node->selected) {
1139                         sp_nodepath_move_node_and_handles (n_node, 
1140                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1141                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1142                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1143                     }
1144                     if (n_node == p_node) {
1145                         n_going = false;
1146                         p_going = false;
1147                     }
1148                 }
1149                 if (p_node && p_going)
1150                     p_node = p_node->p.other;
1151                 if (p_node == NULL) {
1152                     p_going = false;
1153                 } else {
1154                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1155                     if (p_node->selected) {
1156                         sp_nodepath_move_node_and_handles (p_node, 
1157                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1158                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1159                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1160                     }
1161                     if (p_node == n_node) {
1162                         n_going = false;
1163                         p_going = false;
1164                     }
1165                 }
1166             } while (n_going || p_going);
1167         }
1169     } else {
1170         // Multiple subpaths have selected nodes:
1171         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1172         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1173         // fix the pear-like shape when sculpting e.g. a ring
1175         // First pass: calculate range
1176         gdouble direct_range = 0;
1177         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1178             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1179             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1180                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1181                 if (node->selected) {
1182                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1183                 }
1184             }
1185         }
1187         // Second pass: actually move nodes
1188         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1189             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1190             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1191                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1192                 if (node->selected) {
1193                     if (direct_range > 1e-6) {
1194                         sp_nodepath_move_node_and_handles (node,
1195                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1196                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1197                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1198                     } else {
1199                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1200                     }
1202                 }
1203             }
1204         }
1205     }
1207     // do not update repr here so that node dragging is acceptably fast
1208     update_object(nodepath);
1212 /**
1213  * Move node selection to point, adjust its and neighbouring handles,
1214  * handle possible snapping, and commit the change with possible undo.
1215  */
1216 void
1217 sp_node_selected_move(gdouble dx, gdouble dy)
1219     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1220     if (!nodepath) return;
1222     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1224     if (dx == 0) {
1225         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1226     } else if (dy == 0) {
1227         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1228     } else {
1229         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1230     }
1233 /**
1234  * Move node selection off screen and commit the change.
1235  */
1236 void
1237 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1239     // borrowed from sp_selection_move_screen in selection-chemistry.c
1240     // we find out the current zoom factor and divide deltas by it
1241     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1243     gdouble zoom = desktop->current_zoom();
1244     gdouble zdx = dx / zoom;
1245     gdouble zdy = dy / zoom;
1247     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1248     if (!nodepath) return;
1250     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1252     if (dx == 0) {
1253         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1254     } else if (dy == 0) {
1255         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1256     } else {
1257         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1258     }
1261 /** If they don't yet exist, creates knot and line for the given side of the node */
1262 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1264     if (!side->knot) {
1265         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"));
1267         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1268         side->knot->setSize (7);
1269         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1270         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1271         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1272         sp_knot_update_ctrl(side->knot);
1274         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1275         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1276         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1277         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1278         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1279         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1280     }
1282     if (!side->line) {
1283         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1284                                         SP_TYPE_CTRLLINE, NULL);
1285     }
1288 /**
1289  * Ensure the given handle of the node is visible/invisible, update its screen position
1290  */
1291 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1293     g_assert(node != NULL);
1295    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1296     NRPathcode code = sp_node_path_code_from_side(node, side);
1298     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1300     if (show_handle) {
1301         if (!side->knot) { // No handle knot at all
1302             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1303             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1304             side->knot->pos = side->pos;
1305             if (side->knot->item) 
1306                 SP_CTRL(side->knot->item)->moveto(side->pos);
1307             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1308             sp_knot_show(side->knot);
1309         } else {
1310             if (side->knot->pos != side->pos) { // only if it's really moved
1311                 if (fire_move_signals) {
1312                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1313                 } else {
1314                     sp_knot_moveto(side->knot, &side->pos);
1315                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1316                 }
1317             }
1318             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1319                 sp_knot_show(side->knot);
1320             }
1321         }
1322         sp_canvas_item_show(side->line);
1323     } else {
1324         if (side->knot) {
1325             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1326                 sp_knot_hide(side->knot);
1327             }
1328         }
1329         if (side->line) {
1330             sp_canvas_item_hide(side->line);
1331         }
1332     }
1335 /**
1336  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1337  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1338  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1339  * updated; otherwise, just move the knots silently (used in batch moves).
1340  */
1341 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1343     g_assert(node != NULL);
1345     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1346         sp_knot_show(node->knot);
1347     }
1349     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1350         if (fire_move_signals)
1351             sp_knot_set_position(node->knot, &node->pos, 0);
1352         else 
1353             sp_knot_moveto(node->knot, &node->pos);
1354     }
1356     gboolean show_handles = node->selected;
1357     if (node->p.other != NULL) {
1358         if (node->p.other->selected) show_handles = TRUE;
1359     }
1360     if (node->n.other != NULL) {
1361         if (node->n.other->selected) show_handles = TRUE;
1362     }
1364     if (node->subpath->nodepath->show_handles == false)
1365         show_handles = FALSE;
1367     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1368     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1371 /**
1372  * Call sp_node_update_handles() for all nodes on subpath.
1373  */
1374 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1376     g_assert(subpath != NULL);
1378     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1379         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1380     }
1383 /**
1384  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1385  */
1386 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1388     g_assert(nodepath != NULL);
1390     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1391         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1392     }
1395 void
1396 sp_nodepath_show_handles(bool show)
1398     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1399     if (nodepath == NULL) return;
1401     nodepath->show_handles = show;
1402     sp_nodepath_update_handles(nodepath);
1405 /**
1406  * Adds all selected nodes in nodepath to list.
1407  */
1408 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1410     StlConv<Node *>::list(l, selected);
1411 /// \todo this adds a copying, rework when the selection becomes a stl list
1414 /**
1415  * Align selected nodes on the specified axis.
1416  */
1417 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1419     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1420         return;
1421     }
1423     if ( !nodepath->selected->next ) { // only one node selected
1424         return;
1425     }
1426    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1427     NR::Point dest(pNode->pos);
1428     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1429         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1430         if (pNode) {
1431             dest[axis] = pNode->pos[axis];
1432             sp_node_moveto(pNode, dest);
1433         }
1434     }
1436     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1439 /// Helper struct.
1440 struct NodeSort
1442    Inkscape::NodePath::Node *_node;
1443     NR::Coord _coord;
1444     /// \todo use vectorof pointers instead of calling copy ctor
1445     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1446         _node(node), _coord(node->pos[axis])
1447     {}
1449 };
1451 static bool operator<(NodeSort const &a, NodeSort const &b)
1453     return (a._coord < b._coord);
1456 /**
1457  * Distribute selected nodes on the specified axis.
1458  */
1459 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1461     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1462         return;
1463     }
1465     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1466         return;
1467     }
1469    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1470     std::vector<NodeSort> sorted;
1471     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1472         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1473         if (pNode) {
1474             NodeSort n(pNode, axis);
1475             sorted.push_back(n);
1476             //dest[axis] = pNode->pos[axis];
1477             //sp_node_moveto(pNode, dest);
1478         }
1479     }
1480     std::sort(sorted.begin(), sorted.end());
1481     unsigned int len = sorted.size();
1482     //overall bboxes span
1483     float dist = (sorted.back()._coord -
1484                   sorted.front()._coord);
1485     //new distance between each bbox
1486     float step = (dist) / (len - 1);
1487     float pos = sorted.front()._coord;
1488     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1489           it < sorted.end();
1490           it ++ )
1491     {
1492         NR::Point dest((*it)._node->pos);
1493         dest[axis] = pos;
1494         sp_node_moveto((*it)._node, dest);
1495         pos += step;
1496     }
1498     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1502 /**
1503  * Call sp_nodepath_line_add_node() for all selected segments.
1504  */
1505 void
1506 sp_node_selected_add_node(void)
1508     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1509     if (!nodepath) {
1510         return;
1511     }
1513     GList *nl = NULL;
1515     int n_added = 0;
1517     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1518        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1519         g_assert(t->selected);
1520         if (t->p.other && t->p.other->selected) {
1521             nl = g_list_prepend(nl, t);
1522         }
1523     }
1525     while (nl) {
1526        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1527        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1528        sp_nodepath_node_select(n, TRUE, FALSE);
1529        n_added ++;
1530        nl = g_list_remove(nl, t);
1531     }
1533     /** \todo fixme: adjust ? */
1534     sp_nodepath_update_handles(nodepath);
1536     if (n_added > 1) {
1537         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1538     } else if (n_added > 0) {
1539         sp_nodepath_update_repr(nodepath, _("Add node"));
1540     }
1542     sp_nodepath_update_statusbar(nodepath);
1545 /**
1546  * Select segment nearest to point
1547  */
1548 void
1549 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1551     if (!nodepath) {
1552         return;
1553     }
1555     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1557     //find segment to segment
1558     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1560     //fixme: this can return NULL, so check before proceeding.
1561     g_return_if_fail(e != NULL);
1562     
1563     gboolean force = FALSE;
1564     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1565         force = TRUE;
1566     }
1567     sp_nodepath_node_select(e, (gboolean) toggle, force);
1568     if (e->p.other)
1569         sp_nodepath_node_select(e->p.other, TRUE, force);
1571     sp_nodepath_update_handles(nodepath);
1573     sp_nodepath_update_statusbar(nodepath);
1576 /**
1577  * Add a node nearest to point
1578  */
1579 void
1580 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1582     if (!nodepath) {
1583         return;
1584     }
1586     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1588     //find segment to split
1589     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1591     //don't know why but t seems to flip for lines
1592     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1593         position.t = 1.0 - position.t;
1594     }
1595     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1596     sp_nodepath_node_select(n, FALSE, TRUE);
1598     /* fixme: adjust ? */
1599     sp_nodepath_update_handles(nodepath);
1601     sp_nodepath_update_repr(nodepath, _("Add node"));
1603     sp_nodepath_update_statusbar(nodepath);
1606 /*
1607  * Adjusts a segment so that t moves by a certain delta for dragging
1608  * converts lines to curves
1609  *
1610  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1611  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1612  */
1613 void
1614 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1616     //fixme: e and e->p can be NULL, so check for those before proceeding
1617     g_return_if_fail(e != NULL);
1618     g_return_if_fail(&e->p != NULL);
1619     
1620     /* feel good is an arbitrary parameter that distributes the delta between handles
1621      * if t of the drag point is less than 1/6 distance form the endpoint only
1622      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1623      */
1624     double feel_good;
1625     if (t <= 1.0 / 6.0)
1626         feel_good = 0;
1627     else if (t <= 0.5)
1628         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1629     else if (t <= 5.0 / 6.0)
1630         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1631     else
1632         feel_good = 1;
1634     //if we're dragging a line convert it to a curve
1635     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1636         sp_nodepath_set_line_type(e, NR_CURVETO);
1637     }
1639     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1640     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1641     e->p.other->n.pos += offsetcoord0;
1642     e->p.pos += offsetcoord1;
1644     // adjust handles of adjacent nodes where necessary
1645     sp_node_adjust_handle(e,1);
1646     sp_node_adjust_handle(e->p.other,-1);
1648     sp_nodepath_update_handles(e->subpath->nodepath);
1650     update_object(e->subpath->nodepath);
1652     sp_nodepath_update_statusbar(e->subpath->nodepath);
1656 /**
1657  * Call sp_nodepath_break() for all selected segments.
1658  */
1659 void sp_node_selected_break()
1661     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1662     if (!nodepath) return;
1664     GList *temp = NULL;
1665     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1666        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1667        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1668         if (nn == NULL) continue; // no break, no new node
1669         temp = g_list_prepend(temp, nn);
1670     }
1672     if (temp) {
1673         sp_nodepath_deselect(nodepath);
1674     }
1675     for (GList *l = temp; l != NULL; l = l->next) {
1676         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1677     }
1679     sp_nodepath_update_handles(nodepath);
1681     sp_nodepath_update_repr(nodepath, _("Break path"));
1684 /**
1685  * Duplicate the selected node(s).
1686  */
1687 void sp_node_selected_duplicate()
1689     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1690     if (!nodepath) {
1691         return;
1692     }
1694     GList *temp = NULL;
1695     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1696        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1697        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1698         if (nn == NULL) continue; // could not duplicate
1699         temp = g_list_prepend(temp, nn);
1700     }
1702     if (temp) {
1703         sp_nodepath_deselect(nodepath);
1704     }
1705     for (GList *l = temp; l != NULL; l = l->next) {
1706         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1707     }
1709     sp_nodepath_update_handles(nodepath);
1711     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1714 /**
1715  *  Join two nodes by merging them into one.
1716  */
1717 void sp_node_selected_join()
1719     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1720     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1722     if (g_list_length(nodepath->selected) != 2) {
1723         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1724         return;
1725     }
1727    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1728    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1730     g_assert(a != b);
1731     g_assert(a->p.other || a->n.other);
1732     g_assert(b->p.other || b->n.other);
1734     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1735         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1736         return;
1737     }
1739     /* a and b are endpoints */
1741     NR::Point c;
1742     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1743         c = a->pos;
1744     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1745         c = b->pos;
1746     } else {
1747         c = (a->pos + b->pos) / 2;
1748     }
1750     if (a->subpath == b->subpath) {
1751        Inkscape::NodePath::SubPath *sp = a->subpath;
1752         sp_nodepath_subpath_close(sp);
1753         sp_node_moveto (sp->first, c);
1755         sp_nodepath_update_handles(sp->nodepath);
1756         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1757         return;
1758     }
1760     /* a and b are separate subpaths */
1761    Inkscape::NodePath::SubPath *sa = a->subpath;
1762    Inkscape::NodePath::SubPath *sb = b->subpath;
1763     NR::Point p;
1764    Inkscape::NodePath::Node *n;
1765     NRPathcode code;
1766     if (a == sa->first) {
1767         p = sa->first->n.pos;
1768         code = (NRPathcode)sa->first->n.other->code;
1769        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1770         n = sa->last;
1771         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1772         n = n->p.other;
1773         while (n) {
1774             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1775             n = n->p.other;
1776             if (n == sa->first) n = NULL;
1777         }
1778         sp_nodepath_subpath_destroy(sa);
1779         sa = t;
1780     } else if (a == sa->last) {
1781         p = sa->last->p.pos;
1782         code = (NRPathcode)sa->last->code;
1783         sp_nodepath_node_destroy(sa->last);
1784     } else {
1785         code = NR_END;
1786         g_assert_not_reached();
1787     }
1789     if (b == sb->first) {
1790         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1791         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1792             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1793         }
1794     } else if (b == sb->last) {
1795         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1796         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1797             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1798         }
1799     } else {
1800         g_assert_not_reached();
1801     }
1802     /* and now destroy sb */
1804     sp_nodepath_subpath_destroy(sb);
1806     sp_nodepath_update_handles(sa->nodepath);
1808     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1810     sp_nodepath_update_statusbar(nodepath);
1813 /**
1814  *  Join two nodes by adding a segment between them.
1815  */
1816 void sp_node_selected_join_segment()
1818     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1819     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1821     if (g_list_length(nodepath->selected) != 2) {
1822         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1823         return;
1824     }
1826    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1827    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1829     g_assert(a != b);
1830     g_assert(a->p.other || a->n.other);
1831     g_assert(b->p.other || b->n.other);
1833     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1834         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1835         return;
1836     }
1838     if (a->subpath == b->subpath) {
1839        Inkscape::NodePath::SubPath *sp = a->subpath;
1841         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1842         sp->closed = TRUE;
1844         sp->first->p.other = sp->last;
1845         sp->last->n.other  = sp->first;
1847         sp_node_handle_mirror_p_to_n(sp->last);
1848         sp_node_handle_mirror_n_to_p(sp->first);
1850         sp->first->code = sp->last->code;
1851         sp->first       = sp->last;
1853         sp_nodepath_update_handles(sp->nodepath);
1855         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1857         return;
1858     }
1860     /* a and b are separate subpaths */
1861    Inkscape::NodePath::SubPath *sa = a->subpath;
1862    Inkscape::NodePath::SubPath *sb = b->subpath;
1864    Inkscape::NodePath::Node *n;
1865     NR::Point p;
1866     NRPathcode code;
1867     if (a == sa->first) {
1868         code = (NRPathcode) sa->first->n.other->code;
1869        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1870         n = sa->last;
1871         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1872         for (n = n->p.other; n != NULL; n = n->p.other) {
1873             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1874         }
1875         sp_nodepath_subpath_destroy(sa);
1876         sa = t;
1877     } else if (a == sa->last) {
1878         code = (NRPathcode)sa->last->code;
1879     } else {
1880         code = NR_END;
1881         g_assert_not_reached();
1882     }
1884     if (b == sb->first) {
1885         n = sb->first;
1886         sp_node_handle_mirror_p_to_n(sa->last);
1887         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1888         sp_node_handle_mirror_n_to_p(sa->last);
1889         for (n = n->n.other; n != NULL; n = n->n.other) {
1890             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1891         }
1892     } else if (b == sb->last) {
1893         n = sb->last;
1894         sp_node_handle_mirror_p_to_n(sa->last);
1895         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1896         sp_node_handle_mirror_n_to_p(sa->last);
1897         for (n = n->p.other; n != NULL; n = n->p.other) {
1898             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1899         }
1900     } else {
1901         g_assert_not_reached();
1902     }
1903     /* and now destroy sb */
1905     sp_nodepath_subpath_destroy(sb);
1907     sp_nodepath_update_handles(sa->nodepath);
1909     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1912 /**
1913  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1914  */
1915 void sp_node_delete_preserve(GList *nodes_to_delete)
1917     GSList *nodepaths = NULL;
1918     
1919     while (nodes_to_delete) {
1920         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1921         Inkscape::NodePath::SubPath *sp = node->subpath;
1922         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1923         Inkscape::NodePath::Node *sample_cursor = NULL;
1924         Inkscape::NodePath::Node *sample_end = NULL;
1925         Inkscape::NodePath::Node *delete_cursor = node;
1926         bool just_delete = false;
1927         
1928         //find the start of this contiguous selection
1929         //move left to the first node that is not selected
1930         //or the start of the non-closed path
1931         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1932             delete_cursor = curr;
1933         }
1935         //just delete at the beginning of an open path
1936         if (!delete_cursor->p.other) {
1937             sample_cursor = delete_cursor;
1938             just_delete = true;
1939         } else {
1940             sample_cursor = delete_cursor->p.other;
1941         }
1942         
1943         //calculate points for each segment
1944         int rate = 5;
1945         float period = 1.0 / rate;
1946         std::vector<NR::Point> data;
1947         if (!just_delete) {
1948             data.push_back(sample_cursor->pos);
1949             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1950                 //just delete at the end of an open path
1951                 if (!sp->closed && curr == sp->last) {
1952                     just_delete = true;
1953                     break;
1954                 }
1955                 
1956                 //sample points on the contiguous selected segment
1957                 NR::Point *bez;
1958                 bez = new NR::Point [4];
1959                 bez[0] = curr->pos;
1960                 bez[1] = curr->n.pos;
1961                 bez[2] = curr->n.other->p.pos;
1962                 bez[3] = curr->n.other->pos;
1963                 for (int i=1; i<rate; i++) {
1964                     gdouble t = i * period;
1965                     NR::Point p = bezier_pt(3, bez, t);
1966                     data.push_back(p);
1967                 }
1968                 data.push_back(curr->n.other->pos);
1970                 sample_end = curr->n.other;
1971                 //break if we've come full circle or hit the end of the selection
1972                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1973                     break;
1974                 }
1975             }
1976         }
1978         if (!just_delete) {
1979             //calculate the best fitting single segment and adjust the endpoints
1980             NR::Point *adata;
1981             adata = new NR::Point [data.size()];
1982             copy(data.begin(), data.end(), adata);
1983             
1984             NR::Point *bez;
1985             bez = new NR::Point [4];
1986             //would decreasing error create a better fitting approximation?
1987             gdouble error = 1.0;
1988             gint ret;
1989             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1991             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
1992             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
1993             //the resulting nodes behave as expected.
1994             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
1995             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
1996             
1997             //adjust endpoints
1998             sample_cursor->n.pos = bez[1];
1999             sample_end->p.pos = bez[2];
2000         }
2001        
2002         //destroy this contiguous selection
2003         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2004             Inkscape::NodePath::Node *temp = delete_cursor;
2005             if (delete_cursor->n.other == delete_cursor) {
2006                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2007                 delete_cursor = NULL; 
2008             } else {
2009                 delete_cursor = delete_cursor->n.other;
2010             }
2011             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2012             sp_nodepath_node_destroy(temp);
2013         }
2015         sp_nodepath_update_handles(nodepath);
2017         if (!g_slist_find(nodepaths, nodepath))
2018             nodepaths = g_slist_prepend (nodepaths, nodepath);
2019     }
2021     for (GSList *i = nodepaths; i; i = i->next) {
2022         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2023         // different nodepaths will give us one undo event per nodepath
2024         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2026         // if the entire nodepath is removed, delete the selected object.
2027         if (nodepath->subpaths == NULL ||
2028             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2029             //at least 2
2030             sp_nodepath_get_node_count(nodepath) < 2) {
2031             SPDocument *document = sp_desktop_document (nodepath->desktop);
2032             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2033             //delete this nodepath's object, not the entire selection! (though at this time, this
2034             //does not matter)
2035             sp_selection_delete();
2036             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2037                               _("Delete nodes"));
2038         } else {
2039             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2040             sp_nodepath_update_statusbar(nodepath);
2041         }
2042     }
2044     g_slist_free (nodepaths);
2047 /**
2048  * Delete one or more selected nodes.
2049  */
2050 void sp_node_selected_delete()
2052     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2053     if (!nodepath) return;
2054     if (!nodepath->selected) return;
2056     /** \todo fixme: do it the right way */
2057     while (nodepath->selected) {
2058        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2059         sp_nodepath_node_destroy(node);
2060     }
2063     //clean up the nodepath (such as for trivial subpaths)
2064     sp_nodepath_cleanup(nodepath);
2066     sp_nodepath_update_handles(nodepath);
2068     // if the entire nodepath is removed, delete the selected object.
2069     if (nodepath->subpaths == NULL ||
2070         sp_nodepath_get_node_count(nodepath) < 2) {
2071         SPDocument *document = sp_desktop_document (nodepath->desktop);
2072         sp_selection_delete();
2073         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2074                           _("Delete nodes"));
2075         return;
2076     }
2078     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2080     sp_nodepath_update_statusbar(nodepath);
2083 /**
2084  * Delete one or more segments between two selected nodes.
2085  * This is the code for 'split'.
2086  */
2087 void
2088 sp_node_selected_delete_segment(void)
2090    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2091    Inkscape::NodePath::Node *curr, *next;     //Iterators
2093     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2094     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2096     if (g_list_length(nodepath->selected) != 2) {
2097         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2098                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2099         return;
2100     }
2102     //Selected nodes, not inclusive
2103    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2104    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2106     if ( ( a==b)                       ||  //same node
2107          (a->subpath  != b->subpath )  ||  //not the same path
2108          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2109          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2110     {
2111         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2112                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2113         return;
2114     }
2116     //###########################################
2117     //# BEGIN EDITS
2118     //###########################################
2119     //##################################
2120     //# CLOSED PATH
2121     //##################################
2122     if (a->subpath->closed) {
2125         gboolean reversed = FALSE;
2127         //Since we can go in a circle, we need to find the shorter distance.
2128         //  a->b or b->a
2129         start = end = NULL;
2130         int distance    = 0;
2131         int minDistance = 0;
2132         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2133             if (curr==b) {
2134                 //printf("a to b:%d\n", distance);
2135                 start = a;//go from a to b
2136                 end   = b;
2137                 minDistance = distance;
2138                 //printf("A to B :\n");
2139                 break;
2140             }
2141             distance++;
2142         }
2144         //try again, the other direction
2145         distance = 0;
2146         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2147             if (curr==a) {
2148                 //printf("b to a:%d\n", distance);
2149                 if (distance < minDistance) {
2150                     start    = b;  //we go from b to a
2151                     end      = a;
2152                     reversed = TRUE;
2153                     //printf("B to A\n");
2154                 }
2155                 break;
2156             }
2157             distance++;
2158         }
2161         //Copy everything from 'end' to 'start' to a new subpath
2162        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2163         for (curr=end ; curr ; curr=curr->n.other) {
2164             NRPathcode code = (NRPathcode) curr->code;
2165             if (curr == end)
2166                 code = NR_MOVETO;
2167             sp_nodepath_node_new(t, NULL,
2168                                  (Inkscape::NodePath::NodeType)curr->type, code,
2169                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2170             if (curr == start)
2171                 break;
2172         }
2173         sp_nodepath_subpath_destroy(a->subpath);
2176     }
2180     //##################################
2181     //# OPEN PATH
2182     //##################################
2183     else {
2185         //We need to get the direction of the list between A and B
2186         //Can we walk from a to b?
2187         start = end = NULL;
2188         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2189             if (curr==b) {
2190                 start = a;  //did it!  we go from a to b
2191                 end   = b;
2192                 //printf("A to B\n");
2193                 break;
2194             }
2195         }
2196         if (!start) {//didn't work?  let's try the other direction
2197             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2198                 if (curr==a) {
2199                     start = b;  //did it!  we go from b to a
2200                     end   = a;
2201                     //printf("B to A\n");
2202                     break;
2203                 }
2204             }
2205         }
2206         if (!start) {
2207             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2208                                                      _("Cannot find path between nodes."));
2209             return;
2210         }
2214         //Copy everything after 'end' to a new subpath
2215        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2216         for (curr=end ; curr ; curr=curr->n.other) {
2217             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2218                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2219         }
2221         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2222         for (curr = start->n.other ; curr  ; curr=next) {
2223             next = curr->n.other;
2224             sp_nodepath_node_destroy(curr);
2225         }
2227     }
2228     //###########################################
2229     //# END EDITS
2230     //###########################################
2232     //clean up the nodepath (such as for trivial subpaths)
2233     sp_nodepath_cleanup(nodepath);
2235     sp_nodepath_update_handles(nodepath);
2237     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2239     sp_nodepath_update_statusbar(nodepath);
2242 /**
2243  * Call sp_nodepath_set_line() for all selected segments.
2244  */
2245 void
2246 sp_node_selected_set_line_type(NRPathcode code)
2248     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2249     if (nodepath == NULL) return;
2251     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2252        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2253         g_assert(n->selected);
2254         if (n->p.other && n->p.other->selected) {
2255             sp_nodepath_set_line_type(n, code);
2256         }
2257     }
2259     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2262 /**
2263  * Call sp_nodepath_convert_node_type() for all selected nodes.
2264  */
2265 void
2266 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2268     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2269     if (nodepath == NULL) return;
2271     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2272         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2273     }
2275     sp_nodepath_update_repr(nodepath, _("Change node type"));
2278 /**
2279  * Change select status of node, update its own and neighbour handles.
2280  */
2281 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2283     node->selected = selected;
2285     if (selected) {
2286         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2287         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2288         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2289         sp_knot_update_ctrl(node->knot);
2290     } else {
2291         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2292         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2293         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2294         sp_knot_update_ctrl(node->knot);
2295     }
2297     sp_node_update_handles(node);
2298     if (node->n.other) sp_node_update_handles(node->n.other);
2299     if (node->p.other) sp_node_update_handles(node->p.other);
2302 /**
2303 \brief Select a node
2304 \param node     The node to select
2305 \param incremental   If true, add to selection, otherwise deselect others
2306 \param override   If true, always select this node, otherwise toggle selected status
2307 */
2308 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2310     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2312     if (incremental) {
2313         if (override) {
2314             if (!g_list_find(nodepath->selected, node)) {
2315                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2316             }
2317             sp_node_set_selected(node, TRUE);
2318         } else { // toggle
2319             if (node->selected) {
2320                 g_assert(g_list_find(nodepath->selected, node));
2321                 nodepath->selected = g_list_remove(nodepath->selected, node);
2322             } else {
2323                 g_assert(!g_list_find(nodepath->selected, node));
2324                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2325             }
2326             sp_node_set_selected(node, !node->selected);
2327         }
2328     } else {
2329         sp_nodepath_deselect(nodepath);
2330         nodepath->selected = g_list_prepend(nodepath->selected, node);
2331         sp_node_set_selected(node, TRUE);
2332     }
2334     sp_nodepath_update_statusbar(nodepath);
2338 /**
2339 \brief Deselect all nodes in the nodepath
2340 */
2341 void
2342 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2344     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2346     while (nodepath->selected) {
2347         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2348         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2349     }
2350     sp_nodepath_update_statusbar(nodepath);
2353 /**
2354 \brief Select or invert selection of all nodes in the nodepath
2355 */
2356 void
2357 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2359     if (!nodepath) return;
2361     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2362        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2363         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2364            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2365            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2366         }
2367     }
2370 /**
2371  * If nothing selected, does the same as sp_nodepath_select_all();
2372  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2373  * (i.e., similar to "select all in layer", with the "selected" subpaths
2374  * being treated as "layers" in the path).
2375  */
2376 void
2377 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2379     if (!nodepath) return;
2381     if (g_list_length (nodepath->selected) == 0) {
2382         sp_nodepath_select_all (nodepath, invert);
2383         return;
2384     }
2386     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2387     GSList *subpaths = NULL;
2389     for (GList *l = copy; l != NULL; l = l->next) {
2390         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2391         Inkscape::NodePath::SubPath *subpath = n->subpath;
2392         if (!g_slist_find (subpaths, subpath))
2393             subpaths = g_slist_prepend (subpaths, subpath);
2394     }
2396     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2397         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2398         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2399             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2400             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2401         }
2402     }
2404     g_slist_free (subpaths);
2405     g_list_free (copy);
2408 /**
2409  * \brief Select the node after the last selected; if none is selected,
2410  * select the first within path.
2411  */
2412 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2414     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2416    Inkscape::NodePath::Node *last = NULL;
2417     if (nodepath->selected) {
2418         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2419            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2420             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2421             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2422                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2423                 if (node->selected) {
2424                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2425                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2426                             if (spl->next) { // there's a next subpath
2427                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2428                                 last = subpath_next->first;
2429                             } else if (spl->prev) { // there's a previous subpath
2430                                 last = NULL; // to be set later to the first node of first subpath
2431                             } else {
2432                                 last = node->n.other;
2433                             }
2434                         } else {
2435                             last = node->n.other;
2436                         }
2437                     } else {
2438                         if (node->n.other) {
2439                             last = node->n.other;
2440                         } else {
2441                             if (spl->next) { // there's a next subpath
2442                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2443                                 last = subpath_next->first;
2444                             } else if (spl->prev) { // there's a previous subpath
2445                                 last = NULL; // to be set later to the first node of first subpath
2446                             } else {
2447                                 last = (Inkscape::NodePath::Node *) subpath->first;
2448                             }
2449                         }
2450                     }
2451                 }
2452             }
2453         }
2454         sp_nodepath_deselect(nodepath);
2455     }
2457     if (last) { // there's at least one more node after selected
2458         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2459     } else { // no more nodes, select the first one in first subpath
2460        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2461         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2462     }
2465 /**
2466  * \brief Select the node before the first selected; if none is selected,
2467  * select the last within path
2468  */
2469 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2471     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2473    Inkscape::NodePath::Node *last = NULL;
2474     if (nodepath->selected) {
2475         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2476            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2477             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2478                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2479                 if (node->selected) {
2480                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2481                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2482                             if (spl->prev) { // there's a prev subpath
2483                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2484                                 last = subpath_prev->last;
2485                             } else if (spl->next) { // there's a next subpath
2486                                 last = NULL; // to be set later to the last node of last subpath
2487                             } else {
2488                                 last = node->p.other;
2489                             }
2490                         } else {
2491                             last = node->p.other;
2492                         }
2493                     } else {
2494                         if (node->p.other) {
2495                             last = node->p.other;
2496                         } else {
2497                             if (spl->prev) { // there's a prev subpath
2498                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2499                                 last = subpath_prev->last;
2500                             } else if (spl->next) { // there's a next subpath
2501                                 last = NULL; // to be set later to the last node of last subpath
2502                             } else {
2503                                 last = (Inkscape::NodePath::Node *) subpath->last;
2504                             }
2505                         }
2506                     }
2507                 }
2508             }
2509         }
2510         sp_nodepath_deselect(nodepath);
2511     }
2513     if (last) { // there's at least one more node before selected
2514         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2515     } else { // no more nodes, select the last one in last subpath
2516         GList *spl = g_list_last(nodepath->subpaths);
2517        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2518         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2519     }
2522 /**
2523  * \brief Select all nodes that are within the rectangle.
2524  */
2525 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2527     if (!incremental) {
2528         sp_nodepath_deselect(nodepath);
2529     }
2531     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2532        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2533         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2534            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2536             if (b.contains(node->pos)) {
2537                 sp_nodepath_node_select(node, TRUE, TRUE);
2538             }
2539         }
2540     }
2544 void
2545 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2547     g_assert (n);
2548     g_assert (nodepath);
2549     g_assert (n->subpath->nodepath == nodepath);
2551     if (g_list_length (nodepath->selected) == 0) {
2552         if (grow > 0) {
2553             sp_nodepath_node_select(n, TRUE, TRUE);
2554         }
2555         return;
2556     }
2558     if (g_list_length (nodepath->selected) == 1) {
2559         if (grow < 0) {
2560             sp_nodepath_deselect (nodepath);
2561             return;
2562         }
2563     }
2565         double n_sel_range = 0, p_sel_range = 0;
2566             Inkscape::NodePath::Node *farthest_n_node = n;
2567             Inkscape::NodePath::Node *farthest_p_node = n;
2569         // Calculate ranges
2570         {
2571             double n_range = 0, p_range = 0;
2572             bool n_going = true, p_going = true;
2573             Inkscape::NodePath::Node *n_node = n;
2574             Inkscape::NodePath::Node *p_node = n;
2575             do {
2576                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2577                 if (n_node && n_going)
2578                     n_node = n_node->n.other;
2579                 if (n_node == NULL) {
2580                     n_going = false;
2581                 } else {
2582                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2583                     if (n_node->selected) {
2584                         n_sel_range = n_range;
2585                         farthest_n_node = n_node;
2586                     }
2587                     if (n_node == p_node) {
2588                         n_going = false;
2589                         p_going = false;
2590                     }
2591                 }
2592                 if (p_node && p_going)
2593                     p_node = p_node->p.other;
2594                 if (p_node == NULL) {
2595                     p_going = false;
2596                 } else {
2597                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2598                     if (p_node->selected) {
2599                         p_sel_range = p_range;
2600                         farthest_p_node = p_node;
2601                     }
2602                     if (p_node == n_node) {
2603                         n_going = false;
2604                         p_going = false;
2605                     }
2606                 }
2607             } while (n_going || p_going);
2608         }
2610     if (grow > 0) {
2611         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2612                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2613         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2614                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2615         }
2616     } else {
2617         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2618                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2619         } else if (farthest_p_node && farthest_p_node->selected) {
2620                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2621         }
2622     }
2625 void
2626 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2628     g_assert (n);
2629     g_assert (nodepath);
2630     g_assert (n->subpath->nodepath == nodepath);
2632     if (g_list_length (nodepath->selected) == 0) {
2633         if (grow > 0) {
2634             sp_nodepath_node_select(n, TRUE, TRUE);
2635         }
2636         return;
2637     }
2639     if (g_list_length (nodepath->selected) == 1) {
2640         if (grow < 0) {
2641             sp_nodepath_deselect (nodepath);
2642             return;
2643         }
2644     }
2646     Inkscape::NodePath::Node *farthest_selected = NULL;
2647     double farthest_dist = 0;
2649     Inkscape::NodePath::Node *closest_unselected = NULL;
2650     double closest_dist = NR_HUGE;
2652     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2653        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2654         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2655            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2656            if (node == n)
2657                continue;
2658            if (node->selected) {
2659                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2660                    farthest_dist = NR::L2(node->pos - n->pos);
2661                    farthest_selected = node;
2662                }
2663            } else {
2664                if (NR::L2(node->pos - n->pos) < closest_dist) {
2665                    closest_dist = NR::L2(node->pos - n->pos);
2666                    closest_unselected = node;
2667                }
2668            }
2669         }
2670     }
2672     if (grow > 0) {
2673         if (closest_unselected) {
2674             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2675         }
2676     } else {
2677         if (farthest_selected) {
2678             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2679         }
2680     }
2684 /**
2685 \brief  Saves all nodes' and handles' current positions in their origin members
2686 */
2687 void
2688 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2690     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2691        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2692         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2693            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2694            n->origin = n->pos;
2695            n->p.origin = n->p.pos;
2696            n->n.origin = n->n.pos;
2697         }
2698     }
2699
2701 /**
2702 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2703 */
2704 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2706     if (!nodepath->selected) {
2707         return NULL;
2708     }
2710     GList *r = NULL;
2711     guint i = 0;
2712     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2713        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2714         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2715            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2716             i++;
2717             if (node->selected) {
2718                 r = g_list_append(r, GINT_TO_POINTER(i));
2719             }
2720         }
2721     }
2722     return r;
2725 /**
2726 \brief  Restores selection by selecting nodes whose positions are in the list
2727 */
2728 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2730     sp_nodepath_deselect(nodepath);
2732     guint i = 0;
2733     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2734        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2735         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2736            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2737             i++;
2738             if (g_list_find(r, GINT_TO_POINTER(i))) {
2739                 sp_nodepath_node_select(node, TRUE, TRUE);
2740             }
2741         }
2742     }
2746 /**
2747 \brief Adjusts handle according to node type and line code.
2748 */
2749 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2751     double len, otherlen, linelen;
2753     g_assert(node);
2755    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2756    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2758     /** \todo fixme: */
2759     if (me->other == NULL) return;
2760     if (other->other == NULL) return;
2762     /* I have line */
2764     NRPathcode mecode, ocode;
2765     if (which_adjust == 1) {
2766         mecode = (NRPathcode)me->other->code;
2767         ocode = (NRPathcode)node->code;
2768     } else {
2769         mecode = (NRPathcode)node->code;
2770         ocode = (NRPathcode)other->other->code;
2771     }
2773     if (mecode == NR_LINETO) return;
2775     /* I am curve */
2777     if (other->other == NULL) return;
2779     /* Other has line */
2781     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2783     NR::Point delta;
2784     if (ocode == NR_LINETO) {
2785         /* other is lineto, we are either smooth or symm */
2786        Inkscape::NodePath::Node *othernode = other->other;
2787         len = NR::L2(me->pos - node->pos);
2788         delta = node->pos - othernode->pos;
2789         linelen = NR::L2(delta);
2790         if (linelen < 1e-18) 
2791             return;
2792         me->pos = node->pos + (len / linelen)*delta;
2793         return;
2794     }
2796     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2798         me->pos = 2 * node->pos - other->pos;
2799         return;
2800     }
2802     /* We are smooth */
2804     len = NR::L2(me->pos - node->pos);
2805     delta = other->pos - node->pos;
2806     otherlen = NR::L2(delta);
2807     if (otherlen < 1e-18) return;
2809     me->pos = node->pos - (len / otherlen) * delta;
2812 /**
2813  \brief Adjusts both handles according to node type and line code
2814  */
2815 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2817     g_assert(node);
2819     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2821     /* we are either smooth or symm */
2823     if (node->p.other == NULL) return;
2825     if (node->n.other == NULL) return;
2827     if (node->code == NR_LINETO) {
2828         if (node->n.other->code == NR_LINETO) return;
2829         sp_node_adjust_handle(node, 1);
2830         return;
2831     }
2833     if (node->n.other->code == NR_LINETO) {
2834         if (node->code == NR_LINETO) return;
2835         sp_node_adjust_handle(node, -1);
2836         return;
2837     }
2839     /* both are curves */
2840     NR::Point const delta( node->n.pos - node->p.pos );
2842     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2843         node->p.pos = node->pos - delta / 2;
2844         node->n.pos = node->pos + delta / 2;
2845         return;
2846     }
2848     /* We are smooth */
2849     double plen = NR::L2(node->p.pos - node->pos);
2850     if (plen < 1e-18) return;
2851     double nlen = NR::L2(node->n.pos - node->pos);
2852     if (nlen < 1e-18) return;
2853     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2854     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2857 /**
2858  * Node event callback.
2859  */
2860 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2862     gboolean ret = FALSE;
2863     switch (event->type) {
2864         case GDK_ENTER_NOTIFY:
2865             active_node = n;
2866             break;
2867         case GDK_LEAVE_NOTIFY:
2868             active_node = NULL;
2869             break;
2870         case GDK_KEY_PRESS:
2871             switch (get_group0_keyval (&event->key)) {
2872                 case GDK_space:
2873                     if (event->key.state & GDK_BUTTON1_MASK) {
2874                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2875                         stamp_repr(nodepath);
2876                         ret = TRUE;
2877                     }
2878                     break;
2879                 case GDK_Page_Up:
2880                     if (event->key.state & GDK_CONTROL_MASK) {
2881                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2882                     } else {
2883                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2884                     }
2885                     break;
2886                 case GDK_Page_Down:
2887                     if (event->key.state & GDK_CONTROL_MASK) {
2888                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2889                     } else {
2890                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2891                     }
2892                     break;
2893                 default:
2894                     break;
2895             }
2896             break;
2897         default:
2898             break;
2899     }
2901     return ret;
2904 /**
2905  * Handle keypress on node; directly called.
2906  */
2907 gboolean node_key(GdkEvent *event)
2909     Inkscape::NodePath::Path *np;
2911     // there is no way to verify nodes so set active_node to nil when deleting!!
2912     if (active_node == NULL) return FALSE;
2914     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2915         gint ret = FALSE;
2916         switch (get_group0_keyval (&event->key)) {
2917             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2918             case GDK_BackSpace:
2919                 np = active_node->subpath->nodepath;
2920                 sp_nodepath_node_destroy(active_node);
2921                 sp_nodepath_update_repr(np, _("Delete node"));
2922                 active_node = NULL;
2923                 ret = TRUE;
2924                 break;
2925             case GDK_c:
2926                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2927                 ret = TRUE;
2928                 break;
2929             case GDK_s:
2930                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2931                 ret = TRUE;
2932                 break;
2933             case GDK_y:
2934                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2935                 ret = TRUE;
2936                 break;
2937             case GDK_b:
2938                 sp_nodepath_node_break(active_node);
2939                 ret = TRUE;
2940                 break;
2941         }
2942         return ret;
2943     }
2944     return FALSE;
2947 /**
2948  * Mouseclick on node callback.
2949  */
2950 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2952    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2954     if (state & GDK_CONTROL_MASK) {
2955         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2957         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2958             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2959                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2960             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2961                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2962             } else {
2963                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2964             }
2965             sp_nodepath_update_repr(nodepath, _("Change node type"));
2966             sp_nodepath_update_statusbar(nodepath);
2968         } else { //ctrl+alt+click: delete node
2969             GList *node_to_delete = NULL;
2970             node_to_delete = g_list_append(node_to_delete, n);
2971             sp_node_delete_preserve(node_to_delete);
2972         }
2974     } else {
2975         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2976     }
2979 /**
2980  * Mouse grabbed node callback.
2981  */
2982 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2984    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2986     if (!n->selected) {
2987         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2988     }
2990     n->is_dragging = true;
2991     //sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
2993     sp_nodepath_remember_origins (n->subpath->nodepath);
2996 /**
2997  * Mouse ungrabbed node callback.
2998  */
2999 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
3001    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3003    n->dragging_out = NULL;
3004    n->is_dragging = false;
3005    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3007    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3010 /**
3011  * The point on a line, given by its angle, closest to the given point.
3012  * \param p  A point.
3013  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3014  * \param closest  Pointer to the point struct where the result is stored.
3015  * \todo FIXME: use dot product perhaps?
3016  */
3017 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3019     if (a == HUGE_VAL) { // vertical
3020         *closest = NR::Point(0, (*p)[NR::Y]);
3021     } else {
3022         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3023         (*closest)[NR::Y] = a * (*closest)[NR::X];
3024     }
3027 /**
3028  * Distance from the point to a line given by its angle.
3029  * \param p  A point.
3030  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3031  */
3032 static double point_line_distance(NR::Point *p, double a)
3034     NR::Point c;
3035     point_line_closest(p, a, &c);
3036     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]));
3039 /**
3040  * Callback for node "request" signal.
3041  * \todo fixme: This goes to "moved" event? (lauris)
3042  */
3043 static gboolean
3044 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3046     double yn, xn, yp, xp;
3047     double an, ap, na, pa;
3048     double d_an, d_ap, d_na, d_pa;
3049     gboolean collinear = FALSE;
3050     NR::Point c;
3051     NR::Point pr;
3053    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3055    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3056    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3058        NR::Point mouse = (*p);
3060        if (!n->dragging_out) {
3061            // This is the first drag-out event; find out which handle to drag out
3062            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3063            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3065            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3066                return FALSE;
3068            Inkscape::NodePath::NodeSide *opposite;
3069            if (appr_p > appr_n) { // closer to p
3070                n->dragging_out = &n->p;
3071                opposite = &n->n;
3072                n->code = NR_CURVETO;
3073            } else if (appr_p < appr_n) { // closer to n
3074                n->dragging_out = &n->n;
3075                opposite = &n->p;
3076                n->n.other->code = NR_CURVETO;
3077            } else { // p and n nodes are the same
3078                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3079                    n->dragging_out = &n->p;
3080                    opposite = &n->n;
3081                    n->code = NR_CURVETO;
3082                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3083                    n->dragging_out = &n->n;
3084                    opposite = &n->p;
3085                    n->n.other->code = NR_CURVETO;
3086                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3087                    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);
3088                    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);
3089                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3090                        n->dragging_out = &n->n;
3091                        opposite = &n->p;
3092                        n->n.other->code = NR_CURVETO;
3093                    } else { // closer to other's n handle
3094                        n->dragging_out = &n->p;
3095                        opposite = &n->n;
3096                        n->code = NR_CURVETO;
3097                    }
3098                }
3099            }
3101            // if there's another handle, make sure the one we drag out starts parallel to it
3102            if (opposite->pos != n->pos) {
3103                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3104            }
3106            // knots might not be created yet!
3107            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3108            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3109        }
3111        // pass this on to the handle-moved callback
3112        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3113        sp_node_update_handles(n);
3114        return TRUE;
3115    }
3117     if (state & GDK_CONTROL_MASK) { // constrained motion
3119         // calculate relative distances of handles
3120         // n handle:
3121         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3122         xn = n->n.pos[NR::X] - n->pos[NR::X];
3123         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3124         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3125             if (n->n.other) { // if there is the next point
3126                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3127                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3128                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3129             }
3130         }
3131         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3132         if (yn < 0) { xn = -xn; yn = -yn; }
3134         // p handle:
3135         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3136         xp = n->p.pos[NR::X] - n->pos[NR::X];
3137         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3138         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3139             if (n->p.other) {
3140                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3141                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3142                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3143             }
3144         }
3145         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3146         if (yp < 0) { xp = -xp; yp = -yp; }
3148         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3149             // sliding on handles, only if at least one of the handles is non-vertical
3150             // (otherwise it's the same as ctrl+drag anyway)
3152             // calculate angles of the handles
3153             if (xn == 0) {
3154                 if (yn == 0) { // no handle, consider it the continuation of the other one
3155                     an = 0;
3156                     collinear = TRUE;
3157                 }
3158                 else an = 0; // vertical; set the angle to horizontal
3159             } else an = yn/xn;
3161             if (xp == 0) {
3162                 if (yp == 0) { // no handle, consider it the continuation of the other one
3163                     ap = an;
3164                 }
3165                 else ap = 0; // vertical; set the angle to horizontal
3166             } else  ap = yp/xp;
3168             if (collinear) an = ap;
3170             // angles of the perpendiculars; HUGE_VAL means vertical
3171             if (an == 0) na = HUGE_VAL; else na = -1/an;
3172             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3174             // mouse point relative to the node's original pos
3175             pr = (*p) - n->origin;
3177             // distances to the four lines (two handles and two perpendiculars)
3178             d_an = point_line_distance(&pr, an);
3179             d_na = point_line_distance(&pr, na);
3180             d_ap = point_line_distance(&pr, ap);
3181             d_pa = point_line_distance(&pr, pa);
3183             // find out which line is the closest, save its closest point in c
3184             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3185                 point_line_closest(&pr, an, &c);
3186             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3187                 point_line_closest(&pr, ap, &c);
3188             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3189                 point_line_closest(&pr, na, &c);
3190             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3191                 point_line_closest(&pr, pa, &c);
3192             }
3194             // move the node to the closest point
3195             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3196                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3197                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3199         } else {  // constraining to hor/vert
3201             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3202                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3203             } else { // snap to vert
3204                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3205             }
3206         }
3207     } else { // move freely
3208         if (n->is_dragging) {
3209             if (state & GDK_MOD1_MASK) { // sculpt
3210                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3211             } else {
3212                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3213                                             (*p)[NR::X] - n->pos[NR::X],
3214                                             (*p)[NR::Y] - n->pos[NR::Y],
3215                                             (state & GDK_SHIFT_MASK) == 0);
3216             }
3217         }
3218     }
3220     n->subpath->nodepath->desktop->scroll_to_point(p);
3222     return TRUE;
3225 /**
3226  * Node handle clicked callback.
3227  */
3228 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3230    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3232     if (state & GDK_CONTROL_MASK) { // "delete" handle
3233         if (n->p.knot == knot) {
3234             n->p.pos = n->pos;
3235         } else if (n->n.knot == knot) {
3236             n->n.pos = n->pos;
3237         }
3238         sp_node_update_handles(n);
3239         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3240         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3241         sp_nodepath_update_statusbar(nodepath);
3243     } else { // just select or add to selection, depending in Shift
3244         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3245     }
3248 /**
3249  * Node handle grabbed callback.
3250  */
3251 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3253    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3255     if (!n->selected) {
3256         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3257     }
3259     // remember the origin point of the handle
3260     if (n->p.knot == knot) {
3261         n->p.origin_radial = n->p.pos - n->pos;
3262     } else if (n->n.knot == knot) {
3263         n->n.origin_radial = n->n.pos - n->pos;
3264     } else {
3265         g_assert_not_reached();
3266     }
3268     //sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3271 /**
3272  * Node handle ungrabbed callback.
3273  */
3274 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3276    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3278     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3279     if (n->p.knot == knot) {
3280         n->p.origin_radial.a = 0;
3281         sp_knot_set_position(knot, &n->p.pos, state);
3282     } else if (n->n.knot == knot) {
3283         n->n.origin_radial.a = 0;
3284         sp_knot_set_position(knot, &n->n.pos, state);
3285     } else {
3286         g_assert_not_reached();
3287     }
3289     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3290     //sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3293 /**
3294  * Node handle "request" signal callback.
3295  */
3296 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3298     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3300     Inkscape::NodePath::NodeSide *me, *opposite;
3301     gint which;
3302     if (n->p.knot == knot) {
3303         me = &n->p;
3304         opposite = &n->n;
3305         which = -1;
3306     } else if (n->n.knot == knot) {
3307         me = &n->n;
3308         opposite = &n->p;
3309         which = 1;
3310     } else {
3311         me = opposite = NULL;
3312         which = 0;
3313         g_assert_not_reached();
3314     }
3316     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3318     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3320     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3321         /* We are smooth node adjacent with line */
3322         NR::Point const delta = *p - n->pos;
3323         NR::Coord const len = NR::L2(delta);
3324         Inkscape::NodePath::Node *othernode = opposite->other;
3325         NR::Point const ndelta = n->pos - othernode->pos;
3326         NR::Coord const linelen = NR::L2(ndelta);
3327         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3328             NR::Coord const scal = dot(delta, ndelta) / linelen;
3329             (*p) = n->pos + (scal / linelen) * ndelta;
3330         }
3331         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3332     } else {
3333         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3334     }
3336     sp_node_adjust_handle(n, -which);
3338     return FALSE;
3341 /**
3342  * Node handle moved callback.
3343  */
3344 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3346    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3348    Inkscape::NodePath::NodeSide *me;
3349    Inkscape::NodePath::NodeSide *other;
3350     if (n->p.knot == knot) {
3351         me = &n->p;
3352         other = &n->n;
3353     } else if (n->n.knot == knot) {
3354         me = &n->n;
3355         other = &n->p;
3356     } else {
3357         me = NULL;
3358         other = NULL;
3359         g_assert_not_reached();
3360     }
3362     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3363     Radial rme(me->pos - n->pos);
3364     Radial rother(other->pos - n->pos);
3365     Radial rnew(*p - n->pos);
3367     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3368         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3369         /* 0 interpreted as "no snapping". */
3371         // The closest PI/snaps angle, starting from zero.
3372         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3373         if (me->origin_radial.a == HUGE_VAL) {
3374             // ortho doesn't exist: original handle was zero length.
3375             rnew.a = a_snapped;
3376         } else {
3377             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3378              * its opposite and perpendiculars). */
3379             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3381             // Snap to the closest.
3382             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3383                        ? a_snapped
3384                        : a_ortho );
3385         }
3386     }
3388     if (state & GDK_MOD1_MASK) {
3389         // lock handle length
3390         rnew.r = me->origin_radial.r;
3391     }
3393     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3394         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3395         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3396         rother.a += rnew.a - rme.a;
3397         other->pos = NR::Point(rother) + n->pos;
3398         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3399         sp_knot_set_position(other->knot, &other->pos, 0);
3400     }
3402     me->pos = NR::Point(rnew) + n->pos;
3403     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3405     // this is what sp_knot_set_position does, but without emitting the signal:
3406     // we cannot emit a "moved" signal because we're now processing it
3407     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3409     knot->desktop->set_coordinate_status(me->pos);
3411     update_object(n->subpath->nodepath);
3413     /* status text */
3414     SPDesktop *desktop = n->subpath->nodepath->desktop;
3415     if (!desktop) return;
3416     SPEventContext *ec = desktop->event_context;
3417     if (!ec) return;
3418     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3419     if (!mc) return;
3421     double degrees = 180 / M_PI * rnew.a;
3422     if (degrees > 180) degrees -= 360;
3423     if (degrees < -180) degrees += 360;
3424     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3425         degrees = angle_to_compass (degrees);
3427     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3429     mc->setF(Inkscape::NORMAL_MESSAGE,
3430          _("<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);
3432     g_string_free(length, TRUE);
3435 /**
3436  * Node handle event callback.
3437  */
3438 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3440     gboolean ret = FALSE;
3441     switch (event->type) {
3442         case GDK_KEY_PRESS:
3443             switch (get_group0_keyval (&event->key)) {
3444                 case GDK_space:
3445                     if (event->key.state & GDK_BUTTON1_MASK) {
3446                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3447                         stamp_repr(nodepath);
3448                         ret = TRUE;
3449                     }
3450                     break;
3451                 default:
3452                     break;
3453             }
3454             break;
3455         default:
3456             break;
3457     }
3459     return ret;
3462 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3463                                  Radial &rme, Radial &rother, gboolean const both)
3465     rme.a += angle;
3466     if ( both
3467          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3468          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3469     {
3470         rother.a += angle;
3471     }
3474 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3475                                         Radial &rme, Radial &rother, gboolean const both)
3477     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3479     gdouble r;
3480     if ( both
3481          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3482          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3483     {
3484         r = MAX(rme.r, rother.r);
3485     } else {
3486         r = rme.r;
3487     }
3489     gdouble const weird_angle = atan2(norm_angle, r);
3490 /* Bulia says norm_angle is just the visible distance that the
3491  * object's end must travel on the screen.  Left as 'angle' for want of
3492  * a better name.*/
3494     rme.a += weird_angle;
3495     if ( both
3496          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3497          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3498     {
3499         rother.a += weird_angle;
3500     }
3503 /**
3504  * Rotate one node.
3505  */
3506 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3508     Inkscape::NodePath::NodeSide *me, *other;
3509     bool both = false;
3511     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3512     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3514     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3515         me = &(n->p);
3516         other = &(n->n);
3517     } else if (!n->p.other) {
3518         me = &(n->n);
3519         other = &(n->p);
3520     } else {
3521         if (which > 0) { // right handle
3522             if (xn > xp) {
3523                 me = &(n->n);
3524                 other = &(n->p);
3525             } else {
3526                 me = &(n->p);
3527                 other = &(n->n);
3528             }
3529         } else if (which < 0){ // left handle
3530             if (xn <= xp) {
3531                 me = &(n->n);
3532                 other = &(n->p);
3533             } else {
3534                 me = &(n->p);
3535                 other = &(n->n);
3536             }
3537         } else { // both handles
3538             me = &(n->n);
3539             other = &(n->p);
3540             both = true;
3541         }
3542     }
3544     Radial rme(me->pos - n->pos);
3545     Radial rother(other->pos - n->pos);
3547     if (screen) {
3548         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3549     } else {
3550         node_rotate_one_internal (*n, angle, rme, rother, both);
3551     }
3553     me->pos = n->pos + NR::Point(rme);
3555     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3556         other->pos =  n->pos + NR::Point(rother);
3557     }
3559     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3560     // so here we just move all the knots without emitting move signals, for speed
3561     sp_node_update_handles(n, false);
3564 /**
3565  * Rotate selected nodes.
3566  */
3567 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3569     if (!nodepath || !nodepath->selected) return;
3571     if (g_list_length(nodepath->selected) == 1) {
3572        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3573         node_rotate_one (n, angle, which, screen);
3574     } else {
3575        // rotate as an object:
3577         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3578         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3579         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3580             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3581             box.expandTo (n->pos); // contain all selected nodes
3582         }
3584         gdouble rot;
3585         if (screen) {
3586             gdouble const zoom = nodepath->desktop->current_zoom();
3587             gdouble const zmove = angle / zoom;
3588             gdouble const r = NR::L2(box.max() - box.midpoint());
3589             rot = atan2(zmove, r);
3590         } else {
3591             rot = angle;
3592         }
3594         NR::Matrix t =
3595             NR::Matrix (NR::translate(-box.midpoint())) *
3596             NR::Matrix (NR::rotate(rot)) *
3597             NR::Matrix (NR::translate(box.midpoint()));
3599         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3600             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3601             n->pos *= t;
3602             n->n.pos *= t;
3603             n->p.pos *= t;
3604             sp_node_update_handles(n, false);
3605         }
3606     }
3608     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3611 /**
3612  * Scale one node.
3613  */
3614 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3616     bool both = false;
3617     Inkscape::NodePath::NodeSide *me, *other;
3619     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3620     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3622     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3623         me = &(n->p);
3624         other = &(n->n);
3625         n->code = NR_CURVETO;
3626     } else if (!n->p.other) {
3627         me = &(n->n);
3628         other = &(n->p);
3629         if (n->n.other)
3630             n->n.other->code = NR_CURVETO;
3631     } else {
3632         if (which > 0) { // right handle
3633             if (xn > xp) {
3634                 me = &(n->n);
3635                 other = &(n->p);
3636                 if (n->n.other)
3637                     n->n.other->code = NR_CURVETO;
3638             } else {
3639                 me = &(n->p);
3640                 other = &(n->n);
3641                 n->code = NR_CURVETO;
3642             }
3643         } else if (which < 0){ // left handle
3644             if (xn <= xp) {
3645                 me = &(n->n);
3646                 other = &(n->p);
3647                 if (n->n.other)
3648                     n->n.other->code = NR_CURVETO;
3649             } else {
3650                 me = &(n->p);
3651                 other = &(n->n);
3652                 n->code = NR_CURVETO;
3653             }
3654         } else { // both handles
3655             me = &(n->n);
3656             other = &(n->p);
3657             both = true;
3658             n->code = NR_CURVETO;
3659             if (n->n.other)
3660                 n->n.other->code = NR_CURVETO;
3661         }
3662     }
3664     Radial rme(me->pos - n->pos);
3665     Radial rother(other->pos - n->pos);
3667     rme.r += grow;
3668     if (rme.r < 0) rme.r = 0;
3669     if (rme.a == HUGE_VAL) {
3670         if (me->other) { // if direction is unknown, initialize it towards the next node
3671             Radial rme_next(me->other->pos - n->pos);
3672             rme.a = rme_next.a;
3673         } else { // if there's no next, initialize to 0
3674             rme.a = 0;
3675         }
3676     }
3677     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3678         rother.r += grow;
3679         if (rother.r < 0) rother.r = 0;
3680         if (rother.a == HUGE_VAL) {
3681             rother.a = rme.a + M_PI;
3682         }
3683     }
3685     me->pos = n->pos + NR::Point(rme);
3687     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3688         other->pos = n->pos + NR::Point(rother);
3689     }
3691     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3692     // so here we just move all the knots without emitting move signals, for speed
3693     sp_node_update_handles(n, false);
3696 /**
3697  * Scale selected nodes.
3698  */
3699 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3701     if (!nodepath || !nodepath->selected) return;
3703     if (g_list_length(nodepath->selected) == 1) {
3704         // scale handles of the single selected node
3705         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3706         node_scale_one (n, grow, which);
3707     } else {
3708         // scale nodes as an "object":
3710         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3711         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3712         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3713             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3714             box.expandTo (n->pos); // contain all selected nodes
3715         }
3717         double scale = (box.maxExtent() + grow)/box.maxExtent();
3719         NR::Matrix t =
3720             NR::Matrix (NR::translate(-box.midpoint())) *
3721             NR::Matrix (NR::scale(scale, scale)) *
3722             NR::Matrix (NR::translate(box.midpoint()));
3724         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3725             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3726             n->pos *= t;
3727             n->n.pos *= t;
3728             n->p.pos *= t;
3729             sp_node_update_handles(n, false);
3730         }
3731     }
3733     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3736 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3738     if (!nodepath) return;
3739     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3742 /**
3743  * Flip selected nodes horizontally/vertically.
3744  */
3745 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3747     if (!nodepath || !nodepath->selected) return;
3749     if (g_list_length(nodepath->selected) == 1) {
3750         // flip handles of the single selected node
3751         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3752         double temp = n->p.pos[axis];
3753         n->p.pos[axis] = n->n.pos[axis];
3754         n->n.pos[axis] = temp;
3755         sp_node_update_handles(n, false);
3756     } else {
3757         // scale nodes as an "object":
3759         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3760         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3761         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3762             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3763             box.expandTo (n->pos); // contain all selected nodes
3764         }
3766         NR::Matrix t =
3767             NR::Matrix (NR::translate(-box.midpoint())) *
3768             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3769             NR::Matrix (NR::translate(box.midpoint()));
3771         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3772             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3773             n->pos *= t;
3774             n->n.pos *= t;
3775             n->p.pos *= t;
3776             sp_node_update_handles(n, false);
3777         }
3778     }
3780     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3783 //-----------------------------------------------
3784 /**
3785  * Return new subpath under given nodepath.
3786  */
3787 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3789     g_assert(nodepath);
3790     g_assert(nodepath->desktop);
3792    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3794     s->nodepath = nodepath;
3795     s->closed = FALSE;
3796     s->nodes = NULL;
3797     s->first = NULL;
3798     s->last = NULL;
3800     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3801     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3802     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3804     return s;
3807 /**
3808  * Destroy nodes in subpath, then subpath itself.
3809  */
3810 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3812     g_assert(subpath);
3813     g_assert(subpath->nodepath);
3814     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3816     while (subpath->nodes) {
3817         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3818     }
3820     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3822     g_free(subpath);
3825 /**
3826  * Link head to tail in subpath.
3827  */
3828 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3830     g_assert(!sp->closed);
3831     g_assert(sp->last != sp->first);
3832     g_assert(sp->first->code == NR_MOVETO);
3834     sp->closed = TRUE;
3836     //Link the head to the tail
3837     sp->first->p.other = sp->last;
3838     sp->last->n.other  = sp->first;
3839     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3840     sp->first          = sp->last;
3842     //Remove the extra end node
3843     sp_nodepath_node_destroy(sp->last->n.other);
3846 /**
3847  * Open closed (loopy) subpath at node.
3848  */
3849 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3851     g_assert(sp->closed);
3852     g_assert(n->subpath == sp);
3853     g_assert(sp->first == sp->last);
3855     /* We create new startpoint, current node will become last one */
3857    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3858                                                 &n->pos, &n->pos, &n->n.pos);
3861     sp->closed        = FALSE;
3863     //Unlink to make a head and tail
3864     sp->first         = new_path;
3865     sp->last          = n;
3866     n->n.other        = NULL;
3867     new_path->p.other = NULL;
3870 /**
3871  * Returns area in triangle given by points; may be negative.
3872  */
3873 inline double
3874 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3876     return (p1[NR::X]*p2[NR::Y] + p1[NR::Y]*p3[NR::X] + p2[NR::X]*p3[NR::Y] - p2[NR::Y]*p3[NR::X] - p1[NR::Y]*p2[NR::X] - p1[NR::X]*p3[NR::Y]);
3879 /**
3880  * Return new node in subpath with given properties.
3881  * \param pos Position of node.
3882  * \param ppos Handle position in previous direction
3883  * \param npos Handle position in previous direction
3884  */
3885 Inkscape::NodePath::Node *
3886 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)
3888     g_assert(sp);
3889     g_assert(sp->nodepath);
3890     g_assert(sp->nodepath->desktop);
3892     if (nodechunk == NULL)
3893         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3895     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3897     n->subpath  = sp;
3899     if (type != Inkscape::NodePath::NODE_NONE) {
3900         // use the type from sodipodi:nodetypes
3901         n->type = type;
3902     } else {
3903         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3904             // points are (almost) collinear
3905             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3906                 // endnode, or a node with a retracted handle
3907                 n->type = Inkscape::NodePath::NODE_CUSP;
3908             } else {
3909                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3910             }
3911         } else {
3912             n->type = Inkscape::NodePath::NODE_CUSP;
3913         }
3914     }
3916     n->code     = code;
3917     n->selected = FALSE;
3918     n->pos      = *pos;
3919     n->p.pos    = *ppos;
3920     n->n.pos    = *npos;
3922     n->dragging_out = NULL;
3924     Inkscape::NodePath::Node *prev;
3925     if (next) {
3926         //g_assert(g_list_find(sp->nodes, next));
3927         prev = next->p.other;
3928     } else {
3929         prev = sp->last;
3930     }
3932     if (prev)
3933         prev->n.other = n;
3934     else
3935         sp->first = n;
3937     if (next)
3938         next->p.other = n;
3939     else
3940         sp->last = n;
3942     n->p.other = prev;
3943     n->n.other = next;
3945     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"));
3946     sp_knot_set_position(n->knot, pos, 0);
3948     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3949     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3950     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3951     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3952     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3953     sp_knot_update_ctrl(n->knot);
3955     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3956     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3957     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3958     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3959     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3960     sp_knot_show(n->knot);
3962     // We only create handle knots and lines on demand
3963     n->p.knot = NULL;
3964     n->p.line = NULL;
3965     n->n.knot = NULL;
3966     n->n.line = NULL;
3968     sp->nodes = g_list_prepend(sp->nodes, n);
3970     return n;
3973 /**
3974  * Destroy node and its knots, link neighbors in subpath.
3975  */
3976 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3978     g_assert(node);
3979     g_assert(node->subpath);
3980     g_assert(SP_IS_KNOT(node->knot));
3982    Inkscape::NodePath::SubPath *sp = node->subpath;
3984     if (node->selected) { // first, deselect
3985         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3986         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3987     }
3989     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3991     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
3992     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
3993     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
3994     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
3995     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
3996     g_object_unref(G_OBJECT(node->knot));
3998     if (node->p.knot) {
3999         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4000         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4001         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4002         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4003         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4004         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4005         g_object_unref(G_OBJECT(node->p.knot));
4006     }
4008     if (node->n.knot) {
4009         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4010         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4011         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4012         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4013         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4014         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4015         g_object_unref(G_OBJECT(node->n.knot));
4016     }
4018     if (node->p.line)
4019         gtk_object_destroy(GTK_OBJECT(node->p.line));
4020     if (node->n.line)
4021         gtk_object_destroy(GTK_OBJECT(node->n.line));
4023     if (sp->nodes) { // there are others nodes on the subpath
4024         if (sp->closed) {
4025             if (sp->first == node) {
4026                 g_assert(sp->last == node);
4027                 sp->first = node->n.other;
4028                 sp->last = sp->first;
4029             }
4030             node->p.other->n.other = node->n.other;
4031             node->n.other->p.other = node->p.other;
4032         } else {
4033             if (sp->first == node) {
4034                 sp->first = node->n.other;
4035                 sp->first->code = NR_MOVETO;
4036             }
4037             if (sp->last == node) sp->last = node->p.other;
4038             if (node->p.other) node->p.other->n.other = node->n.other;
4039             if (node->n.other) node->n.other->p.other = node->p.other;
4040         }
4041     } else { // this was the last node on subpath
4042         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4043     }
4045     g_mem_chunk_free(nodechunk, node);
4048 /**
4049  * Returns one of the node's two sides.
4050  * \param which Indicates which side.
4051  * \return Pointer to previous node side if which==-1, next if which==1.
4052  */
4053 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4055     g_assert(node);
4057     switch (which) {
4058         case -1:
4059             return &node->p;
4060         case 1:
4061             return &node->n;
4062         default:
4063             break;
4064     }
4066     g_assert_not_reached();
4068     return NULL;
4071 /**
4072  * Return the other side of the node, given one of its sides.
4073  */
4074 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4076     g_assert(node);
4078     if (me == &node->p) return &node->n;
4079     if (me == &node->n) return &node->p;
4081     g_assert_not_reached();
4083     return NULL;
4086 /**
4087  * Return NRPathcode on the given side of the node.
4088  */
4089 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4091     g_assert(node);
4093     if (me == &node->p) {
4094         if (node->p.other) return (NRPathcode)node->code;
4095         return NR_MOVETO;
4096     }
4098     if (me == &node->n) {
4099         if (node->n.other) return (NRPathcode)node->n.other->code;
4100         return NR_MOVETO;
4101     }
4103     g_assert_not_reached();
4105     return NR_END;
4108 /**
4109  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4110  */
4111 Inkscape::NodePath::Node *
4112 sp_nodepath_get_node_by_index(int index)
4114     Inkscape::NodePath::Node *e = NULL;
4116     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4117     if (!nodepath) {
4118         return e;
4119     }
4121     //find segment
4122     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4124         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4125         int n = g_list_length(sp->nodes);
4126         if (sp->closed) {
4127             n++;
4128         }
4130         //if the piece belongs to this subpath grab it
4131         //otherwise move onto the next subpath
4132         if (index < n) {
4133             e = sp->first;
4134             for (int i = 0; i < index; ++i) {
4135                 e = e->n.other;
4136             }
4137             break;
4138         } else {
4139             if (sp->closed) {
4140                 index -= (n+1);
4141             } else {
4142                 index -= n;
4143             }
4144         }
4145     }
4147     return e;
4150 /**
4151  * Returns plain text meaning of node type.
4152  */
4153 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4155     unsigned retracted = 0;
4156     bool endnode = false;
4158     for (int which = -1; which <= 1; which += 2) {
4159         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4160         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4161             retracted ++;
4162         if (!side->other)
4163             endnode = true;
4164     }
4166     if (retracted == 0) {
4167         if (endnode) {
4168                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4169                 return _("end node");
4170         } else {
4171             switch (node->type) {
4172                 case Inkscape::NodePath::NODE_CUSP:
4173                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4174                     return _("cusp");
4175                 case Inkscape::NodePath::NODE_SMOOTH:
4176                     // TRANSLATORS: "smooth" is an adjective here
4177                     return _("smooth");
4178                 case Inkscape::NodePath::NODE_SYMM:
4179                     return _("symmetric");
4180             }
4181         }
4182     } else if (retracted == 1) {
4183         if (endnode) {
4184             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4185             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4186         } else {
4187             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4188         }
4189     } else {
4190         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4191     }
4193     return NULL;
4196 /**
4197  * Handles content of statusbar as long as node tool is active.
4198  */
4199 void
4200 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
4202     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");
4203     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4205     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4206     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4207     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4208     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4210     SPDesktop *desktop = NULL;
4211     if (nodepath) {
4212         desktop = nodepath->desktop;
4213     } else {
4214         desktop = SP_ACTIVE_DESKTOP;
4215     }
4217     SPEventContext *ec = desktop->event_context;
4218     if (!ec) return;
4219     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4220     if (!mc) return;
4222     if (selected_nodes == 0) {
4223         Inkscape::Selection *sel = desktop->selection;
4224         if (!sel || sel->isEmpty()) {
4225             mc->setF(Inkscape::NORMAL_MESSAGE,
4226                      _("Select a single object to edit its nodes or handles."));
4227         } else {
4228             if (nodepath) {
4229             mc->setF(Inkscape::NORMAL_MESSAGE,
4230                      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.",
4231                               "<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.",
4232                               total_nodes),
4233                      total_nodes);
4234             } else {
4235                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4236                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4237                 } else {
4238                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4239                 }
4240             }
4241         }
4242     } else if (nodepath && selected_nodes == 1) {
4243         mc->setF(Inkscape::NORMAL_MESSAGE,
4244                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4245                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4246                           total_nodes),
4247                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4248     } else {
4249         if (selected_subpaths > 1) {
4250             mc->setF(Inkscape::NORMAL_MESSAGE,
4251                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4252                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4253                               total_nodes),
4254                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4255         } else {
4256             mc->setF(Inkscape::NORMAL_MESSAGE,
4257                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4258                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4259                               total_nodes),
4260                      selected_nodes, total_nodes, when_selected);
4261         }
4262     }
4266 /*
4267   Local Variables:
4268   mode:c++
4269   c-file-style:"stroustrup"
4270   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4271   indent-tabs-mode:nil
4272   fill-column:99
4273   End:
4274 */
4275 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :