Code

plumb XML::Document parameter into duplication, courtesy of bryce
[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 "shape-editor.h"
35 #include "selection-chemistry.h"
36 #include "selection.h"
37 #include "xml/repr.h"
38 #include "prefs-utils.h"
39 #include "sp-metrics.h"
40 #include "sp-path.h"
41 #include "libnr/nr-matrix-ops.h"
42 #include "splivarot.h"
43 #include "svg/svg.h"
44 #include "verbs.h"
45 #include "display/bezier-utils.h"
46 #include <vector>
47 #include <algorithm>
49 class NR::Matrix;
51 /// \todo
52 /// evil evil evil. FIXME: conflict of two different Path classes!
53 /// There is a conflict in the namespace between two classes named Path.
54 /// #include "sp-flowtext.h"
55 /// #include "sp-flowregion.h"
57 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
58 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
59 GType sp_flowregion_get_type (void);
60 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
61 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
62 GType sp_flowtext_get_type (void);
63 // end evil workaround
65 #include "helper/stlport.h"
68 /// \todo fixme: Implement these via preferences */
70 #define NODE_FILL          0xbfbfbf00
71 #define NODE_STROKE        0x000000ff
72 #define NODE_FILL_HI       0xff000000
73 #define NODE_STROKE_HI     0x000000ff
74 #define NODE_FILL_SEL      0x0000ffff
75 #define NODE_STROKE_SEL    0x000000ff
76 #define NODE_FILL_SEL_HI   0xff000000
77 #define NODE_STROKE_SEL_HI 0x000000ff
78 #define KNOT_FILL          0xffffffff
79 #define KNOT_STROKE        0x000000ff
80 #define KNOT_FILL_HI       0xff000000
81 #define KNOT_STROKE_HI     0x000000ff
83 static GMemChunk *nodechunk = NULL;
85 /* Creation from object */
87 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
88 static gchar *parse_nodetypes(gchar const *types, gint length);
90 /* Object updating */
92 static void stamp_repr(Inkscape::NodePath::Path *np);
93 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
94 static gchar *create_typestr(Inkscape::NodePath::Path *np);
96 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
98 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
100 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
102 /* Adjust handle placement, if the node or the other handle is moved */
103 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
104 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
106 /* Node event callbacks */
107 static void node_clicked(SPKnot *knot, guint state, gpointer data);
108 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
109 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
110 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
112 /* Handle event callbacks */
113 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
114 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
115 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
116 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
117 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
118 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
120 /* Constructors and destructors */
122 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
123 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
124 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
125 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
126 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
127                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
128 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
130 /* Helpers */
132 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
133 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
134 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
136 // active_node indicates mouseover node
137 static Inkscape::NodePath::Node *active_node = NULL;
139 /**
140  * \brief Creates new nodepath from item
141  */
142 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool show_handles)
144     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
146     /** \todo
147      * FIXME: remove this. We don't want to edit paths inside flowtext.
148      * Instead we will build our flowtext with cloned paths, so that the
149      * real paths are outside the flowtext and thus editable as usual.
150      */
151     if (SP_IS_FLOWTEXT(item)) {
152         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
153             if SP_IS_FLOWREGION(child) {
154                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
155                 if (grandchild && SP_IS_PATH(grandchild)) {
156                     item = SP_ITEM(grandchild);
157                     break;
158                 }
159             }
160         }
161     }
163     if (!SP_IS_PATH(item))
164         return NULL;
165     SPPath *path = SP_PATH(item);
166     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
167     if (curve == NULL)
168         return NULL;
170     NArtBpath *bpath = sp_curve_first_bpath(curve);
171     gint length = curve->end;
172     if (length == 0)
173         return NULL; // prevent crash for one-node paths
175     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
176     gchar *typestr = parse_nodetypes(nodetypes, length);
178     //Create new nodepath
179     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
180     if (!np)
181         return NULL;
183     // Set defaults
184     np->desktop     = desktop;
185     np->path        = path;
186     np->subpaths    = NULL;
187     np->selected    = NULL;
188     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
189     np->livarot_path = NULL;
190     np->local_change = 0;
191     np->show_handles = show_handles;
193     // we need to update item's transform from the repr here,
194     // because they may be out of sync when we respond
195     // to a change in repr by regenerating nodepath     --bb
196     sp_object_read_attr(SP_OBJECT(item), "transform");
198     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
199     np->d2i  = np->i2d.inverse();
200     np->repr = repr;
202     // create the subpath(s) from the bpath
203     NArtBpath *b = bpath;
204     while (b->code != NR_END) {
205         b = subpath_from_bpath(np, b, typestr + (b - bpath));
206     }
208     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
209     np->subpaths = g_list_reverse(np->subpaths);
211     g_free(typestr);
212     sp_curve_unref(curve);
214     // create the livarot representation from the same item
215     sp_nodepath_ensure_livarot_path(np);
217     return np;
220 /**
221  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
222  */
223 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
225     if (!np)  //soft fail, like delete
226         return;
228     while (np->subpaths) {
229         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
230     }
232     //Inform the ShapeEditor that made me, if any, that I am gone.
233     if (np->shape_editor)
234         np->shape_editor->nodepath_destroyed();
236     g_assert(!np->selected);
238     if (np->livarot_path) {
239         delete np->livarot_path;
240         np->livarot_path = NULL;
241     }
243     np->desktop = NULL;
245     g_free(np);
249 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
251     if (np && np->livarot_path == NULL && np->path && SP_IS_ITEM(np->path)) {
252         np->livarot_path = Path_for_item (np->path, true, true);
253         if (np->livarot_path)
254             np->livarot_path->ConvertWithBackData(0.01);
255     }
259 /**
260  *  Return the node count of a given NodeSubPath.
261  */
262 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
264     if (!subpath)
265         return 0;
266     gint nodeCount = g_list_length(subpath->nodes);
267     return nodeCount;
270 /**
271  *  Return the node count of a given NodePath.
272  */
273 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
275     if (!np)
276         return 0;
277     gint nodeCount = 0;
278     for (GList *item = np->subpaths ; item ; item=item->next) {
279        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
280         nodeCount += g_list_length(subpath->nodes);
281     }
282     return nodeCount;
285 /**
286  *  Return the subpath count of a given NodePath.
287  */
288 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
290     if (!np)
291         return 0;
292     return g_list_length (np->subpaths);
295 /**
296  *  Return the selected node count of a given NodePath.
297  */
298 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
300     if (!np)
301         return 0;
302     return g_list_length (np->selected);
305 /**
306  *  Return the number of subpaths where nodes are selected in a given NodePath.
307  */
308 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
310     if (!np)
311         return 0;
312     if (!np->selected)
313         return 0;
314     if (!np->selected->next)
315         return 1;
316     gint count = 0;
317     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
318         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
319         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
320             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
321             if (node->selected) {
322                 count ++;
323                 break;
324             }
325         }
326     }
327     return count;
329  
330 /**
331  * Clean up a nodepath after editing.
332  *
333  * Currently we are deleting trivial subpaths.
334  */
335 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
337     GList *badSubPaths = NULL;
339     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
340     for (GList *l = nodepath->subpaths; l ; l=l->next) {
341        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
342        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
343             badSubPaths = g_list_append(badSubPaths, sp);
344     }
346     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
347     //also removes the subpath from nodepath->subpaths
348     for (GList *l = badSubPaths; l ; l=l->next) {
349        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
350         sp_nodepath_subpath_destroy(sp);
351     }
353     g_list_free(badSubPaths);
356 /**
357  * Create new nodepath from b, make it subpath of np.
358  * \param t The node type.
359  * \todo Fixme: t should be a proper type, rather than gchar
360  */
361 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
363     NR::Point ppos, pos, npos;
365     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
367     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
368     bool const closed = (b->code == NR_MOVETO);
370     pos = NR::Point(b->x3, b->y3) * np->i2d;
371     if (b[1].code == NR_CURVETO) {
372         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
373     } else {
374         npos = pos;
375     }
376     Inkscape::NodePath::Node *n;
377     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
378     g_assert(sp->first == n);
379     g_assert(sp->last  == n);
381     b++;
382     t++;
383     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
384         pos = NR::Point(b->x3, b->y3) * np->i2d;
385         if (b->code == NR_CURVETO) {
386             ppos = NR::Point(b->x2, b->y2) * np->i2d;
387         } else {
388             ppos = pos;
389         }
390         if (b[1].code == NR_CURVETO) {
391             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
392         } else {
393             npos = pos;
394         }
395         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
396         b++;
397         t++;
398     }
400     if (closed) sp_nodepath_subpath_close(sp);
402     return b;
405 /**
406  * Convert from sodipodi:nodetypes to new style type string.
407  */
408 static gchar *parse_nodetypes(gchar const *types, gint length)
410     g_assert(length > 0);
412     gchar *typestr = g_new(gchar, length + 1);
414     gint pos = 0;
416     if (types) {
417         for (gint i = 0; types[i] && ( i < length ); i++) {
418             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
419             if (types[i] != '\0') {
420                 switch (types[i]) {
421                     case 's':
422                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
423                         break;
424                     case 'z':
425                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
426                         break;
427                     case 'c':
428                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
429                         break;
430                     default:
431                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
432                         break;
433                 }
434             }
435         }
436     }
438     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
440     return typestr;
443 /**
444  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
445  * updated but repr is not (for speed). Used during curve and node drag.
446  */
447 static void update_object(Inkscape::NodePath::Path *np)
449     g_assert(np);
451     SPCurve *curve = create_curve(np);
453     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
455     sp_curve_unref(curve);
458 /**
459  * Update XML path node with data from path object.
460  */
461 static void update_repr_internal(Inkscape::NodePath::Path *np)
463     g_assert(np);
465     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
467     SPCurve *curve = create_curve(np);
468     gchar *typestr = create_typestr(np);
469     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
471     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
472         np->local_change++;
473         repr->setAttribute("d", svgpath);
474     }
476     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
477         np->local_change++;
478         repr->setAttribute("sodipodi:nodetypes", typestr);
479     }
481     g_free(svgpath);
482     g_free(typestr);
483     sp_curve_unref(curve);
486 /**
487  * Update XML path node with data from path object, commit changes forever.
488  */
489 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
491     //fixme: np can be NULL, so check before proceeding
492     g_return_if_fail(np != NULL);
494     if (np->livarot_path) {
495         delete np->livarot_path;
496         np->livarot_path = NULL;
497     }
499     update_repr_internal(np);
500     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
501     
502     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
503                      annotation);
506 /**
507  * Update XML path node with data from path object, commit changes with undo.
508  */
509 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
511     if (np->livarot_path) {
512         delete np->livarot_path;
513         np->livarot_path = NULL;
514     }
516     update_repr_internal(np);
517     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
518                            annotation);
521 /**
522  * Make duplicate of path, replace corresponding XML node in tree, commit.
523  */
524 static void stamp_repr(Inkscape::NodePath::Path *np)
526     g_assert(np);
528     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
529     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
531     // remember the position of the item
532     gint pos = old_repr->position();
533     // remember parent
534     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
536     SPCurve *curve = create_curve(np);
537     gchar *typestr = create_typestr(np);
539     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
541     new_repr->setAttribute("d", svgpath);
542     new_repr->setAttribute("sodipodi:nodetypes", typestr);
544     // add the new repr to the parent
545     parent->appendChild(new_repr);
546     // move to the saved position
547     new_repr->setPosition(pos > 0 ? pos : 0);
549     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
550                      _("Stamp"));
552     Inkscape::GC::release(new_repr);
553     g_free(svgpath);
554     g_free(typestr);
555     sp_curve_unref(curve);
558 /**
559  * Create curve from path.
560  */
561 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
563     SPCurve *curve = sp_curve_new();
565     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
566        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
567         sp_curve_moveto(curve,
568                         sp->first->pos * np->d2i);
569        Inkscape::NodePath::Node *n = sp->first->n.other;
570         while (n) {
571             NR::Point const end_pt = n->pos * np->d2i;
572             switch (n->code) {
573                 case NR_LINETO:
574                     sp_curve_lineto(curve, end_pt);
575                     break;
576                 case NR_CURVETO:
577                     sp_curve_curveto(curve,
578                                      n->p.other->n.pos * np->d2i,
579                                      n->p.pos * np->d2i,
580                                      end_pt);
581                     break;
582                 default:
583                     g_assert_not_reached();
584                     break;
585             }
586             if (n != sp->last) {
587                 n = n->n.other;
588             } else {
589                 n = NULL;
590             }
591         }
592         if (sp->closed) {
593             sp_curve_closepath(curve);
594         }
595     }
597     return curve;
600 /**
601  * Convert path type string to sodipodi:nodetypes style.
602  */
603 static gchar *create_typestr(Inkscape::NodePath::Path *np)
605     gchar *typestr = g_new(gchar, 32);
606     gint len = 32;
607     gint pos = 0;
609     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
610        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
612         if (pos >= len) {
613             typestr = g_renew(gchar, typestr, len + 32);
614             len += 32;
615         }
617         typestr[pos++] = 'c';
619        Inkscape::NodePath::Node *n;
620         n = sp->first->n.other;
621         while (n) {
622             gchar code;
624             switch (n->type) {
625                 case Inkscape::NodePath::NODE_CUSP:
626                     code = 'c';
627                     break;
628                 case Inkscape::NodePath::NODE_SMOOTH:
629                     code = 's';
630                     break;
631                 case Inkscape::NodePath::NODE_SYMM:
632                     code = 'z';
633                     break;
634                 default:
635                     g_assert_not_reached();
636                     code = '\0';
637                     break;
638             }
640             if (pos >= len) {
641                 typestr = g_renew(gchar, typestr, len + 32);
642                 len += 32;
643             }
645             typestr[pos++] = code;
647             if (n != sp->last) {
648                 n = n->n.other;
649             } else {
650                 n = NULL;
651             }
652         }
653     }
655     if (pos >= len) {
656         typestr = g_renew(gchar, typestr, len + 1);
657         len += 1;
658     }
660     typestr[pos++] = '\0';
662     return typestr;
665 /**
666  * Returns current path in context. // later eliminate this function at all!
667  */
668 static Inkscape::NodePath::Path *sp_nodepath_current()
670     if (!SP_ACTIVE_DESKTOP) {
671         return NULL;
672     }
674     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
676     if (!SP_IS_NODE_CONTEXT(event_context)) {
677         return NULL;
678     }
680     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
685 /**
686  \brief Fills node and handle positions for three nodes, splitting line
687   marked by end at distance t.
688  */
689 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
691     g_assert(new_path != NULL);
692     g_assert(end      != NULL);
694     g_assert(end->p.other == new_path);
695    Inkscape::NodePath::Node *start = new_path->p.other;
696     g_assert(start);
698     if (end->code == NR_LINETO) {
699         new_path->type =Inkscape::NodePath::NODE_CUSP;
700         new_path->code = NR_LINETO;
701         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
702     } else {
703         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
704         new_path->code = NR_CURVETO;
705         gdouble s      = 1 - t;
706         for (int dim = 0; dim < 2; dim++) {
707             NR::Coord const f000 = start->pos[dim];
708             NR::Coord const f001 = start->n.pos[dim];
709             NR::Coord const f011 = end->p.pos[dim];
710             NR::Coord const f111 = end->pos[dim];
711             NR::Coord const f00t = s * f000 + t * f001;
712             NR::Coord const f01t = s * f001 + t * f011;
713             NR::Coord const f11t = s * f011 + t * f111;
714             NR::Coord const f0tt = s * f00t + t * f01t;
715             NR::Coord const f1tt = s * f01t + t * f11t;
716             NR::Coord const fttt = s * f0tt + t * f1tt;
717             start->n.pos[dim]    = f00t;
718             new_path->p.pos[dim] = f0tt;
719             new_path->pos[dim]   = fttt;
720             new_path->n.pos[dim] = f1tt;
721             end->p.pos[dim]      = f11t;
722         }
723     }
726 /**
727  * Adds new node on direct line between two nodes, activates handles of all
728  * three nodes.
729  */
730 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
732     g_assert(end);
733     g_assert(end->subpath);
734     g_assert(g_list_find(end->subpath->nodes, end));
736    Inkscape::NodePath::Node *start = end->p.other;
737     g_assert( start->n.other == end );
738    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
739                                                end,
740                                               Inkscape::NodePath::NODE_SMOOTH,
741                                                (NRPathcode)end->code,
742                                                &start->pos, &start->pos, &start->n.pos);
743     sp_nodepath_line_midpoint(newnode, end, t);
745     sp_node_update_handles(start);
746     sp_node_update_handles(newnode);
747     sp_node_update_handles(end);
749     return newnode;
752 /**
753 \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
754 */
755 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
757     g_assert(node);
758     g_assert(node->subpath);
759     g_assert(g_list_find(node->subpath->nodes, node));
761    Inkscape::NodePath::SubPath *sp = node->subpath;
762     Inkscape::NodePath::Path *np    = sp->nodepath;
764     if (sp->closed) {
765         sp_nodepath_subpath_open(sp, node);
766         return sp->first;
767     } else {
768         // no break for end nodes
769         if (node == sp->first) return NULL;
770         if (node == sp->last ) return NULL;
772         // create a new subpath
773        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
775         // duplicate the break node as start of the new subpath
776        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
778         while (node->n.other) { // copy the remaining nodes into the new subpath
779            Inkscape::NodePath::Node *n  = node->n.other;
780            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);
781             if (n->selected) {
782                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
783             }
784             sp_nodepath_node_destroy(n); // remove the point on the original subpath
785         }
787         return newnode;
788     }
791 /**
792  * Duplicate node and connect to neighbours.
793  */
794 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
796     g_assert(node);
797     g_assert(node->subpath);
798     g_assert(g_list_find(node->subpath->nodes, node));
800    Inkscape::NodePath::SubPath *sp = node->subpath;
802     NRPathcode code = (NRPathcode) node->code;
803     if (code == NR_MOVETO) { // if node is the endnode,
804         node->code = NR_LINETO; // new one is inserted before it, so change that to line
805     }
807     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
809     if (!node->n.other || !node->p.other) // if node is an endnode, select it
810         return node;
811     else
812         return newnode; // otherwise select the newly created node
815 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
817     node->p.pos = (node->pos + (node->pos - node->n.pos));
820 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
822     node->n.pos = (node->pos + (node->pos - node->p.pos));
825 /**
826  * Change line type at node, with side effects on neighbours.
827  */
828 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
830     g_assert(end);
831     g_assert(end->subpath);
832     g_assert(end->p.other);
834     if (end->code == static_cast< guint > ( code ) )
835         return;
837    Inkscape::NodePath::Node *start = end->p.other;
839     end->code = code;
841     if (code == NR_LINETO) {
842         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
843         if (end->n.other) {
844             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
845         }
846         sp_node_adjust_handle(start, -1);
847         sp_node_adjust_handle(end, 1);
848     } else {
849         NR::Point delta = end->pos - start->pos;
850         start->n.pos = start->pos + delta / 3;
851         end->p.pos = end->pos - delta / 3;
852         sp_node_adjust_handle(start, 1);
853         sp_node_adjust_handle(end, -1);
854     }
856     sp_node_update_handles(start);
857     sp_node_update_handles(end);
860 /**
861  * Change node type, and its handles accordingly.
862  */
863 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
865     g_assert(node);
866     g_assert(node->subpath);
868     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
869         return node;
871     if ((node->p.other != NULL) && (node->n.other != NULL)) {
872         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
873             type =Inkscape::NodePath::NODE_CUSP;
874         }
875     }
877     node->type = type;
879     if (node->type == Inkscape::NodePath::NODE_CUSP) {
880         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
881         node->knot->setSize (node->selected? 11 : 9);
882         sp_knot_update_ctrl(node->knot);
883     } else {
884         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
885         node->knot->setSize (node->selected? 9 : 7);
886         sp_knot_update_ctrl(node->knot);
887     }
889     // if one of handles is mouseovered, preserve its position
890     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
891         sp_node_adjust_handle(node, 1);
892     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
893         sp_node_adjust_handle(node, -1);
894     } else {
895         sp_node_adjust_handles(node);
896     }
898     sp_node_update_handles(node);
900     sp_nodepath_update_statusbar(node->subpath->nodepath);
902     return node;
905 /**
906  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
907  * adjacent segments from lines to curves.
908 */
909 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
911     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
912         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
913             // convert adjacent segment BEFORE to curve
914             node->code = NR_CURVETO;
915             NR::Point delta;
916             if (node->n.other != NULL)
917                 delta = node->n.other->pos - node->p.other->pos;
918             else
919                 delta = node->pos - node->p.other->pos;
920             node->p.pos = node->pos - delta / 4;
921             sp_node_update_handles(node);
922         }
924         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
925             // convert adjacent segment AFTER to curve
926             node->n.other->code = NR_CURVETO;
927             NR::Point delta;
928             if (node->p.other != NULL)
929                 delta = node->p.other->pos - node->n.other->pos;
930             else
931                 delta = node->pos - node->n.other->pos;
932             node->n.pos = node->pos - delta / 4;
933             sp_node_update_handles(node);
934         }
935     }
937     sp_nodepath_set_node_type (node, type);
940 /**
941  * Move node to point, and adjust its and neighbouring handles.
942  */
943 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
945     NR::Point delta = p - node->pos;
946     node->pos = p;
948     node->p.pos += delta;
949     node->n.pos += delta;
951     if (node->p.other) {
952         if (node->code == NR_LINETO) {
953             sp_node_adjust_handle(node, 1);
954             sp_node_adjust_handle(node->p.other, -1);
955         }
956     }
957     if (node->n.other) {
958         if (node->n.other->code == NR_LINETO) {
959             sp_node_adjust_handle(node, -1);
960             sp_node_adjust_handle(node->n.other, 1);
961         }
962     }
964     // this function is only called from batch movers that will update display at the end
965     // themselves, so here we just move all the knots without emitting move signals, for speed
966     sp_node_update_handles(node, false);
969 /**
970  * Call sp_node_moveto() for node selection and handle possible snapping.
971  */
972 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
973                                             bool const snap = true)
975     NR::Coord best = NR_HUGE;
976     NR::Point delta(dx, dy);
977     NR::Point best_pt = delta;
979     if (snap) {
980         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
981         
982         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
983             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
984             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
985             if (s.getDistance() < best) {
986                 best = s.getDistance();
987                 best_pt = s.getPoint() - n->pos;
988             }
989         }
990     }
992     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
993         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
994         sp_node_moveto(n, n->pos + best_pt);
995     }
997     // do not update repr here so that node dragging is acceptably fast
998     update_object(nodepath);
1001 /**
1002 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1003 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1004 near x = 0.
1005  */
1006 double
1007 sculpt_profile (double x, double alpha, guint profile)
1009     if (x >= 1)
1010         return 0;
1011     if (x <= 0)
1012         return 1;
1014     switch (profile) {
1015         case SCULPT_PROFILE_LINEAR:
1016         return 1 - x;
1017         case SCULPT_PROFILE_BELL:
1018         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1019         case SCULPT_PROFILE_ELLIPTIC:
1020         return sqrt(1 - x*x);
1021     }
1023     return 1;
1026 double
1027 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1029     // extremely primitive for now, don't have time to look for the real one
1030     double lower = NR::L2(b - a);
1031     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1032     return (lower + upper)/2;
1035 void
1036 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1038     n->pos = n->origin + delta;
1039     n->n.pos = n->n.origin + delta_n;
1040     n->p.pos = n->p.origin + delta_p;
1041     sp_node_adjust_handles(n);
1042     sp_node_update_handles(n, false);
1045 /**
1046  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1047  * on how far they are from the dragged node n.
1048  */
1049 static void 
1050 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1052     g_assert (n);
1053     g_assert (nodepath);
1054     g_assert (n->subpath->nodepath == nodepath);
1056     double pressure = n->knot->pressure;
1057     if (pressure == 0)
1058         pressure = 0.5; // default
1059     pressure = CLAMP (pressure, 0.2, 0.8);
1061     // map pressure to alpha = 1/5 ... 5
1062     double alpha = 1 - 2 * fabs(pressure - 0.5);
1063     if (pressure > 0.5)
1064         alpha = 1/alpha;
1066     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1068     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1069         // Only one subpath has selected nodes:
1070         // use linear mode, where the distance from n to node being dragged is calculated along the path
1072         double n_sel_range = 0, p_sel_range = 0;
1073         guint n_nodes = 0, p_nodes = 0;
1074         guint n_sel_nodes = 0, p_sel_nodes = 0;
1076         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1077         {
1078             double n_range = 0, p_range = 0;
1079             bool n_going = true, p_going = true;
1080             Inkscape::NodePath::Node *n_node = n;
1081             Inkscape::NodePath::Node *p_node = n;
1082             do {
1083                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1084                 if (n_node && n_going)
1085                     n_node = n_node->n.other;
1086                 if (n_node == NULL) {
1087                     n_going = false;
1088                 } else {
1089                     n_nodes ++;
1090                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1091                     if (n_node->selected) {
1092                         n_sel_nodes ++;
1093                         n_sel_range = n_range;
1094                     }
1095                     if (n_node == p_node) {
1096                         n_going = false;
1097                         p_going = false;
1098                     }
1099                 }
1100                 if (p_node && p_going)
1101                     p_node = p_node->p.other;
1102                 if (p_node == NULL) {
1103                     p_going = false;
1104                 } else {
1105                     p_nodes ++;
1106                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1107                     if (p_node->selected) {
1108                         p_sel_nodes ++;
1109                         p_sel_range = p_range;
1110                     }
1111                     if (p_node == n_node) {
1112                         n_going = false;
1113                         p_going = false;
1114                     }
1115                 }
1116             } while (n_going || p_going);
1117         }
1119         // Second pass: actually move nodes in this subpath
1120         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1121         {
1122             double n_range = 0, p_range = 0;
1123             bool n_going = true, p_going = true;
1124             Inkscape::NodePath::Node *n_node = n;
1125             Inkscape::NodePath::Node *p_node = n;
1126             do {
1127                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1128                 if (n_node && n_going)
1129                     n_node = n_node->n.other;
1130                 if (n_node == NULL) {
1131                     n_going = false;
1132                 } else {
1133                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1134                     if (n_node->selected) {
1135                         sp_nodepath_move_node_and_handles (n_node, 
1136                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1137                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1138                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1139                     }
1140                     if (n_node == p_node) {
1141                         n_going = false;
1142                         p_going = false;
1143                     }
1144                 }
1145                 if (p_node && p_going)
1146                     p_node = p_node->p.other;
1147                 if (p_node == NULL) {
1148                     p_going = false;
1149                 } else {
1150                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1151                     if (p_node->selected) {
1152                         sp_nodepath_move_node_and_handles (p_node, 
1153                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1154                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1155                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1156                     }
1157                     if (p_node == n_node) {
1158                         n_going = false;
1159                         p_going = false;
1160                     }
1161                 }
1162             } while (n_going || p_going);
1163         }
1165     } else {
1166         // Multiple subpaths have selected nodes:
1167         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1168         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1169         // fix the pear-like shape when sculpting e.g. a ring
1171         // First pass: calculate range
1172         gdouble direct_range = 0;
1173         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1174             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1175             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1176                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1177                 if (node->selected) {
1178                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1179                 }
1180             }
1181         }
1183         // Second pass: actually move nodes
1184         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1185             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1186             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1187                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1188                 if (node->selected) {
1189                     if (direct_range > 1e-6) {
1190                         sp_nodepath_move_node_and_handles (node,
1191                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1192                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1193                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1194                     } else {
1195                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1196                     }
1198                 }
1199             }
1200         }
1201     }
1203     // do not update repr here so that node dragging is acceptably fast
1204     update_object(nodepath);
1208 /**
1209  * Move node selection to point, adjust its and neighbouring handles,
1210  * handle possible snapping, and commit the change with possible undo.
1211  */
1212 void
1213 sp_node_selected_move(gdouble dx, gdouble dy)
1215     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1216     if (!nodepath) return;
1218     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1220     if (dx == 0) {
1221         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1222     } else if (dy == 0) {
1223         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1224     } else {
1225         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1226     }
1229 /**
1230  * Move node selection off screen and commit the change.
1231  */
1232 void
1233 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1235     // borrowed from sp_selection_move_screen in selection-chemistry.c
1236     // we find out the current zoom factor and divide deltas by it
1237     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1239     gdouble zoom = desktop->current_zoom();
1240     gdouble zdx = dx / zoom;
1241     gdouble zdy = dy / zoom;
1243     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1244     if (!nodepath) return;
1246     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1248     if (dx == 0) {
1249         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1250     } else if (dy == 0) {
1251         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1252     } else {
1253         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1254     }
1257 /** If they don't yet exist, creates knot and line for the given side of the node */
1258 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1260     if (!side->knot) {
1261         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"));
1263         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1264         side->knot->setSize (7);
1265         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1266         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1267         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1268         sp_knot_update_ctrl(side->knot);
1270         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1271         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1272         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1273         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1274         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1275         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1276     }
1278     if (!side->line) {
1279         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1280                                         SP_TYPE_CTRLLINE, NULL);
1281     }
1284 /**
1285  * Ensure the given handle of the node is visible/invisible, update its screen position
1286  */
1287 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1289     g_assert(node != NULL);
1291    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1292     NRPathcode code = sp_node_path_code_from_side(node, side);
1294     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1296     if (show_handle) {
1297         if (!side->knot) { // No handle knot at all
1298             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1299             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1300             side->knot->pos = side->pos;
1301             if (side->knot->item) 
1302                 SP_CTRL(side->knot->item)->moveto(side->pos);
1303             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1304             sp_knot_show(side->knot);
1305         } else {
1306             if (side->knot->pos != side->pos) { // only if it's really moved
1307                 if (fire_move_signals) {
1308                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1309                 } else {
1310                     sp_knot_moveto(side->knot, &side->pos);
1311                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1312                 }
1313             }
1314             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1315                 sp_knot_show(side->knot);
1316             }
1317         }
1318         sp_canvas_item_show(side->line);
1319     } else {
1320         if (side->knot) {
1321             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1322                 sp_knot_hide(side->knot);
1323             }
1324         }
1325         if (side->line) {
1326             sp_canvas_item_hide(side->line);
1327         }
1328     }
1331 /**
1332  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1333  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1334  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1335  * updated; otherwise, just move the knots silently (used in batch moves).
1336  */
1337 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1339     g_assert(node != NULL);
1341     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1342         sp_knot_show(node->knot);
1343     }
1345     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1346         if (fire_move_signals)
1347             sp_knot_set_position(node->knot, &node->pos, 0);
1348         else 
1349             sp_knot_moveto(node->knot, &node->pos);
1350     }
1352     gboolean show_handles = node->selected;
1353     if (node->p.other != NULL) {
1354         if (node->p.other->selected) show_handles = TRUE;
1355     }
1356     if (node->n.other != NULL) {
1357         if (node->n.other->selected) show_handles = TRUE;
1358     }
1360     if (node->subpath->nodepath->show_handles == false)
1361         show_handles = FALSE;
1363     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1364     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1367 /**
1368  * Call sp_node_update_handles() for all nodes on subpath.
1369  */
1370 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1372     g_assert(subpath != NULL);
1374     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1375         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1376     }
1379 /**
1380  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1381  */
1382 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1384     g_assert(nodepath != NULL);
1386     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1387         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1388     }
1391 void
1392 sp_nodepath_show_handles(bool show)
1394     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1395     if (nodepath == NULL) return;
1397     nodepath->show_handles = show;
1398     sp_nodepath_update_handles(nodepath);
1401 /**
1402  * Adds all selected nodes in nodepath to list.
1403  */
1404 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1406     StlConv<Node *>::list(l, selected);
1407 /// \todo this adds a copying, rework when the selection becomes a stl list
1410 /**
1411  * Align selected nodes on the specified axis.
1412  */
1413 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1415     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1416         return;
1417     }
1419     if ( !nodepath->selected->next ) { // only one node selected
1420         return;
1421     }
1422    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1423     NR::Point dest(pNode->pos);
1424     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1425         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1426         if (pNode) {
1427             dest[axis] = pNode->pos[axis];
1428             sp_node_moveto(pNode, dest);
1429         }
1430     }
1432     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1435 /// Helper struct.
1436 struct NodeSort
1438    Inkscape::NodePath::Node *_node;
1439     NR::Coord _coord;
1440     /// \todo use vectorof pointers instead of calling copy ctor
1441     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1442         _node(node), _coord(node->pos[axis])
1443     {}
1445 };
1447 static bool operator<(NodeSort const &a, NodeSort const &b)
1449     return (a._coord < b._coord);
1452 /**
1453  * Distribute selected nodes on the specified axis.
1454  */
1455 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1457     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1458         return;
1459     }
1461     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1462         return;
1463     }
1465    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1466     std::vector<NodeSort> sorted;
1467     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1468         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1469         if (pNode) {
1470             NodeSort n(pNode, axis);
1471             sorted.push_back(n);
1472             //dest[axis] = pNode->pos[axis];
1473             //sp_node_moveto(pNode, dest);
1474         }
1475     }
1476     std::sort(sorted.begin(), sorted.end());
1477     unsigned int len = sorted.size();
1478     //overall bboxes span
1479     float dist = (sorted.back()._coord -
1480                   sorted.front()._coord);
1481     //new distance between each bbox
1482     float step = (dist) / (len - 1);
1483     float pos = sorted.front()._coord;
1484     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1485           it < sorted.end();
1486           it ++ )
1487     {
1488         NR::Point dest((*it)._node->pos);
1489         dest[axis] = pos;
1490         sp_node_moveto((*it)._node, dest);
1491         pos += step;
1492     }
1494     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1498 /**
1499  * Call sp_nodepath_line_add_node() for all selected segments.
1500  */
1501 void
1502 sp_node_selected_add_node(void)
1504     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1505     if (!nodepath) {
1506         return;
1507     }
1509     GList *nl = NULL;
1511     int n_added = 0;
1513     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1514        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1515         g_assert(t->selected);
1516         if (t->p.other && t->p.other->selected) {
1517             nl = g_list_prepend(nl, t);
1518         }
1519     }
1521     while (nl) {
1522        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1523        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1524        sp_nodepath_node_select(n, TRUE, FALSE);
1525        n_added ++;
1526        nl = g_list_remove(nl, t);
1527     }
1529     /** \todo fixme: adjust ? */
1530     sp_nodepath_update_handles(nodepath);
1532     if (n_added > 1) {
1533         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1534     } else if (n_added > 0) {
1535         sp_nodepath_update_repr(nodepath, _("Add node"));
1536     }
1538     sp_nodepath_update_statusbar(nodepath);
1541 /**
1542  * Select segment nearest to point
1543  */
1544 void
1545 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1547     if (!nodepath) {
1548         return;
1549     }
1551     sp_nodepath_ensure_livarot_path(nodepath);
1552     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1553     if (!maybe_position) {
1554         return;
1555     }
1556     Path::cut_position position = *maybe_position;
1558     //find segment to segment
1559     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1561     //fixme: this can return NULL, so check before proceeding.
1562     g_return_if_fail(e != NULL);
1563     
1564     gboolean force = FALSE;
1565     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1566         force = TRUE;
1567     }
1568     sp_nodepath_node_select(e, (gboolean) toggle, force);
1569     if (e->p.other)
1570         sp_nodepath_node_select(e->p.other, TRUE, force);
1572     sp_nodepath_update_handles(nodepath);
1574     sp_nodepath_update_statusbar(nodepath);
1577 /**
1578  * Add a node nearest to point
1579  */
1580 void
1581 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1583     if (!nodepath) {
1584         return;
1585     }
1587     sp_nodepath_ensure_livarot_path(nodepath);
1588     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1589     if (!maybe_position) {
1590         return;
1591     }
1592     Path::cut_position position = *maybe_position;
1594     //find segment to split
1595     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1597     //don't know why but t seems to flip for lines
1598     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1599         position.t = 1.0 - position.t;
1600     }
1601     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1602     sp_nodepath_node_select(n, FALSE, TRUE);
1604     /* fixme: adjust ? */
1605     sp_nodepath_update_handles(nodepath);
1607     sp_nodepath_update_repr(nodepath, _("Add node"));
1609     sp_nodepath_update_statusbar(nodepath);
1612 /*
1613  * Adjusts a segment so that t moves by a certain delta for dragging
1614  * converts lines to curves
1615  *
1616  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1617  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1618  */
1619 void
1620 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1622     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1624     //fixme: e and e->p can be NULL, so check for those before proceeding
1625     g_return_if_fail(e != NULL);
1626     g_return_if_fail(&e->p != NULL);
1627     
1628     /* feel good is an arbitrary parameter that distributes the delta between handles
1629      * if t of the drag point is less than 1/6 distance form the endpoint only
1630      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1631      */
1632     double feel_good;
1633     if (t <= 1.0 / 6.0)
1634         feel_good = 0;
1635     else if (t <= 0.5)
1636         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1637     else if (t <= 5.0 / 6.0)
1638         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1639     else
1640         feel_good = 1;
1642     //if we're dragging a line convert it to a curve
1643     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1644         sp_nodepath_set_line_type(e, NR_CURVETO);
1645     }
1647     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1648     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1649     e->p.other->n.pos += offsetcoord0;
1650     e->p.pos += offsetcoord1;
1652     // adjust handles of adjacent nodes where necessary
1653     sp_node_adjust_handle(e,1);
1654     sp_node_adjust_handle(e->p.other,-1);
1656     sp_nodepath_update_handles(e->subpath->nodepath);
1658     update_object(e->subpath->nodepath);
1660     sp_nodepath_update_statusbar(e->subpath->nodepath);
1664 /**
1665  * Call sp_nodepath_break() for all selected segments.
1666  */
1667 void sp_node_selected_break()
1669     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1670     if (!nodepath) return;
1672     GList *temp = NULL;
1673     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1674        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1675        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1676         if (nn == NULL) continue; // no break, no new node
1677         temp = g_list_prepend(temp, nn);
1678     }
1680     if (temp) {
1681         sp_nodepath_deselect(nodepath);
1682     }
1683     for (GList *l = temp; l != NULL; l = l->next) {
1684         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1685     }
1687     sp_nodepath_update_handles(nodepath);
1689     sp_nodepath_update_repr(nodepath, _("Break path"));
1692 /**
1693  * Duplicate the selected node(s).
1694  */
1695 void sp_node_selected_duplicate()
1697     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1698     if (!nodepath) {
1699         return;
1700     }
1702     GList *temp = NULL;
1703     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1704        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1705        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1706         if (nn == NULL) continue; // could not duplicate
1707         temp = g_list_prepend(temp, nn);
1708     }
1710     if (temp) {
1711         sp_nodepath_deselect(nodepath);
1712     }
1713     for (GList *l = temp; l != NULL; l = l->next) {
1714         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1715     }
1717     sp_nodepath_update_handles(nodepath);
1719     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1722 /**
1723  *  Join two nodes by merging them into one.
1724  */
1725 void sp_node_selected_join()
1727     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1728     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1730     if (g_list_length(nodepath->selected) != 2) {
1731         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1732         return;
1733     }
1735    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1736    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1738     g_assert(a != b);
1739     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1740         // someone tried to join an orphan node (i.e. a single-node subpath).
1741         // this is not worth an error message, just fail silently.
1742         return;
1743     }
1745     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1746         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1747         return;
1748     }
1750     /* a and b are endpoints */
1752     NR::Point c;
1753     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1754         c = a->pos;
1755     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1756         c = b->pos;
1757     } else {
1758         c = (a->pos + b->pos) / 2;
1759     }
1761     if (a->subpath == b->subpath) {
1762        Inkscape::NodePath::SubPath *sp = a->subpath;
1763         sp_nodepath_subpath_close(sp);
1764         sp_node_moveto (sp->first, c);
1766         sp_nodepath_update_handles(sp->nodepath);
1767         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1768         return;
1769     }
1771     /* a and b are separate subpaths */
1772    Inkscape::NodePath::SubPath *sa = a->subpath;
1773    Inkscape::NodePath::SubPath *sb = b->subpath;
1774     NR::Point p;
1775    Inkscape::NodePath::Node *n;
1776     NRPathcode code;
1777     if (a == sa->first) {
1778         p = sa->first->n.pos;
1779         code = (NRPathcode)sa->first->n.other->code;
1780        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1781         n = sa->last;
1782         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1783         n = n->p.other;
1784         while (n) {
1785             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1786             n = n->p.other;
1787             if (n == sa->first) n = NULL;
1788         }
1789         sp_nodepath_subpath_destroy(sa);
1790         sa = t;
1791     } else if (a == sa->last) {
1792         p = sa->last->p.pos;
1793         code = (NRPathcode)sa->last->code;
1794         sp_nodepath_node_destroy(sa->last);
1795     } else {
1796         code = NR_END;
1797         g_assert_not_reached();
1798     }
1800     if (b == sb->first) {
1801         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1802         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1803             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1804         }
1805     } else if (b == sb->last) {
1806         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1807         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1808             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1809         }
1810     } else {
1811         g_assert_not_reached();
1812     }
1813     /* and now destroy sb */
1815     sp_nodepath_subpath_destroy(sb);
1817     sp_nodepath_update_handles(sa->nodepath);
1819     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1821     sp_nodepath_update_statusbar(nodepath);
1824 /**
1825  *  Join two nodes by adding a segment between them.
1826  */
1827 void sp_node_selected_join_segment()
1829     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1830     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1832     if (g_list_length(nodepath->selected) != 2) {
1833         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1834         return;
1835     }
1837    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1838    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1840     g_assert(a != b);
1841     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1842         // someone tried to join an orphan node (i.e. a single-node subpath).
1843         // this is not worth an error message, just fail silently.
1844         return;
1845     }
1847     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1848         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1849         return;
1850     }
1852     if (a->subpath == b->subpath) {
1853        Inkscape::NodePath::SubPath *sp = a->subpath;
1855         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1856         sp->closed = TRUE;
1858         sp->first->p.other = sp->last;
1859         sp->last->n.other  = sp->first;
1861         sp_node_handle_mirror_p_to_n(sp->last);
1862         sp_node_handle_mirror_n_to_p(sp->first);
1864         sp->first->code = sp->last->code;
1865         sp->first       = sp->last;
1867         sp_nodepath_update_handles(sp->nodepath);
1869         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1871         return;
1872     }
1874     /* a and b are separate subpaths */
1875    Inkscape::NodePath::SubPath *sa = a->subpath;
1876    Inkscape::NodePath::SubPath *sb = b->subpath;
1878    Inkscape::NodePath::Node *n;
1879     NR::Point p;
1880     NRPathcode code;
1881     if (a == sa->first) {
1882         code = (NRPathcode) sa->first->n.other->code;
1883        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1884         n = sa->last;
1885         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1886         for (n = n->p.other; n != NULL; n = n->p.other) {
1887             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1888         }
1889         sp_nodepath_subpath_destroy(sa);
1890         sa = t;
1891     } else if (a == sa->last) {
1892         code = (NRPathcode)sa->last->code;
1893     } else {
1894         code = NR_END;
1895         g_assert_not_reached();
1896     }
1898     if (b == sb->first) {
1899         n = sb->first;
1900         sp_node_handle_mirror_p_to_n(sa->last);
1901         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1902         sp_node_handle_mirror_n_to_p(sa->last);
1903         for (n = n->n.other; n != NULL; n = n->n.other) {
1904             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1905         }
1906     } else if (b == sb->last) {
1907         n = sb->last;
1908         sp_node_handle_mirror_p_to_n(sa->last);
1909         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1910         sp_node_handle_mirror_n_to_p(sa->last);
1911         for (n = n->p.other; n != NULL; n = n->p.other) {
1912             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1913         }
1914     } else {
1915         g_assert_not_reached();
1916     }
1917     /* and now destroy sb */
1919     sp_nodepath_subpath_destroy(sb);
1921     sp_nodepath_update_handles(sa->nodepath);
1923     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1926 /**
1927  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1928  */
1929 void sp_node_delete_preserve(GList *nodes_to_delete)
1931     GSList *nodepaths = NULL;
1932     
1933     while (nodes_to_delete) {
1934         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1935         Inkscape::NodePath::SubPath *sp = node->subpath;
1936         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1937         Inkscape::NodePath::Node *sample_cursor = NULL;
1938         Inkscape::NodePath::Node *sample_end = NULL;
1939         Inkscape::NodePath::Node *delete_cursor = node;
1940         bool just_delete = false;
1941         
1942         //find the start of this contiguous selection
1943         //move left to the first node that is not selected
1944         //or the start of the non-closed path
1945         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1946             delete_cursor = curr;
1947         }
1949         //just delete at the beginning of an open path
1950         if (!delete_cursor->p.other) {
1951             sample_cursor = delete_cursor;
1952             just_delete = true;
1953         } else {
1954             sample_cursor = delete_cursor->p.other;
1955         }
1956         
1957         //calculate points for each segment
1958         int rate = 5;
1959         float period = 1.0 / rate;
1960         std::vector<NR::Point> data;
1961         if (!just_delete) {
1962             data.push_back(sample_cursor->pos);
1963             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1964                 //just delete at the end of an open path
1965                 if (!sp->closed && curr == sp->last) {
1966                     just_delete = true;
1967                     break;
1968                 }
1969                 
1970                 //sample points on the contiguous selected segment
1971                 NR::Point *bez;
1972                 bez = new NR::Point [4];
1973                 bez[0] = curr->pos;
1974                 bez[1] = curr->n.pos;
1975                 bez[2] = curr->n.other->p.pos;
1976                 bez[3] = curr->n.other->pos;
1977                 for (int i=1; i<rate; i++) {
1978                     gdouble t = i * period;
1979                     NR::Point p = bezier_pt(3, bez, t);
1980                     data.push_back(p);
1981                 }
1982                 data.push_back(curr->n.other->pos);
1984                 sample_end = curr->n.other;
1985                 //break if we've come full circle or hit the end of the selection
1986                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1987                     break;
1988                 }
1989             }
1990         }
1992         if (!just_delete) {
1993             //calculate the best fitting single segment and adjust the endpoints
1994             NR::Point *adata;
1995             adata = new NR::Point [data.size()];
1996             copy(data.begin(), data.end(), adata);
1997             
1998             NR::Point *bez;
1999             bez = new NR::Point [4];
2000             //would decreasing error create a better fitting approximation?
2001             gdouble error = 1.0;
2002             gint ret;
2003             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2005             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2006             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2007             //the resulting nodes behave as expected.
2008             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2009             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2010             
2011             //adjust endpoints
2012             sample_cursor->n.pos = bez[1];
2013             sample_end->p.pos = bez[2];
2014         }
2015        
2016         //destroy this contiguous selection
2017         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2018             Inkscape::NodePath::Node *temp = delete_cursor;
2019             if (delete_cursor->n.other == delete_cursor) {
2020                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2021                 delete_cursor = NULL; 
2022             } else {
2023                 delete_cursor = delete_cursor->n.other;
2024             }
2025             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2026             sp_nodepath_node_destroy(temp);
2027         }
2029         sp_nodepath_update_handles(nodepath);
2031         if (!g_slist_find(nodepaths, nodepath))
2032             nodepaths = g_slist_prepend (nodepaths, nodepath);
2033     }
2035     for (GSList *i = nodepaths; i; i = i->next) {
2036         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2037         // different nodepaths will give us one undo event per nodepath
2038         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2040         // if the entire nodepath is removed, delete the selected object.
2041         if (nodepath->subpaths == NULL ||
2042             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2043             //at least 2
2044             sp_nodepath_get_node_count(nodepath) < 2) {
2045             SPDocument *document = sp_desktop_document (nodepath->desktop);
2046             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2047             //delete this nodepath's object, not the entire selection! (though at this time, this
2048             //does not matter)
2049             sp_selection_delete();
2050             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2051                               _("Delete nodes"));
2052         } else {
2053             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2054             sp_nodepath_update_statusbar(nodepath);
2055         }
2056     }
2058     g_slist_free (nodepaths);
2061 /**
2062  * Delete one or more selected nodes.
2063  */
2064 void sp_node_selected_delete()
2066     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2067     if (!nodepath) return;
2068     if (!nodepath->selected) return;
2070     /** \todo fixme: do it the right way */
2071     while (nodepath->selected) {
2072        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2073         sp_nodepath_node_destroy(node);
2074     }
2077     //clean up the nodepath (such as for trivial subpaths)
2078     sp_nodepath_cleanup(nodepath);
2080     sp_nodepath_update_handles(nodepath);
2082     // if the entire nodepath is removed, delete the selected object.
2083     if (nodepath->subpaths == NULL ||
2084         sp_nodepath_get_node_count(nodepath) < 2) {
2085         SPDocument *document = sp_desktop_document (nodepath->desktop);
2086         sp_selection_delete();
2087         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2088                           _("Delete nodes"));
2089         return;
2090     }
2092     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2094     sp_nodepath_update_statusbar(nodepath);
2097 /**
2098  * Delete one or more segments between two selected nodes.
2099  * This is the code for 'split'.
2100  */
2101 void
2102 sp_node_selected_delete_segment(void)
2104    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2105    Inkscape::NodePath::Node *curr, *next;     //Iterators
2107     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2108     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2110     if (g_list_length(nodepath->selected) != 2) {
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     //Selected nodes, not inclusive
2117    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2118    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2120     if ( ( a==b)                       ||  //same node
2121          (a->subpath  != b->subpath )  ||  //not the same path
2122          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2123          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2124     {
2125         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2126                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2127         return;
2128     }
2130     //###########################################
2131     //# BEGIN EDITS
2132     //###########################################
2133     //##################################
2134     //# CLOSED PATH
2135     //##################################
2136     if (a->subpath->closed) {
2139         gboolean reversed = FALSE;
2141         //Since we can go in a circle, we need to find the shorter distance.
2142         //  a->b or b->a
2143         start = end = NULL;
2144         int distance    = 0;
2145         int minDistance = 0;
2146         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2147             if (curr==b) {
2148                 //printf("a to b:%d\n", distance);
2149                 start = a;//go from a to b
2150                 end   = b;
2151                 minDistance = distance;
2152                 //printf("A to B :\n");
2153                 break;
2154             }
2155             distance++;
2156         }
2158         //try again, the other direction
2159         distance = 0;
2160         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2161             if (curr==a) {
2162                 //printf("b to a:%d\n", distance);
2163                 if (distance < minDistance) {
2164                     start    = b;  //we go from b to a
2165                     end      = a;
2166                     reversed = TRUE;
2167                     //printf("B to A\n");
2168                 }
2169                 break;
2170             }
2171             distance++;
2172         }
2175         //Copy everything from 'end' to 'start' to a new subpath
2176        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2177         for (curr=end ; curr ; curr=curr->n.other) {
2178             NRPathcode code = (NRPathcode) curr->code;
2179             if (curr == end)
2180                 code = NR_MOVETO;
2181             sp_nodepath_node_new(t, NULL,
2182                                  (Inkscape::NodePath::NodeType)curr->type, code,
2183                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2184             if (curr == start)
2185                 break;
2186         }
2187         sp_nodepath_subpath_destroy(a->subpath);
2190     }
2194     //##################################
2195     //# OPEN PATH
2196     //##################################
2197     else {
2199         //We need to get the direction of the list between A and B
2200         //Can we walk from a to b?
2201         start = end = NULL;
2202         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2203             if (curr==b) {
2204                 start = a;  //did it!  we go from a to b
2205                 end   = b;
2206                 //printf("A to B\n");
2207                 break;
2208             }
2209         }
2210         if (!start) {//didn't work?  let's try the other direction
2211             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2212                 if (curr==a) {
2213                     start = b;  //did it!  we go from b to a
2214                     end   = a;
2215                     //printf("B to A\n");
2216                     break;
2217                 }
2218             }
2219         }
2220         if (!start) {
2221             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2222                                                      _("Cannot find path between nodes."));
2223             return;
2224         }
2228         //Copy everything after 'end' to a new subpath
2229        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2230         for (curr=end ; curr ; curr=curr->n.other) {
2231             NRPathcode code = (NRPathcode) curr->code;
2232             if (curr == end)
2233                 code = NR_MOVETO;
2234             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2235                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2236         }
2238         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2239         for (curr = start->n.other ; curr  ; curr=next) {
2240             next = curr->n.other;
2241             sp_nodepath_node_destroy(curr);
2242         }
2244     }
2245     //###########################################
2246     //# END EDITS
2247     //###########################################
2249     //clean up the nodepath (such as for trivial subpaths)
2250     sp_nodepath_cleanup(nodepath);
2252     sp_nodepath_update_handles(nodepath);
2254     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2256     sp_nodepath_update_statusbar(nodepath);
2259 /**
2260  * Call sp_nodepath_set_line() for all selected segments.
2261  */
2262 void
2263 sp_node_selected_set_line_type(NRPathcode code)
2265     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2266     if (nodepath == NULL) return;
2268     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2269        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2270         g_assert(n->selected);
2271         if (n->p.other && n->p.other->selected) {
2272             sp_nodepath_set_line_type(n, code);
2273         }
2274     }
2276     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2279 /**
2280  * Call sp_nodepath_convert_node_type() for all selected nodes.
2281  */
2282 void
2283 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2285     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2286     if (nodepath == NULL) return;
2288     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2289         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2290     }
2292     sp_nodepath_update_repr(nodepath, _("Change node type"));
2295 /**
2296  * Change select status of node, update its own and neighbour handles.
2297  */
2298 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2300     node->selected = selected;
2302     if (selected) {
2303         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2304         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2305         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2306         sp_knot_update_ctrl(node->knot);
2307     } else {
2308         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2309         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2310         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2311         sp_knot_update_ctrl(node->knot);
2312     }
2314     sp_node_update_handles(node);
2315     if (node->n.other) sp_node_update_handles(node->n.other);
2316     if (node->p.other) sp_node_update_handles(node->p.other);
2319 /**
2320 \brief Select a node
2321 \param node     The node to select
2322 \param incremental   If true, add to selection, otherwise deselect others
2323 \param override   If true, always select this node, otherwise toggle selected status
2324 */
2325 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2327     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2329     if (incremental) {
2330         if (override) {
2331             if (!g_list_find(nodepath->selected, node)) {
2332                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2333             }
2334             sp_node_set_selected(node, TRUE);
2335         } else { // toggle
2336             if (node->selected) {
2337                 g_assert(g_list_find(nodepath->selected, node));
2338                 nodepath->selected = g_list_remove(nodepath->selected, node);
2339             } else {
2340                 g_assert(!g_list_find(nodepath->selected, node));
2341                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2342             }
2343             sp_node_set_selected(node, !node->selected);
2344         }
2345     } else {
2346         sp_nodepath_deselect(nodepath);
2347         nodepath->selected = g_list_prepend(nodepath->selected, node);
2348         sp_node_set_selected(node, TRUE);
2349     }
2351     sp_nodepath_update_statusbar(nodepath);
2355 /**
2356 \brief Deselect all nodes in the nodepath
2357 */
2358 void
2359 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2361     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2363     while (nodepath->selected) {
2364         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2365         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2366     }
2367     sp_nodepath_update_statusbar(nodepath);
2370 /**
2371 \brief Select or invert selection of all nodes in the nodepath
2372 */
2373 void
2374 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2376     if (!nodepath) return;
2378     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2379        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2380         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2381            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2382            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2383         }
2384     }
2387 /**
2388  * If nothing selected, does the same as sp_nodepath_select_all();
2389  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2390  * (i.e., similar to "select all in layer", with the "selected" subpaths
2391  * being treated as "layers" in the path).
2392  */
2393 void
2394 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2396     if (!nodepath) return;
2398     if (g_list_length (nodepath->selected) == 0) {
2399         sp_nodepath_select_all (nodepath, invert);
2400         return;
2401     }
2403     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2404     GSList *subpaths = NULL;
2406     for (GList *l = copy; l != NULL; l = l->next) {
2407         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2408         Inkscape::NodePath::SubPath *subpath = n->subpath;
2409         if (!g_slist_find (subpaths, subpath))
2410             subpaths = g_slist_prepend (subpaths, subpath);
2411     }
2413     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2414         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2415         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2416             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2417             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2418         }
2419     }
2421     g_slist_free (subpaths);
2422     g_list_free (copy);
2425 /**
2426  * \brief Select the node after the last selected; if none is selected,
2427  * select the first within path.
2428  */
2429 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2431     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2433    Inkscape::NodePath::Node *last = NULL;
2434     if (nodepath->selected) {
2435         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2436            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2437             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2438             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2439                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2440                 if (node->selected) {
2441                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2442                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2443                             if (spl->next) { // there's a next subpath
2444                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2445                                 last = subpath_next->first;
2446                             } else if (spl->prev) { // there's a previous subpath
2447                                 last = NULL; // to be set later to the first node of first subpath
2448                             } else {
2449                                 last = node->n.other;
2450                             }
2451                         } else {
2452                             last = node->n.other;
2453                         }
2454                     } else {
2455                         if (node->n.other) {
2456                             last = node->n.other;
2457                         } else {
2458                             if (spl->next) { // there's a next subpath
2459                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2460                                 last = subpath_next->first;
2461                             } else if (spl->prev) { // there's a previous subpath
2462                                 last = NULL; // to be set later to the first node of first subpath
2463                             } else {
2464                                 last = (Inkscape::NodePath::Node *) subpath->first;
2465                             }
2466                         }
2467                     }
2468                 }
2469             }
2470         }
2471         sp_nodepath_deselect(nodepath);
2472     }
2474     if (last) { // there's at least one more node after selected
2475         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2476     } else { // no more nodes, select the first one in first subpath
2477        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2478         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2479     }
2482 /**
2483  * \brief Select the node before the first selected; if none is selected,
2484  * select the last within path
2485  */
2486 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2488     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2490    Inkscape::NodePath::Node *last = NULL;
2491     if (nodepath->selected) {
2492         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2493            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2494             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2495                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2496                 if (node->selected) {
2497                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2498                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2499                             if (spl->prev) { // there's a prev subpath
2500                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2501                                 last = subpath_prev->last;
2502                             } else if (spl->next) { // there's a next subpath
2503                                 last = NULL; // to be set later to the last node of last subpath
2504                             } else {
2505                                 last = node->p.other;
2506                             }
2507                         } else {
2508                             last = node->p.other;
2509                         }
2510                     } else {
2511                         if (node->p.other) {
2512                             last = node->p.other;
2513                         } else {
2514                             if (spl->prev) { // there's a prev subpath
2515                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2516                                 last = subpath_prev->last;
2517                             } else if (spl->next) { // there's a next subpath
2518                                 last = NULL; // to be set later to the last node of last subpath
2519                             } else {
2520                                 last = (Inkscape::NodePath::Node *) subpath->last;
2521                             }
2522                         }
2523                     }
2524                 }
2525             }
2526         }
2527         sp_nodepath_deselect(nodepath);
2528     }
2530     if (last) { // there's at least one more node before selected
2531         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2532     } else { // no more nodes, select the last one in last subpath
2533         GList *spl = g_list_last(nodepath->subpaths);
2534        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2535         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2536     }
2539 /**
2540  * \brief Select all nodes that are within the rectangle.
2541  */
2542 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2544     if (!incremental) {
2545         sp_nodepath_deselect(nodepath);
2546     }
2548     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2549        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2550         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2551            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2553             if (b.contains(node->pos)) {
2554                 sp_nodepath_node_select(node, TRUE, TRUE);
2555             }
2556         }
2557     }
2561 void
2562 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2564     g_assert (n);
2565     g_assert (nodepath);
2566     g_assert (n->subpath->nodepath == nodepath);
2568     if (g_list_length (nodepath->selected) == 0) {
2569         if (grow > 0) {
2570             sp_nodepath_node_select(n, TRUE, TRUE);
2571         }
2572         return;
2573     }
2575     if (g_list_length (nodepath->selected) == 1) {
2576         if (grow < 0) {
2577             sp_nodepath_deselect (nodepath);
2578             return;
2579         }
2580     }
2582         double n_sel_range = 0, p_sel_range = 0;
2583             Inkscape::NodePath::Node *farthest_n_node = n;
2584             Inkscape::NodePath::Node *farthest_p_node = n;
2586         // Calculate ranges
2587         {
2588             double n_range = 0, p_range = 0;
2589             bool n_going = true, p_going = true;
2590             Inkscape::NodePath::Node *n_node = n;
2591             Inkscape::NodePath::Node *p_node = n;
2592             do {
2593                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2594                 if (n_node && n_going)
2595                     n_node = n_node->n.other;
2596                 if (n_node == NULL) {
2597                     n_going = false;
2598                 } else {
2599                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2600                     if (n_node->selected) {
2601                         n_sel_range = n_range;
2602                         farthest_n_node = n_node;
2603                     }
2604                     if (n_node == p_node) {
2605                         n_going = false;
2606                         p_going = false;
2607                     }
2608                 }
2609                 if (p_node && p_going)
2610                     p_node = p_node->p.other;
2611                 if (p_node == NULL) {
2612                     p_going = false;
2613                 } else {
2614                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2615                     if (p_node->selected) {
2616                         p_sel_range = p_range;
2617                         farthest_p_node = p_node;
2618                     }
2619                     if (p_node == n_node) {
2620                         n_going = false;
2621                         p_going = false;
2622                     }
2623                 }
2624             } while (n_going || p_going);
2625         }
2627     if (grow > 0) {
2628         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2629                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2630         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2631                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2632         }
2633     } else {
2634         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2635                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2636         } else if (farthest_p_node && farthest_p_node->selected) {
2637                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2638         }
2639     }
2642 void
2643 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2645     g_assert (n);
2646     g_assert (nodepath);
2647     g_assert (n->subpath->nodepath == nodepath);
2649     if (g_list_length (nodepath->selected) == 0) {
2650         if (grow > 0) {
2651             sp_nodepath_node_select(n, TRUE, TRUE);
2652         }
2653         return;
2654     }
2656     if (g_list_length (nodepath->selected) == 1) {
2657         if (grow < 0) {
2658             sp_nodepath_deselect (nodepath);
2659             return;
2660         }
2661     }
2663     Inkscape::NodePath::Node *farthest_selected = NULL;
2664     double farthest_dist = 0;
2666     Inkscape::NodePath::Node *closest_unselected = NULL;
2667     double closest_dist = NR_HUGE;
2669     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2670        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2671         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2672            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2673            if (node == n)
2674                continue;
2675            if (node->selected) {
2676                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2677                    farthest_dist = NR::L2(node->pos - n->pos);
2678                    farthest_selected = node;
2679                }
2680            } else {
2681                if (NR::L2(node->pos - n->pos) < closest_dist) {
2682                    closest_dist = NR::L2(node->pos - n->pos);
2683                    closest_unselected = node;
2684                }
2685            }
2686         }
2687     }
2689     if (grow > 0) {
2690         if (closest_unselected) {
2691             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2692         }
2693     } else {
2694         if (farthest_selected) {
2695             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2696         }
2697     }
2701 /**
2702 \brief  Saves all nodes' and handles' current positions in their origin members
2703 */
2704 void
2705 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2707     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2708        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2709         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2710            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2711            n->origin = n->pos;
2712            n->p.origin = n->p.pos;
2713            n->n.origin = n->n.pos;
2714         }
2715     }
2716
2718 /**
2719 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2720 */
2721 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2723     if (!nodepath->selected) {
2724         return NULL;
2725     }
2727     GList *r = NULL;
2728     guint i = 0;
2729     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2730        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2731         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2732            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2733             i++;
2734             if (node->selected) {
2735                 r = g_list_append(r, GINT_TO_POINTER(i));
2736             }
2737         }
2738     }
2739     return r;
2742 /**
2743 \brief  Restores selection by selecting nodes whose positions are in the list
2744 */
2745 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2747     sp_nodepath_deselect(nodepath);
2749     guint i = 0;
2750     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2751        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2752         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2753            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2754             i++;
2755             if (g_list_find(r, GINT_TO_POINTER(i))) {
2756                 sp_nodepath_node_select(node, TRUE, TRUE);
2757             }
2758         }
2759     }
2763 /**
2764 \brief Adjusts handle according to node type and line code.
2765 */
2766 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2768     double len, otherlen, linelen;
2770     g_assert(node);
2772    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2773    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2775     /** \todo fixme: */
2776     if (me->other == NULL) return;
2777     if (other->other == NULL) return;
2779     /* I have line */
2781     NRPathcode mecode, ocode;
2782     if (which_adjust == 1) {
2783         mecode = (NRPathcode)me->other->code;
2784         ocode = (NRPathcode)node->code;
2785     } else {
2786         mecode = (NRPathcode)node->code;
2787         ocode = (NRPathcode)other->other->code;
2788     }
2790     if (mecode == NR_LINETO) return;
2792     /* I am curve */
2794     if (other->other == NULL) return;
2796     /* Other has line */
2798     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2800     NR::Point delta;
2801     if (ocode == NR_LINETO) {
2802         /* other is lineto, we are either smooth or symm */
2803        Inkscape::NodePath::Node *othernode = other->other;
2804         len = NR::L2(me->pos - node->pos);
2805         delta = node->pos - othernode->pos;
2806         linelen = NR::L2(delta);
2807         if (linelen < 1e-18) 
2808             return;
2809         me->pos = node->pos + (len / linelen)*delta;
2810         return;
2811     }
2813     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2815         me->pos = 2 * node->pos - other->pos;
2816         return;
2817     }
2819     /* We are smooth */
2821     len = NR::L2(me->pos - node->pos);
2822     delta = other->pos - node->pos;
2823     otherlen = NR::L2(delta);
2824     if (otherlen < 1e-18) return;
2826     me->pos = node->pos - (len / otherlen) * delta;
2829 /**
2830  \brief Adjusts both handles according to node type and line code
2831  */
2832 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2834     g_assert(node);
2836     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2838     /* we are either smooth or symm */
2840     if (node->p.other == NULL) return;
2842     if (node->n.other == NULL) return;
2844     if (node->code == NR_LINETO) {
2845         if (node->n.other->code == NR_LINETO) return;
2846         sp_node_adjust_handle(node, 1);
2847         return;
2848     }
2850     if (node->n.other->code == NR_LINETO) {
2851         if (node->code == NR_LINETO) return;
2852         sp_node_adjust_handle(node, -1);
2853         return;
2854     }
2856     /* both are curves */
2857     NR::Point const delta( node->n.pos - node->p.pos );
2859     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2860         node->p.pos = node->pos - delta / 2;
2861         node->n.pos = node->pos + delta / 2;
2862         return;
2863     }
2865     /* We are smooth */
2866     double plen = NR::L2(node->p.pos - node->pos);
2867     if (plen < 1e-18) return;
2868     double nlen = NR::L2(node->n.pos - node->pos);
2869     if (nlen < 1e-18) return;
2870     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2871     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2874 /**
2875  * Node event callback.
2876  */
2877 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2879     gboolean ret = FALSE;
2880     switch (event->type) {
2881         case GDK_ENTER_NOTIFY:
2882             active_node = n;
2883             break;
2884         case GDK_LEAVE_NOTIFY:
2885             active_node = NULL;
2886             break;
2887         case GDK_SCROLL:
2888             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
2889                 switch (event->scroll.direction) {
2890                     case GDK_SCROLL_UP:
2891                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2892                         break;
2893                     case GDK_SCROLL_DOWN:
2894                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2895                         break;
2896                     default:
2897                         break;
2898                 }
2899                 ret = TRUE;
2900             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
2901                 switch (event->scroll.direction) {
2902                     case GDK_SCROLL_UP:
2903                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2904                         break;
2905                     case GDK_SCROLL_DOWN:
2906                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2907                         break;
2908                     default:
2909                         break;
2910                 }
2911                 ret = TRUE;
2912             }
2913             break;
2914         case GDK_KEY_PRESS:
2915             switch (get_group0_keyval (&event->key)) {
2916                 case GDK_space:
2917                     if (event->key.state & GDK_BUTTON1_MASK) {
2918                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2919                         stamp_repr(nodepath);
2920                         ret = TRUE;
2921                     }
2922                     break;
2923                 case GDK_Page_Up:
2924                     if (event->key.state & GDK_CONTROL_MASK) {
2925                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2926                     } else {
2927                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2928                     }
2929                     break;
2930                 case GDK_Page_Down:
2931                     if (event->key.state & GDK_CONTROL_MASK) {
2932                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2933                     } else {
2934                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2935                     }
2936                     break;
2937                 default:
2938                     break;
2939             }
2940             break;
2941         default:
2942             break;
2943     }
2945     return ret;
2948 /**
2949  * Handle keypress on node; directly called.
2950  */
2951 gboolean node_key(GdkEvent *event)
2953     Inkscape::NodePath::Path *np;
2955     // there is no way to verify nodes so set active_node to nil when deleting!!
2956     if (active_node == NULL) return FALSE;
2958     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2959         gint ret = FALSE;
2960         switch (get_group0_keyval (&event->key)) {
2961             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2962             case GDK_BackSpace:
2963                 np = active_node->subpath->nodepath;
2964                 sp_nodepath_node_destroy(active_node);
2965                 sp_nodepath_update_repr(np, _("Delete node"));
2966                 active_node = NULL;
2967                 ret = TRUE;
2968                 break;
2969             case GDK_c:
2970                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2971                 ret = TRUE;
2972                 break;
2973             case GDK_s:
2974                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2975                 ret = TRUE;
2976                 break;
2977             case GDK_y:
2978                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2979                 ret = TRUE;
2980                 break;
2981             case GDK_b:
2982                 sp_nodepath_node_break(active_node);
2983                 ret = TRUE;
2984                 break;
2985         }
2986         return ret;
2987     }
2988     return FALSE;
2991 /**
2992  * Mouseclick on node callback.
2993  */
2994 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2996    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2998     if (state & GDK_CONTROL_MASK) {
2999         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3001         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3002             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3003                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3004             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3005                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3006             } else {
3007                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3008             }
3009             sp_nodepath_update_repr(nodepath, _("Change node type"));
3010             sp_nodepath_update_statusbar(nodepath);
3012         } else { //ctrl+alt+click: delete node
3013             GList *node_to_delete = NULL;
3014             node_to_delete = g_list_append(node_to_delete, n);
3015             sp_node_delete_preserve(node_to_delete);
3016         }
3018     } else {
3019         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3020     }
3023 /**
3024  * Mouse grabbed node callback.
3025  */
3026 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3028    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3030     if (!n->selected) {
3031         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3032     }
3034     n->is_dragging = true;
3035     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3037     sp_nodepath_remember_origins (n->subpath->nodepath);
3040 /**
3041  * Mouse ungrabbed node callback.
3042  */
3043 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
3045    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3047    n->dragging_out = NULL;
3048    n->is_dragging = false;
3049    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3051    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3054 /**
3055  * The point on a line, given by its angle, closest to the given point.
3056  * \param p  A point.
3057  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3058  * \param closest  Pointer to the point struct where the result is stored.
3059  * \todo FIXME: use dot product perhaps?
3060  */
3061 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3063     if (a == HUGE_VAL) { // vertical
3064         *closest = NR::Point(0, (*p)[NR::Y]);
3065     } else {
3066         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3067         (*closest)[NR::Y] = a * (*closest)[NR::X];
3068     }
3071 /**
3072  * Distance from the point to a line given by its angle.
3073  * \param p  A point.
3074  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3075  */
3076 static double point_line_distance(NR::Point *p, double a)
3078     NR::Point c;
3079     point_line_closest(p, a, &c);
3080     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]));
3083 /**
3084  * Callback for node "request" signal.
3085  * \todo fixme: This goes to "moved" event? (lauris)
3086  */
3087 static gboolean
3088 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3090     double yn, xn, yp, xp;
3091     double an, ap, na, pa;
3092     double d_an, d_ap, d_na, d_pa;
3093     gboolean collinear = FALSE;
3094     NR::Point c;
3095     NR::Point pr;
3097    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3099    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3100    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3102        NR::Point mouse = (*p);
3104        if (!n->dragging_out) {
3105            // This is the first drag-out event; find out which handle to drag out
3106            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3107            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3109            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3110                return FALSE;
3112            Inkscape::NodePath::NodeSide *opposite;
3113            if (appr_p > appr_n) { // closer to p
3114                n->dragging_out = &n->p;
3115                opposite = &n->n;
3116                n->code = NR_CURVETO;
3117            } else if (appr_p < appr_n) { // closer to n
3118                n->dragging_out = &n->n;
3119                opposite = &n->p;
3120                n->n.other->code = NR_CURVETO;
3121            } else { // p and n nodes are the same
3122                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3123                    n->dragging_out = &n->p;
3124                    opposite = &n->n;
3125                    n->code = NR_CURVETO;
3126                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3127                    n->dragging_out = &n->n;
3128                    opposite = &n->p;
3129                    n->n.other->code = NR_CURVETO;
3130                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3131                    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);
3132                    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);
3133                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3134                        n->dragging_out = &n->n;
3135                        opposite = &n->p;
3136                        n->n.other->code = NR_CURVETO;
3137                    } else { // closer to other's n handle
3138                        n->dragging_out = &n->p;
3139                        opposite = &n->n;
3140                        n->code = NR_CURVETO;
3141                    }
3142                }
3143            }
3145            // if there's another handle, make sure the one we drag out starts parallel to it
3146            if (opposite->pos != n->pos) {
3147                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3148            }
3150            // knots might not be created yet!
3151            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3152            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3153        }
3155        // pass this on to the handle-moved callback
3156        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3157        sp_node_update_handles(n);
3158        return TRUE;
3159    }
3161     if (state & GDK_CONTROL_MASK) { // constrained motion
3163         // calculate relative distances of handles
3164         // n handle:
3165         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3166         xn = n->n.pos[NR::X] - n->pos[NR::X];
3167         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3168         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3169             if (n->n.other) { // if there is the next point
3170                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3171                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3172                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3173             }
3174         }
3175         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3176         if (yn < 0) { xn = -xn; yn = -yn; }
3178         // p handle:
3179         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3180         xp = n->p.pos[NR::X] - n->pos[NR::X];
3181         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3182         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3183             if (n->p.other) {
3184                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3185                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3186                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3187             }
3188         }
3189         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3190         if (yp < 0) { xp = -xp; yp = -yp; }
3192         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3193             // sliding on handles, only if at least one of the handles is non-vertical
3194             // (otherwise it's the same as ctrl+drag anyway)
3196             // calculate angles of the handles
3197             if (xn == 0) {
3198                 if (yn == 0) { // no handle, consider it the continuation of the other one
3199                     an = 0;
3200                     collinear = TRUE;
3201                 }
3202                 else an = 0; // vertical; set the angle to horizontal
3203             } else an = yn/xn;
3205             if (xp == 0) {
3206                 if (yp == 0) { // no handle, consider it the continuation of the other one
3207                     ap = an;
3208                 }
3209                 else ap = 0; // vertical; set the angle to horizontal
3210             } else  ap = yp/xp;
3212             if (collinear) an = ap;
3214             // angles of the perpendiculars; HUGE_VAL means vertical
3215             if (an == 0) na = HUGE_VAL; else na = -1/an;
3216             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3218             // mouse point relative to the node's original pos
3219             pr = (*p) - n->origin;
3221             // distances to the four lines (two handles and two perpendiculars)
3222             d_an = point_line_distance(&pr, an);
3223             d_na = point_line_distance(&pr, na);
3224             d_ap = point_line_distance(&pr, ap);
3225             d_pa = point_line_distance(&pr, pa);
3227             // find out which line is the closest, save its closest point in c
3228             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3229                 point_line_closest(&pr, an, &c);
3230             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3231                 point_line_closest(&pr, ap, &c);
3232             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3233                 point_line_closest(&pr, na, &c);
3234             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3235                 point_line_closest(&pr, pa, &c);
3236             }
3238             // move the node to the closest point
3239             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3240                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3241                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3243         } else {  // constraining to hor/vert
3245             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3246                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3247             } else { // snap to vert
3248                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3249             }
3250         }
3251     } else { // move freely
3252         if (n->is_dragging) {
3253             if (state & GDK_MOD1_MASK) { // sculpt
3254                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3255             } else {
3256                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3257                                             (*p)[NR::X] - n->pos[NR::X],
3258                                             (*p)[NR::Y] - n->pos[NR::Y],
3259                                             (state & GDK_SHIFT_MASK) == 0);
3260             }
3261         }
3262     }
3264     n->subpath->nodepath->desktop->scroll_to_point(p);
3266     return TRUE;
3269 /**
3270  * Node handle clicked callback.
3271  */
3272 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3274    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3276     if (state & GDK_CONTROL_MASK) { // "delete" handle
3277         if (n->p.knot == knot) {
3278             n->p.pos = n->pos;
3279         } else if (n->n.knot == knot) {
3280             n->n.pos = n->pos;
3281         }
3282         sp_node_update_handles(n);
3283         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3284         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3285         sp_nodepath_update_statusbar(nodepath);
3287     } else { // just select or add to selection, depending in Shift
3288         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3289     }
3292 /**
3293  * Node handle grabbed callback.
3294  */
3295 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3297    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3299     if (!n->selected) {
3300         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3301     }
3303     // remember the origin point of the handle
3304     if (n->p.knot == knot) {
3305         n->p.origin_radial = n->p.pos - n->pos;
3306     } else if (n->n.knot == knot) {
3307         n->n.origin_radial = n->n.pos - n->pos;
3308     } else {
3309         g_assert_not_reached();
3310     }
3312     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3315 /**
3316  * Node handle ungrabbed callback.
3317  */
3318 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3320    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3322     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3323     if (n->p.knot == knot) {
3324         n->p.origin_radial.a = 0;
3325         sp_knot_set_position(knot, &n->p.pos, state);
3326     } else if (n->n.knot == knot) {
3327         n->n.origin_radial.a = 0;
3328         sp_knot_set_position(knot, &n->n.pos, state);
3329     } else {
3330         g_assert_not_reached();
3331     }
3333     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3336 /**
3337  * Node handle "request" signal callback.
3338  */
3339 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3341     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3343     Inkscape::NodePath::NodeSide *me, *opposite;
3344     gint which;
3345     if (n->p.knot == knot) {
3346         me = &n->p;
3347         opposite = &n->n;
3348         which = -1;
3349     } else if (n->n.knot == knot) {
3350         me = &n->n;
3351         opposite = &n->p;
3352         which = 1;
3353     } else {
3354         me = opposite = NULL;
3355         which = 0;
3356         g_assert_not_reached();
3357     }
3359     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3361     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3363     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3364         /* We are smooth node adjacent with line */
3365         NR::Point const delta = *p - n->pos;
3366         NR::Coord const len = NR::L2(delta);
3367         Inkscape::NodePath::Node *othernode = opposite->other;
3368         NR::Point const ndelta = n->pos - othernode->pos;
3369         NR::Coord const linelen = NR::L2(ndelta);
3370         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3371             NR::Coord const scal = dot(delta, ndelta) / linelen;
3372             (*p) = n->pos + (scal / linelen) * ndelta;
3373         }
3374         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3375     } else {
3376         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3377     }
3379     sp_node_adjust_handle(n, -which);
3381     return FALSE;
3384 /**
3385  * Node handle moved callback.
3386  */
3387 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3389    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3391    Inkscape::NodePath::NodeSide *me;
3392    Inkscape::NodePath::NodeSide *other;
3393     if (n->p.knot == knot) {
3394         me = &n->p;
3395         other = &n->n;
3396     } else if (n->n.knot == knot) {
3397         me = &n->n;
3398         other = &n->p;
3399     } else {
3400         me = NULL;
3401         other = NULL;
3402         g_assert_not_reached();
3403     }
3405     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3406     Radial rme(me->pos - n->pos);
3407     Radial rother(other->pos - n->pos);
3408     Radial rnew(*p - n->pos);
3410     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3411         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3412         /* 0 interpreted as "no snapping". */
3414         // The closest PI/snaps angle, starting from zero.
3415         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3416         if (me->origin_radial.a == HUGE_VAL) {
3417             // ortho doesn't exist: original handle was zero length.
3418             rnew.a = a_snapped;
3419         } else {
3420             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3421              * its opposite and perpendiculars). */
3422             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3424             // Snap to the closest.
3425             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3426                        ? a_snapped
3427                        : a_ortho );
3428         }
3429     }
3431     if (state & GDK_MOD1_MASK) {
3432         // lock handle length
3433         rnew.r = me->origin_radial.r;
3434     }
3436     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3437         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3438         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3439         rother.a += rnew.a - rme.a;
3440         other->pos = NR::Point(rother) + n->pos;
3441         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3442         sp_knot_set_position(other->knot, &other->pos, 0);
3443     }
3445     me->pos = NR::Point(rnew) + n->pos;
3446     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3448     // move knot, but without emitting the signal:
3449     // we cannot emit a "moved" signal because we're now processing it
3450     sp_knot_moveto(me->knot, &(me->pos));
3452     update_object(n->subpath->nodepath);
3454     /* status text */
3455     SPDesktop *desktop = n->subpath->nodepath->desktop;
3456     if (!desktop) return;
3457     SPEventContext *ec = desktop->event_context;
3458     if (!ec) return;
3459     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3460     if (!mc) return;
3462     double degrees = 180 / M_PI * rnew.a;
3463     if (degrees > 180) degrees -= 360;
3464     if (degrees < -180) degrees += 360;
3465     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3466         degrees = angle_to_compass (degrees);
3468     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3470     mc->setF(Inkscape::NORMAL_MESSAGE,
3471          _("<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);
3473     g_string_free(length, TRUE);
3476 /**
3477  * Node handle event callback.
3478  */
3479 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3481     gboolean ret = FALSE;
3482     switch (event->type) {
3483         case GDK_KEY_PRESS:
3484             switch (get_group0_keyval (&event->key)) {
3485                 case GDK_space:
3486                     if (event->key.state & GDK_BUTTON1_MASK) {
3487                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3488                         stamp_repr(nodepath);
3489                         ret = TRUE;
3490                     }
3491                     break;
3492                 default:
3493                     break;
3494             }
3495             break;
3496         default:
3497             break;
3498     }
3500     return ret;
3503 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3504                                  Radial &rme, Radial &rother, gboolean const both)
3506     rme.a += angle;
3507     if ( both
3508          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3509          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3510     {
3511         rother.a += angle;
3512     }
3515 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3516                                         Radial &rme, Radial &rother, gboolean const both)
3518     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3520     gdouble r;
3521     if ( both
3522          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3523          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3524     {
3525         r = MAX(rme.r, rother.r);
3526     } else {
3527         r = rme.r;
3528     }
3530     gdouble const weird_angle = atan2(norm_angle, r);
3531 /* Bulia says norm_angle is just the visible distance that the
3532  * object's end must travel on the screen.  Left as 'angle' for want of
3533  * a better name.*/
3535     rme.a += weird_angle;
3536     if ( both
3537          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3538          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3539     {
3540         rother.a += weird_angle;
3541     }
3544 /**
3545  * Rotate one node.
3546  */
3547 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3549     Inkscape::NodePath::NodeSide *me, *other;
3550     bool both = false;
3552     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3553     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3555     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3556         me = &(n->p);
3557         other = &(n->n);
3558     } else if (!n->p.other) {
3559         me = &(n->n);
3560         other = &(n->p);
3561     } else {
3562         if (which > 0) { // right handle
3563             if (xn > xp) {
3564                 me = &(n->n);
3565                 other = &(n->p);
3566             } else {
3567                 me = &(n->p);
3568                 other = &(n->n);
3569             }
3570         } else if (which < 0){ // left handle
3571             if (xn <= xp) {
3572                 me = &(n->n);
3573                 other = &(n->p);
3574             } else {
3575                 me = &(n->p);
3576                 other = &(n->n);
3577             }
3578         } else { // both handles
3579             me = &(n->n);
3580             other = &(n->p);
3581             both = true;
3582         }
3583     }
3585     Radial rme(me->pos - n->pos);
3586     Radial rother(other->pos - n->pos);
3588     if (screen) {
3589         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3590     } else {
3591         node_rotate_one_internal (*n, angle, rme, rother, both);
3592     }
3594     me->pos = n->pos + NR::Point(rme);
3596     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3597         other->pos =  n->pos + NR::Point(rother);
3598     }
3600     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3601     // so here we just move all the knots without emitting move signals, for speed
3602     sp_node_update_handles(n, false);
3605 /**
3606  * Rotate selected nodes.
3607  */
3608 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3610     if (!nodepath || !nodepath->selected) return;
3612     if (g_list_length(nodepath->selected) == 1) {
3613        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3614         node_rotate_one (n, angle, which, screen);
3615     } else {
3616        // rotate as an object:
3618         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3619         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3620         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3621             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3622             box.expandTo (n->pos); // contain all selected nodes
3623         }
3625         gdouble rot;
3626         if (screen) {
3627             gdouble const zoom = nodepath->desktop->current_zoom();
3628             gdouble const zmove = angle / zoom;
3629             gdouble const r = NR::L2(box.max() - box.midpoint());
3630             rot = atan2(zmove, r);
3631         } else {
3632             rot = angle;
3633         }
3635         NR::Matrix t =
3636             NR::Matrix (NR::translate(-box.midpoint())) *
3637             NR::Matrix (NR::rotate(rot)) *
3638             NR::Matrix (NR::translate(box.midpoint()));
3640         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3641             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3642             n->pos *= t;
3643             n->n.pos *= t;
3644             n->p.pos *= t;
3645             sp_node_update_handles(n, false);
3646         }
3647     }
3649     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3652 /**
3653  * Scale one node.
3654  */
3655 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3657     bool both = false;
3658     Inkscape::NodePath::NodeSide *me, *other;
3660     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3661     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3663     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3664         me = &(n->p);
3665         other = &(n->n);
3666         n->code = NR_CURVETO;
3667     } else if (!n->p.other) {
3668         me = &(n->n);
3669         other = &(n->p);
3670         if (n->n.other)
3671             n->n.other->code = NR_CURVETO;
3672     } else {
3673         if (which > 0) { // right handle
3674             if (xn > xp) {
3675                 me = &(n->n);
3676                 other = &(n->p);
3677                 if (n->n.other)
3678                     n->n.other->code = NR_CURVETO;
3679             } else {
3680                 me = &(n->p);
3681                 other = &(n->n);
3682                 n->code = NR_CURVETO;
3683             }
3684         } else if (which < 0){ // left handle
3685             if (xn <= xp) {
3686                 me = &(n->n);
3687                 other = &(n->p);
3688                 if (n->n.other)
3689                     n->n.other->code = NR_CURVETO;
3690             } else {
3691                 me = &(n->p);
3692                 other = &(n->n);
3693                 n->code = NR_CURVETO;
3694             }
3695         } else { // both handles
3696             me = &(n->n);
3697             other = &(n->p);
3698             both = true;
3699             n->code = NR_CURVETO;
3700             if (n->n.other)
3701                 n->n.other->code = NR_CURVETO;
3702         }
3703     }
3705     Radial rme(me->pos - n->pos);
3706     Radial rother(other->pos - n->pos);
3708     rme.r += grow;
3709     if (rme.r < 0) rme.r = 0;
3710     if (rme.a == HUGE_VAL) {
3711         if (me->other) { // if direction is unknown, initialize it towards the next node
3712             Radial rme_next(me->other->pos - n->pos);
3713             rme.a = rme_next.a;
3714         } else { // if there's no next, initialize to 0
3715             rme.a = 0;
3716         }
3717     }
3718     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3719         rother.r += grow;
3720         if (rother.r < 0) rother.r = 0;
3721         if (rother.a == HUGE_VAL) {
3722             rother.a = rme.a + M_PI;
3723         }
3724     }
3726     me->pos = n->pos + NR::Point(rme);
3728     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3729         other->pos = n->pos + NR::Point(rother);
3730     }
3732     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3733     // so here we just move all the knots without emitting move signals, for speed
3734     sp_node_update_handles(n, false);
3737 /**
3738  * Scale selected nodes.
3739  */
3740 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3742     if (!nodepath || !nodepath->selected) return;
3744     if (g_list_length(nodepath->selected) == 1) {
3745         // scale handles of the single selected node
3746         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3747         node_scale_one (n, grow, which);
3748     } else {
3749         // scale nodes as an "object":
3751         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3752         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3753         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3754             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3755             box.expandTo (n->pos); // contain all selected nodes
3756         }
3758         double scale = (box.maxExtent() + grow)/box.maxExtent();
3760         NR::Matrix t =
3761             NR::Matrix (NR::translate(-box.midpoint())) *
3762             NR::Matrix (NR::scale(scale, scale)) *
3763             NR::Matrix (NR::translate(box.midpoint()));
3765         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3766             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3767             n->pos *= t;
3768             n->n.pos *= t;
3769             n->p.pos *= t;
3770             sp_node_update_handles(n, false);
3771         }
3772     }
3774     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3777 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3779     if (!nodepath) return;
3780     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3783 /**
3784  * Flip selected nodes horizontally/vertically.
3785  */
3786 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3788     if (!nodepath || !nodepath->selected) return;
3790     if (g_list_length(nodepath->selected) == 1) {
3791         // flip handles of the single selected node
3792         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3793         double temp = n->p.pos[axis];
3794         n->p.pos[axis] = n->n.pos[axis];
3795         n->n.pos[axis] = temp;
3796         sp_node_update_handles(n, false);
3797     } else {
3798         // scale nodes as an "object":
3800         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3801         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3802         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3803             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3804             box.expandTo (n->pos); // contain all selected nodes
3805         }
3807         NR::Matrix t =
3808             NR::Matrix (NR::translate(-box.midpoint())) *
3809             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3810             NR::Matrix (NR::translate(box.midpoint()));
3812         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3813             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3814             n->pos *= t;
3815             n->n.pos *= t;
3816             n->p.pos *= t;
3817             sp_node_update_handles(n, false);
3818         }
3819     }
3821     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3824 //-----------------------------------------------
3825 /**
3826  * Return new subpath under given nodepath.
3827  */
3828 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3830     g_assert(nodepath);
3831     g_assert(nodepath->desktop);
3833    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3835     s->nodepath = nodepath;
3836     s->closed = FALSE;
3837     s->nodes = NULL;
3838     s->first = NULL;
3839     s->last = NULL;
3841     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3842     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3843     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3845     return s;
3848 /**
3849  * Destroy nodes in subpath, then subpath itself.
3850  */
3851 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3853     g_assert(subpath);
3854     g_assert(subpath->nodepath);
3855     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3857     while (subpath->nodes) {
3858         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3859     }
3861     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3863     g_free(subpath);
3866 /**
3867  * Link head to tail in subpath.
3868  */
3869 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3871     g_assert(!sp->closed);
3872     g_assert(sp->last != sp->first);
3873     g_assert(sp->first->code == NR_MOVETO);
3875     sp->closed = TRUE;
3877     //Link the head to the tail
3878     sp->first->p.other = sp->last;
3879     sp->last->n.other  = sp->first;
3880     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3881     sp->first          = sp->last;
3883     //Remove the extra end node
3884     sp_nodepath_node_destroy(sp->last->n.other);
3887 /**
3888  * Open closed (loopy) subpath at node.
3889  */
3890 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3892     g_assert(sp->closed);
3893     g_assert(n->subpath == sp);
3894     g_assert(sp->first == sp->last);
3896     /* We create new startpoint, current node will become last one */
3898    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3899                                                 &n->pos, &n->pos, &n->n.pos);
3902     sp->closed        = FALSE;
3904     //Unlink to make a head and tail
3905     sp->first         = new_path;
3906     sp->last          = n;
3907     n->n.other        = NULL;
3908     new_path->p.other = NULL;
3911 /**
3912  * Returns area in triangle given by points; may be negative.
3913  */
3914 inline double
3915 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3917     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]);
3920 /**
3921  * Return new node in subpath with given properties.
3922  * \param pos Position of node.
3923  * \param ppos Handle position in previous direction
3924  * \param npos Handle position in previous direction
3925  */
3926 Inkscape::NodePath::Node *
3927 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)
3929     g_assert(sp);
3930     g_assert(sp->nodepath);
3931     g_assert(sp->nodepath->desktop);
3933     if (nodechunk == NULL)
3934         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3936     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3938     n->subpath  = sp;
3940     if (type != Inkscape::NodePath::NODE_NONE) {
3941         // use the type from sodipodi:nodetypes
3942         n->type = type;
3943     } else {
3944         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3945             // points are (almost) collinear
3946             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3947                 // endnode, or a node with a retracted handle
3948                 n->type = Inkscape::NodePath::NODE_CUSP;
3949             } else {
3950                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3951             }
3952         } else {
3953             n->type = Inkscape::NodePath::NODE_CUSP;
3954         }
3955     }
3957     n->code     = code;
3958     n->selected = FALSE;
3959     n->pos      = *pos;
3960     n->p.pos    = *ppos;
3961     n->n.pos    = *npos;
3963     n->dragging_out = NULL;
3965     Inkscape::NodePath::Node *prev;
3966     if (next) {
3967         //g_assert(g_list_find(sp->nodes, next));
3968         prev = next->p.other;
3969     } else {
3970         prev = sp->last;
3971     }
3973     if (prev)
3974         prev->n.other = n;
3975     else
3976         sp->first = n;
3978     if (next)
3979         next->p.other = n;
3980     else
3981         sp->last = n;
3983     n->p.other = prev;
3984     n->n.other = next;
3986     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"));
3987     sp_knot_set_position(n->knot, pos, 0);
3989     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3990     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3991     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3992     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3993     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3994     sp_knot_update_ctrl(n->knot);
3996     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3997     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3998     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3999     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4000     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4001     sp_knot_show(n->knot);
4003     // We only create handle knots and lines on demand
4004     n->p.knot = NULL;
4005     n->p.line = NULL;
4006     n->n.knot = NULL;
4007     n->n.line = NULL;
4009     sp->nodes = g_list_prepend(sp->nodes, n);
4011     return n;
4014 /**
4015  * Destroy node and its knots, link neighbors in subpath.
4016  */
4017 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4019     g_assert(node);
4020     g_assert(node->subpath);
4021     g_assert(SP_IS_KNOT(node->knot));
4023    Inkscape::NodePath::SubPath *sp = node->subpath;
4025     if (node->selected) { // first, deselect
4026         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4027         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4028     }
4030     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4032     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4033     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4034     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4035     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4036     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4037     g_object_unref(G_OBJECT(node->knot));
4039     if (node->p.knot) {
4040         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4041         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4042         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4043         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4044         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4045         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4046         g_object_unref(G_OBJECT(node->p.knot));
4047         node->p.knot = NULL;
4048     }
4050     if (node->n.knot) {
4051         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4052         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4053         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4054         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4055         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4056         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4057         g_object_unref(G_OBJECT(node->n.knot));
4058         node->n.knot = NULL;
4059     }
4061     if (node->p.line)
4062         gtk_object_destroy(GTK_OBJECT(node->p.line));
4063     if (node->n.line)
4064         gtk_object_destroy(GTK_OBJECT(node->n.line));
4066     if (sp->nodes) { // there are others nodes on the subpath
4067         if (sp->closed) {
4068             if (sp->first == node) {
4069                 g_assert(sp->last == node);
4070                 sp->first = node->n.other;
4071                 sp->last = sp->first;
4072             }
4073             node->p.other->n.other = node->n.other;
4074             node->n.other->p.other = node->p.other;
4075         } else {
4076             if (sp->first == node) {
4077                 sp->first = node->n.other;
4078                 sp->first->code = NR_MOVETO;
4079             }
4080             if (sp->last == node) sp->last = node->p.other;
4081             if (node->p.other) node->p.other->n.other = node->n.other;
4082             if (node->n.other) node->n.other->p.other = node->p.other;
4083         }
4084     } else { // this was the last node on subpath
4085         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4086     }
4088     g_mem_chunk_free(nodechunk, node);
4091 /**
4092  * Returns one of the node's two sides.
4093  * \param which Indicates which side.
4094  * \return Pointer to previous node side if which==-1, next if which==1.
4095  */
4096 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4098     g_assert(node);
4100     switch (which) {
4101         case -1:
4102             return &node->p;
4103         case 1:
4104             return &node->n;
4105         default:
4106             break;
4107     }
4109     g_assert_not_reached();
4111     return NULL;
4114 /**
4115  * Return the other side of the node, given one of its sides.
4116  */
4117 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4119     g_assert(node);
4121     if (me == &node->p) return &node->n;
4122     if (me == &node->n) return &node->p;
4124     g_assert_not_reached();
4126     return NULL;
4129 /**
4130  * Return NRPathcode on the given side of the node.
4131  */
4132 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4134     g_assert(node);
4136     if (me == &node->p) {
4137         if (node->p.other) return (NRPathcode)node->code;
4138         return NR_MOVETO;
4139     }
4141     if (me == &node->n) {
4142         if (node->n.other) return (NRPathcode)node->n.other->code;
4143         return NR_MOVETO;
4144     }
4146     g_assert_not_reached();
4148     return NR_END;
4151 /**
4152  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4153  */
4154 Inkscape::NodePath::Node *
4155 sp_nodepath_get_node_by_index(int index)
4157     Inkscape::NodePath::Node *e = NULL;
4159     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4160     if (!nodepath) {
4161         return e;
4162     }
4164     //find segment
4165     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4167         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4168         int n = g_list_length(sp->nodes);
4169         if (sp->closed) {
4170             n++;
4171         }
4173         //if the piece belongs to this subpath grab it
4174         //otherwise move onto the next subpath
4175         if (index < n) {
4176             e = sp->first;
4177             for (int i = 0; i < index; ++i) {
4178                 e = e->n.other;
4179             }
4180             break;
4181         } else {
4182             if (sp->closed) {
4183                 index -= (n+1);
4184             } else {
4185                 index -= n;
4186             }
4187         }
4188     }
4190     return e;
4193 /**
4194  * Returns plain text meaning of node type.
4195  */
4196 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4198     unsigned retracted = 0;
4199     bool endnode = false;
4201     for (int which = -1; which <= 1; which += 2) {
4202         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4203         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4204             retracted ++;
4205         if (!side->other)
4206             endnode = true;
4207     }
4209     if (retracted == 0) {
4210         if (endnode) {
4211                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4212                 return _("end node");
4213         } else {
4214             switch (node->type) {
4215                 case Inkscape::NodePath::NODE_CUSP:
4216                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4217                     return _("cusp");
4218                 case Inkscape::NodePath::NODE_SMOOTH:
4219                     // TRANSLATORS: "smooth" is an adjective here
4220                     return _("smooth");
4221                 case Inkscape::NodePath::NODE_SYMM:
4222                     return _("symmetric");
4223             }
4224         }
4225     } else if (retracted == 1) {
4226         if (endnode) {
4227             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4228             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4229         } else {
4230             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4231         }
4232     } else {
4233         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4234     }
4236     return NULL;
4239 /**
4240  * Handles content of statusbar as long as node tool is active.
4241  */
4242 void
4243 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4245     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");
4246     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4248     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4249     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4250     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4251     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4253     SPDesktop *desktop = NULL;
4254     if (nodepath) {
4255         desktop = nodepath->desktop;
4256     } else {
4257         desktop = SP_ACTIVE_DESKTOP;
4258     }
4260     SPEventContext *ec = desktop->event_context;
4261     if (!ec) return;
4262     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4263     if (!mc) return;
4265     if (selected_nodes == 0) {
4266         Inkscape::Selection *sel = desktop->selection;
4267         if (!sel || sel->isEmpty()) {
4268             mc->setF(Inkscape::NORMAL_MESSAGE,
4269                      _("Select a single object to edit its nodes or handles."));
4270         } else {
4271             if (nodepath) {
4272             mc->setF(Inkscape::NORMAL_MESSAGE,
4273                      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.",
4274                               "<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.",
4275                               total_nodes),
4276                      total_nodes);
4277             } else {
4278                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4279                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4280                 } else {
4281                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4282                 }
4283             }
4284         }
4285     } else if (nodepath && selected_nodes == 1) {
4286         mc->setF(Inkscape::NORMAL_MESSAGE,
4287                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4288                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4289                           total_nodes),
4290                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4291     } else {
4292         if (selected_subpaths > 1) {
4293             mc->setF(Inkscape::NORMAL_MESSAGE,
4294                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4295                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4296                               total_nodes),
4297                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4298         } else {
4299             mc->setF(Inkscape::NORMAL_MESSAGE,
4300                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4301                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4302                               total_nodes),
4303                      selected_nodes, total_nodes, when_selected);
4304         }
4305     }
4309 /*
4310   Local Variables:
4311   mode:c++
4312   c-file-style:"stroustrup"
4313   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4314   indent-tabs-mode:nil
4315   fill-column:99
4316   End:
4317 */
4318 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :