Code

e3681f59adcd94e7a4225407bf8edf8ecf5728aa
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/curve.h"
19 #include "display/sp-ctrlline.h"
20 #include "display/sodipodi-ctrl.h"
21 #include <glibmm/i18n.h>
22 #include "libnr/n-art-bpath.h"
23 #include "helper/units.h"
24 #include "knot.h"
25 #include "inkscape.h"
26 #include "document.h"
27 #include "sp-namedview.h"
28 #include "desktop.h"
29 #include "desktop-handles.h"
30 #include "snap.h"
31 #include "message-stack.h"
32 #include "message-context.h"
33 #include "node-context.h"
34 #include "selection-chemistry.h"
35 #include "selection.h"
36 #include "xml/repr.h"
37 #include "prefs-utils.h"
38 #include "sp-metrics.h"
39 #include "sp-path.h"
40 #include "libnr/nr-matrix-ops.h"
41 #include "splivarot.h"
42 #include "svg/svg.h"
43 #include "verbs.h"
44 #include "display/bezier-utils.h"
45 #include <vector>
46 #include <algorithm>
48 class NR::Matrix;
50 /// \todo
51 /// evil evil evil. FIXME: conflict of two different Path classes!
52 /// There is a conflict in the namespace between two classes named Path.
53 /// #include "sp-flowtext.h"
54 /// #include "sp-flowregion.h"
56 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
57 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
58 GType sp_flowregion_get_type (void);
59 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
60 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
61 GType sp_flowtext_get_type (void);
62 // end evil workaround
64 #include "helper/stlport.h"
67 /// \todo fixme: Implement these via preferences */
69 #define NODE_FILL          0xbfbfbf00
70 #define NODE_STROKE        0x000000ff
71 #define NODE_FILL_HI       0xff000000
72 #define NODE_STROKE_HI     0x000000ff
73 #define NODE_FILL_SEL      0x0000ffff
74 #define NODE_STROKE_SEL    0x000000ff
75 #define NODE_FILL_SEL_HI   0xff000000
76 #define NODE_STROKE_SEL_HI 0x000000ff
77 #define KNOT_FILL          0xffffffff
78 #define KNOT_STROKE        0x000000ff
79 #define KNOT_FILL_HI       0xff000000
80 #define KNOT_STROKE_HI     0x000000ff
82 static GMemChunk *nodechunk = NULL;
84 /* Creation from object */
86 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
87 static gchar *parse_nodetypes(gchar const *types, gint length);
89 /* Object updating */
91 static void stamp_repr(Inkscape::NodePath::Path *np);
92 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
93 static gchar *create_typestr(Inkscape::NodePath::Path *np);
95 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
97 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
99 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
101 /* Adjust handle placement, if the node or the other handle is moved */
102 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
103 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
105 /* Node event callbacks */
106 static void node_clicked(SPKnot *knot, guint state, gpointer data);
107 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
108 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
109 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
111 /* Handle event callbacks */
112 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
113 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
114 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
115 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
116 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
117 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
119 /* Constructors and destructors */
121 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
122 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
123 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
124 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
125 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
126                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
127 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
129 /* Helpers */
131 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
132 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
133 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
135 // active_node indicates mouseover node
136 static Inkscape::NodePath::Node *active_node = NULL;
138 /**
139  * \brief Creates new nodepath from item
140  */
141 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool show_handles)
143     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
145     /** \todo
146      * FIXME: remove this. We don't want to edit paths inside flowtext.
147      * Instead we will build our flowtext with cloned paths, so that the
148      * real paths are outside the flowtext and thus editable as usual.
149      */
150     if (SP_IS_FLOWTEXT(item)) {
151         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
152             if SP_IS_FLOWREGION(child) {
153                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
154                 if (grandchild && SP_IS_PATH(grandchild)) {
155                     item = SP_ITEM(grandchild);
156                     break;
157                 }
158             }
159         }
160     }
162     if (!SP_IS_PATH(item))
163         return NULL;
164     SPPath *path = SP_PATH(item);
165     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
166     if (curve == NULL)
167         return NULL;
169     NArtBpath *bpath = sp_curve_first_bpath(curve);
170     gint length = curve->end;
171     if (length == 0)
172         return NULL; // prevent crash for one-node paths
174     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
175     gchar *typestr = parse_nodetypes(nodetypes, length);
177     //Create new nodepath
178     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
179     if (!np)
180         return NULL;
182     // Set defaults
183     np->desktop     = desktop;
184     np->path        = path;
185     np->subpaths    = NULL;
186     np->selected    = NULL;
187     np->nodeContext = NULL; //Let the context that makes this set it
188     np->livarot_path = NULL;
189     np->local_change = 0;
190     np->show_handles = show_handles;
192     // we need to update item's transform from the repr here,
193     // because they may be out of sync when we respond
194     // to a change in repr by regenerating nodepath     --bb
195     sp_object_read_attr(SP_OBJECT(item), "transform");
197     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
198     np->d2i  = np->i2d.inverse();
199     np->repr = repr;
201     // create the subpath(s) from the bpath
202     NArtBpath *b = bpath;
203     while (b->code != NR_END) {
204         b = subpath_from_bpath(np, b, typestr + (b - bpath));
205     }
207     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
208     np->subpaths = g_list_reverse(np->subpaths);
210     g_free(typestr);
211     sp_curve_unref(curve);
213     // create the livarot representation from the same item
214     np->livarot_path = Path_for_item(item, true, true);
215     if (np->livarot_path)
216         np->livarot_path->ConvertWithBackData(0.01);
218     return np;
221 /**
222  * Destroys nodepath's subpaths, then itself, also tell context about it.
223  */
224 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
226     if (!np)  //soft fail, like delete
227         return;
229     while (np->subpaths) {
230         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
231     }
233     //Inform the context that made me, if any, that I am gone.
234     if (np->nodeContext)
235         np->nodeContext->nodepath = NULL;
237     g_assert(!np->selected);
239     if (np->livarot_path) {
240         delete np->livarot_path;
241         np->livarot_path = NULL;
242     }
244     np->desktop = NULL;
246     g_free(np);
250 /**
251  *  Return the node count of a given NodeSubPath.
252  */
253 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
255     if (!subpath)
256         return 0;
257     gint nodeCount = g_list_length(subpath->nodes);
258     return nodeCount;
261 /**
262  *  Return the node count of a given NodePath.
263  */
264 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
266     if (!np)
267         return 0;
268     gint nodeCount = 0;
269     for (GList *item = np->subpaths ; item ; item=item->next) {
270        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
271         nodeCount += g_list_length(subpath->nodes);
272     }
273     return nodeCount;
276 /**
277  *  Return the subpath count of a given NodePath.
278  */
279 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
281     if (!np)
282         return 0;
283     return g_list_length (np->subpaths);
286 /**
287  *  Return the selected node count of a given NodePath.
288  */
289 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
291     if (!np)
292         return 0;
293     return g_list_length (np->selected);
296 /**
297  *  Return the number of subpaths where nodes are selected in a given NodePath.
298  */
299 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
301     if (!np)
302         return 0;
303     if (!np->selected)
304         return 0;
305     if (!np->selected->next)
306         return 1;
307     gint count = 0;
308     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
309         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
310         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
311             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
312             if (node->selected) {
313                 count ++;
314                 break;
315             }
316         }
317     }
318     return count;
320  
321 /**
322  * Clean up a nodepath after editing.
323  *
324  * Currently we are deleting trivial subpaths.
325  */
326 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
328     GList *badSubPaths = NULL;
330     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
331     for (GList *l = nodepath->subpaths; l ; l=l->next) {
332        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
333        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
334             badSubPaths = g_list_append(badSubPaths, sp);
335     }
337     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
338     //also removes the subpath from nodepath->subpaths
339     for (GList *l = badSubPaths; l ; l=l->next) {
340        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
341         sp_nodepath_subpath_destroy(sp);
342     }
344     g_list_free(badSubPaths);
347 /**
348  * Create new nodepath from b, make it subpath of np.
349  * \param t The node type.
350  * \todo Fixme: t should be a proper type, rather than gchar
351  */
352 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
354     NR::Point ppos, pos, npos;
356     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
358     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
359     bool const closed = (b->code == NR_MOVETO);
361     pos = NR::Point(b->x3, b->y3) * np->i2d;
362     if (b[1].code == NR_CURVETO) {
363         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
364     } else {
365         npos = pos;
366     }
367     Inkscape::NodePath::Node *n;
368     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
369     g_assert(sp->first == n);
370     g_assert(sp->last  == n);
372     b++;
373     t++;
374     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
375         pos = NR::Point(b->x3, b->y3) * np->i2d;
376         if (b->code == NR_CURVETO) {
377             ppos = NR::Point(b->x2, b->y2) * np->i2d;
378         } else {
379             ppos = pos;
380         }
381         if (b[1].code == NR_CURVETO) {
382             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
383         } else {
384             npos = pos;
385         }
386         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
387         b++;
388         t++;
389     }
391     if (closed) sp_nodepath_subpath_close(sp);
393     return b;
396 /**
397  * Convert from sodipodi:nodetypes to new style type string.
398  */
399 static gchar *parse_nodetypes(gchar const *types, gint length)
401     g_assert(length > 0);
403     gchar *typestr = g_new(gchar, length + 1);
405     gint pos = 0;
407     if (types) {
408         for (gint i = 0; types[i] && ( i < length ); i++) {
409             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
410             if (types[i] != '\0') {
411                 switch (types[i]) {
412                     case 's':
413                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
414                         break;
415                     case 'z':
416                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
417                         break;
418                     case 'c':
419                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
420                         break;
421                     default:
422                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
423                         break;
424                 }
425             }
426         }
427     }
429     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
431     return typestr;
434 /**
435  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
436  * updated but repr is not (for speed). Used during curve and node drag.
437  */
438 static void update_object(Inkscape::NodePath::Path *np)
440     g_assert(np);
442     SPCurve *curve = create_curve(np);
444     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
446     sp_curve_unref(curve);
449 /**
450  * Update XML path node with data from path object.
451  */
452 static void update_repr_internal(Inkscape::NodePath::Path *np)
454     g_assert(np);
456     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
458     SPCurve *curve = create_curve(np);
459     gchar *typestr = create_typestr(np);
460     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
462     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
463         np->local_change++;
464         repr->setAttribute("d", svgpath);
465     }
467     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
468         np->local_change++;
469         repr->setAttribute("sodipodi:nodetypes", typestr);
470     }
472     g_free(svgpath);
473     g_free(typestr);
474     sp_curve_unref(curve);
477 /**
478  * Update XML path node with data from path object, commit changes forever.
479  */
480 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
482     //fixme: np can be NULL, so check before proceeding
483     g_return_if_fail(np != NULL);
485     if (np->livarot_path) {
486         delete np->livarot_path;
487         np->livarot_path = NULL;
488     }
490     if (np->path && SP_IS_ITEM(np->path)) {
491         np->livarot_path = Path_for_item (np->path, true, true);
492         if (np->livarot_path)
493             np->livarot_path->ConvertWithBackData(0.01);
494     }
496     update_repr_internal(np);
497     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
498                      annotation);
501 /**
502  * Update XML path node with data from path object, commit changes with undo.
503  */
504 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
506     update_repr_internal(np);
507     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
508                            annotation);
510     if (np->livarot_path) {
511         delete np->livarot_path;
512         np->livarot_path = NULL;
513     }
515     if (np->path && SP_IS_ITEM(np->path)) {
516         np->livarot_path = Path_for_item (np->path, true, true);
517         if (np->livarot_path)
518             np->livarot_path->ConvertWithBackData(0.01);
519     }
522 /**
523  * Make duplicate of path, replace corresponding XML node in tree, commit.
524  */
525 static void stamp_repr(Inkscape::NodePath::Path *np)
527     g_assert(np);
529     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
530     Inkscape::XML::Node *new_repr = old_repr->duplicate();
532     // remember the position of the item
533     gint pos = old_repr->position();
534     // remember parent
535     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
537     SPCurve *curve = create_curve(np);
538     gchar *typestr = create_typestr(np);
540     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
542     new_repr->setAttribute("d", svgpath);
543     new_repr->setAttribute("sodipodi:nodetypes", typestr);
545     // add the new repr to the parent
546     parent->appendChild(new_repr);
547     // move to the saved position
548     new_repr->setPosition(pos > 0 ? pos : 0);
550     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
551                      _("Stamp"));
553     Inkscape::GC::release(new_repr);
554     g_free(svgpath);
555     g_free(typestr);
556     sp_curve_unref(curve);
559 /**
560  * Create curve from path.
561  */
562 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
564     SPCurve *curve = sp_curve_new();
566     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
567        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
568         sp_curve_moveto(curve,
569                         sp->first->pos * np->d2i);
570        Inkscape::NodePath::Node *n = sp->first->n.other;
571         while (n) {
572             NR::Point const end_pt = n->pos * np->d2i;
573             switch (n->code) {
574                 case NR_LINETO:
575                     sp_curve_lineto(curve, end_pt);
576                     break;
577                 case NR_CURVETO:
578                     sp_curve_curveto(curve,
579                                      n->p.other->n.pos * np->d2i,
580                                      n->p.pos * np->d2i,
581                                      end_pt);
582                     break;
583                 default:
584                     g_assert_not_reached();
585                     break;
586             }
587             if (n != sp->last) {
588                 n = n->n.other;
589             } else {
590                 n = NULL;
591             }
592         }
593         if (sp->closed) {
594             sp_curve_closepath(curve);
595         }
596     }
598     return curve;
601 /**
602  * Convert path type string to sodipodi:nodetypes style.
603  */
604 static gchar *create_typestr(Inkscape::NodePath::Path *np)
606     gchar *typestr = g_new(gchar, 32);
607     gint len = 32;
608     gint pos = 0;
610     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
611        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
613         if (pos >= len) {
614             typestr = g_renew(gchar, typestr, len + 32);
615             len += 32;
616         }
618         typestr[pos++] = 'c';
620        Inkscape::NodePath::Node *n;
621         n = sp->first->n.other;
622         while (n) {
623             gchar code;
625             switch (n->type) {
626                 case Inkscape::NodePath::NODE_CUSP:
627                     code = 'c';
628                     break;
629                 case Inkscape::NodePath::NODE_SMOOTH:
630                     code = 's';
631                     break;
632                 case Inkscape::NodePath::NODE_SYMM:
633                     code = 'z';
634                     break;
635                 default:
636                     g_assert_not_reached();
637                     code = '\0';
638                     break;
639             }
641             if (pos >= len) {
642                 typestr = g_renew(gchar, typestr, len + 32);
643                 len += 32;
644             }
646             typestr[pos++] = code;
648             if (n != sp->last) {
649                 n = n->n.other;
650             } else {
651                 n = NULL;
652             }
653         }
654     }
656     if (pos >= len) {
657         typestr = g_renew(gchar, typestr, len + 1);
658         len += 1;
659     }
661     typestr[pos++] = '\0';
663     return typestr;
666 /**
667  * Returns current path in context.
668  */
669 static Inkscape::NodePath::Path *sp_nodepath_current()
671     if (!SP_ACTIVE_DESKTOP) {
672         return NULL;
673     }
675     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
677     if (!SP_IS_NODE_CONTEXT(event_context)) {
678         return NULL;
679     }
681     return SP_NODE_CONTEXT(event_context)->nodepath;
686 /**
687  \brief Fills node and handle positions for three nodes, splitting line
688   marked by end at distance t.
689  */
690 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
692     g_assert(new_path != NULL);
693     g_assert(end      != NULL);
695     g_assert(end->p.other == new_path);
696    Inkscape::NodePath::Node *start = new_path->p.other;
697     g_assert(start);
699     if (end->code == NR_LINETO) {
700         new_path->type =Inkscape::NodePath::NODE_CUSP;
701         new_path->code = NR_LINETO;
702         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
703     } else {
704         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
705         new_path->code = NR_CURVETO;
706         gdouble s      = 1 - t;
707         for (int dim = 0; dim < 2; dim++) {
708             NR::Coord const f000 = start->pos[dim];
709             NR::Coord const f001 = start->n.pos[dim];
710             NR::Coord const f011 = end->p.pos[dim];
711             NR::Coord const f111 = end->pos[dim];
712             NR::Coord const f00t = s * f000 + t * f001;
713             NR::Coord const f01t = s * f001 + t * f011;
714             NR::Coord const f11t = s * f011 + t * f111;
715             NR::Coord const f0tt = s * f00t + t * f01t;
716             NR::Coord const f1tt = s * f01t + t * f11t;
717             NR::Coord const fttt = s * f0tt + t * f1tt;
718             start->n.pos[dim]    = f00t;
719             new_path->p.pos[dim] = f0tt;
720             new_path->pos[dim]   = fttt;
721             new_path->n.pos[dim] = f1tt;
722             end->p.pos[dim]      = f11t;
723         }
724     }
727 /**
728  * Adds new node on direct line between two nodes, activates handles of all
729  * three nodes.
730  */
731 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
733     g_assert(end);
734     g_assert(end->subpath);
735     g_assert(g_list_find(end->subpath->nodes, end));
737    Inkscape::NodePath::Node *start = end->p.other;
738     g_assert( start->n.other == end );
739    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
740                                                end,
741                                               Inkscape::NodePath::NODE_SMOOTH,
742                                                (NRPathcode)end->code,
743                                                &start->pos, &start->pos, &start->n.pos);
744     sp_nodepath_line_midpoint(newnode, end, t);
746     sp_node_update_handles(start);
747     sp_node_update_handles(newnode);
748     sp_node_update_handles(end);
750     return newnode;
753 /**
754 \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
755 */
756 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
758     g_assert(node);
759     g_assert(node->subpath);
760     g_assert(g_list_find(node->subpath->nodes, node));
762    Inkscape::NodePath::SubPath *sp = node->subpath;
763     Inkscape::NodePath::Path *np    = sp->nodepath;
765     if (sp->closed) {
766         sp_nodepath_subpath_open(sp, node);
767         return sp->first;
768     } else {
769         // no break for end nodes
770         if (node == sp->first) return NULL;
771         if (node == sp->last ) return NULL;
773         // create a new subpath
774        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
776         // duplicate the break node as start of the new subpath
777        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
779         while (node->n.other) { // copy the remaining nodes into the new subpath
780            Inkscape::NodePath::Node *n  = node->n.other;
781            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);
782             if (n->selected) {
783                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
784             }
785             sp_nodepath_node_destroy(n); // remove the point on the original subpath
786         }
788         return newnode;
789     }
792 /**
793  * Duplicate node and connect to neighbours.
794  */
795 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
797     g_assert(node);
798     g_assert(node->subpath);
799     g_assert(g_list_find(node->subpath->nodes, node));
801    Inkscape::NodePath::SubPath *sp = node->subpath;
803     NRPathcode code = (NRPathcode) node->code;
804     if (code == NR_MOVETO) { // if node is the endnode,
805         node->code = NR_LINETO; // new one is inserted before it, so change that to line
806     }
808     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
810     if (!node->n.other || !node->p.other) // if node is an endnode, select it
811         return node;
812     else
813         return newnode; // otherwise select the newly created node
816 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
818     node->p.pos = (node->pos + (node->pos - node->n.pos));
821 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
823     node->n.pos = (node->pos + (node->pos - node->p.pos));
826 /**
827  * Change line type at node, with side effects on neighbours.
828  */
829 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
831     g_assert(end);
832     g_assert(end->subpath);
833     g_assert(end->p.other);
835     if (end->code == static_cast< guint > ( code ) )
836         return;
838    Inkscape::NodePath::Node *start = end->p.other;
840     end->code = code;
842     if (code == NR_LINETO) {
843         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
844         if (end->n.other) {
845             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
846         }
847         sp_node_adjust_handle(start, -1);
848         sp_node_adjust_handle(end, 1);
849     } else {
850         NR::Point delta = end->pos - start->pos;
851         start->n.pos = start->pos + delta / 3;
852         end->p.pos = end->pos - delta / 3;
853         sp_node_adjust_handle(start, 1);
854         sp_node_adjust_handle(end, -1);
855     }
857     sp_node_update_handles(start);
858     sp_node_update_handles(end);
861 /**
862  * Change node type, and its handles accordingly.
863  */
864 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
866     g_assert(node);
867     g_assert(node->subpath);
869     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
870         return node;
872     if ((node->p.other != NULL) && (node->n.other != NULL)) {
873         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
874             type =Inkscape::NodePath::NODE_CUSP;
875         }
876     }
878     node->type = type;
880     if (node->type == Inkscape::NodePath::NODE_CUSP) {
881         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
882         node->knot->setSize (node->selected? 11 : 9);
883         sp_knot_update_ctrl(node->knot);
884     } else {
885         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
886         node->knot->setSize (node->selected? 9 : 7);
887         sp_knot_update_ctrl(node->knot);
888     }
890     // if one of handles is mouseovered, preserve its position
891     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
892         sp_node_adjust_handle(node, 1);
893     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
894         sp_node_adjust_handle(node, -1);
895     } else {
896         sp_node_adjust_handles(node);
897     }
899     sp_node_update_handles(node);
901     sp_nodepath_update_statusbar(node->subpath->nodepath);
903     return node;
906 /**
907  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
908  * adjacent segments from lines to curves.
909 */
910 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
912     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
913         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
914             // convert adjacent segment BEFORE to curve
915             node->code = NR_CURVETO;
916             NR::Point delta;
917             if (node->n.other != NULL)
918                 delta = node->n.other->pos - node->p.other->pos;
919             else
920                 delta = node->pos - node->p.other->pos;
921             node->p.pos = node->pos - delta / 4;
922             sp_node_update_handles(node);
923         }
925         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
926             // convert adjacent segment AFTER to curve
927             node->n.other->code = NR_CURVETO;
928             NR::Point delta;
929             if (node->p.other != NULL)
930                 delta = node->p.other->pos - node->n.other->pos;
931             else
932                 delta = node->pos - node->n.other->pos;
933             node->n.pos = node->pos - delta / 4;
934             sp_node_update_handles(node);
935         }
936     }
938     sp_nodepath_set_node_type (node, type);
941 /**
942  * Move node to point, and adjust its and neighbouring handles.
943  */
944 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
946     NR::Point delta = p - node->pos;
947     node->pos = p;
949     node->p.pos += delta;
950     node->n.pos += delta;
952     if (node->p.other) {
953         if (node->code == NR_LINETO) {
954             sp_node_adjust_handle(node, 1);
955             sp_node_adjust_handle(node->p.other, -1);
956         }
957     }
958     if (node->n.other) {
959         if (node->n.other->code == NR_LINETO) {
960             sp_node_adjust_handle(node, -1);
961             sp_node_adjust_handle(node->n.other, 1);
962         }
963     }
965     // this function is only called from batch movers that will update display at the end
966     // themselves, so here we just move all the knots without emitting move signals, for speed
967     sp_node_update_handles(node, false);
970 /**
971  * Call sp_node_moveto() for node selection and handle possible snapping.
972  */
973 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
974                                             bool const snap = true)
976     NR::Coord best = NR_HUGE;
977     NR::Point delta(dx, dy);
978     NR::Point best_pt = delta;
980     if (snap) {
981         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
982         
983         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
984             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
985             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
986             if (s.getDistance() < best) {
987                 best = s.getDistance();
988                 best_pt = s.getPoint() - n->pos;
989             }
990         }
991     }
993     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
994        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
995         sp_node_moveto(n, n->pos + best_pt);
996     }
998     // do not update repr here so that node dragging is acceptably fast
999     update_object(nodepath);
1002 /**
1003 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1004 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1005 near x = 0.
1006  */
1007 double
1008 sculpt_profile (double x, double alpha, guint profile)
1010     if (x >= 1)
1011         return 0;
1012     if (x <= 0)
1013         return 1;
1015     switch (profile) {
1016         case SCULPT_PROFILE_LINEAR:
1017         return 1 - x;
1018         case SCULPT_PROFILE_BELL:
1019         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1020         case SCULPT_PROFILE_ELLIPTIC:
1021         return sqrt(1 - x*x);
1022     }
1024     return 1;
1027 double
1028 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1030     // extremely primitive for now, don't have time to look for the real one
1031     double lower = NR::L2(b - a);
1032     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1033     return (lower + upper)/2;
1036 void
1037 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1039     n->pos = n->origin + delta;
1040     n->n.pos = n->n.origin + delta_n;
1041     n->p.pos = n->p.origin + delta_p;
1042     sp_node_adjust_handles(n);
1043     sp_node_update_handles(n, false);
1046 /**
1047  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1048  * on how far they are from the dragged node n.
1049  */
1050 static void 
1051 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1053     g_assert (n);
1054     g_assert (nodepath);
1055     g_assert (n->subpath->nodepath == nodepath);
1057     double pressure = n->knot->pressure;
1058     if (pressure == 0)
1059         pressure = 0.5; // default
1060     pressure = CLAMP (pressure, 0.2, 0.8);
1062     // map pressure to alpha = 1/5 ... 5
1063     double alpha = 1 - 2 * fabs(pressure - 0.5);
1064     if (pressure > 0.5)
1065         alpha = 1/alpha;
1067     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1069     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1070         // Only one subpath has selected nodes:
1071         // use linear mode, where the distance from n to node being dragged is calculated along the path
1073         double n_sel_range = 0, p_sel_range = 0;
1074         guint n_nodes = 0, p_nodes = 0;
1075         guint n_sel_nodes = 0, p_sel_nodes = 0;
1077         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1078         {
1079             double n_range = 0, p_range = 0;
1080             bool n_going = true, p_going = true;
1081             Inkscape::NodePath::Node *n_node = n;
1082             Inkscape::NodePath::Node *p_node = n;
1083             do {
1084                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1085                 if (n_node && n_going)
1086                     n_node = n_node->n.other;
1087                 if (n_node == NULL) {
1088                     n_going = false;
1089                 } else {
1090                     n_nodes ++;
1091                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1092                     if (n_node->selected) {
1093                         n_sel_nodes ++;
1094                         n_sel_range = n_range;
1095                     }
1096                     if (n_node == p_node) {
1097                         n_going = false;
1098                         p_going = false;
1099                     }
1100                 }
1101                 if (p_node && p_going)
1102                     p_node = p_node->p.other;
1103                 if (p_node == NULL) {
1104                     p_going = false;
1105                 } else {
1106                     p_nodes ++;
1107                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1108                     if (p_node->selected) {
1109                         p_sel_nodes ++;
1110                         p_sel_range = p_range;
1111                     }
1112                     if (p_node == n_node) {
1113                         n_going = false;
1114                         p_going = false;
1115                     }
1116                 }
1117             } while (n_going || p_going);
1118         }
1120         // Second pass: actually move nodes in this subpath
1121         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1122         {
1123             double n_range = 0, p_range = 0;
1124             bool n_going = true, p_going = true;
1125             Inkscape::NodePath::Node *n_node = n;
1126             Inkscape::NodePath::Node *p_node = n;
1127             do {
1128                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1129                 if (n_node && n_going)
1130                     n_node = n_node->n.other;
1131                 if (n_node == NULL) {
1132                     n_going = false;
1133                 } else {
1134                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1135                     if (n_node->selected) {
1136                         sp_nodepath_move_node_and_handles (n_node, 
1137                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1138                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1139                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1140                     }
1141                     if (n_node == p_node) {
1142                         n_going = false;
1143                         p_going = false;
1144                     }
1145                 }
1146                 if (p_node && p_going)
1147                     p_node = p_node->p.other;
1148                 if (p_node == NULL) {
1149                     p_going = false;
1150                 } else {
1151                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1152                     if (p_node->selected) {
1153                         sp_nodepath_move_node_and_handles (p_node, 
1154                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1155                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1156                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1157                     }
1158                     if (p_node == n_node) {
1159                         n_going = false;
1160                         p_going = false;
1161                     }
1162                 }
1163             } while (n_going || p_going);
1164         }
1166     } else {
1167         // Multiple subpaths have selected nodes:
1168         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1169         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1170         // fix the pear-like shape when sculpting e.g. a ring
1172         // First pass: calculate range
1173         gdouble direct_range = 0;
1174         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1175             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1176             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1177                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1178                 if (node->selected) {
1179                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1180                 }
1181             }
1182         }
1184         // Second pass: actually move nodes
1185         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1186             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1187             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1188                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1189                 if (node->selected) {
1190                     if (direct_range > 1e-6) {
1191                         sp_nodepath_move_node_and_handles (node,
1192                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1193                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1194                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1195                     } else {
1196                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1197                     }
1199                 }
1200             }
1201         }
1202     }
1204     // do not update repr here so that node dragging is acceptably fast
1205     update_object(nodepath);
1209 /**
1210  * Move node selection to point, adjust its and neighbouring handles,
1211  * handle possible snapping, and commit the change with possible undo.
1212  */
1213 void
1214 sp_node_selected_move(gdouble dx, gdouble dy)
1216     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1217     if (!nodepath) return;
1219     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1221     if (dx == 0) {
1222         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1223     } else if (dy == 0) {
1224         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1225     } else {
1226         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1227     }
1230 /**
1231  * Move node selection off screen and commit the change.
1232  */
1233 void
1234 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1236     // borrowed from sp_selection_move_screen in selection-chemistry.c
1237     // we find out the current zoom factor and divide deltas by it
1238     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1240     gdouble zoom = desktop->current_zoom();
1241     gdouble zdx = dx / zoom;
1242     gdouble zdy = dy / zoom;
1244     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1245     if (!nodepath) return;
1247     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1249     if (dx == 0) {
1250         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1251     } else if (dy == 0) {
1252         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1253     } else {
1254         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1255     }
1258 /** If they don't yet exist, creates knot and line for the given side of the node */
1259 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1261     if (!side->knot) {
1262         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"));
1264         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1265         side->knot->setSize (7);
1266         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1267         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1268         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1269         sp_knot_update_ctrl(side->knot);
1271         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1272         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1273         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1274         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1275         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1276         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1277     }
1279     if (!side->line) {
1280         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1281                                         SP_TYPE_CTRLLINE, NULL);
1282     }
1285 /**
1286  * Ensure the given handle of the node is visible/invisible, update its screen position
1287  */
1288 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1290     g_assert(node != NULL);
1292    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1293     NRPathcode code = sp_node_path_code_from_side(node, side);
1295     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1297     if (show_handle) {
1298         if (!side->knot) { // No handle knot at all
1299             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1300             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1301             side->knot->pos = side->pos;
1302             if (side->knot->item) 
1303                 SP_CTRL(side->knot->item)->moveto(side->pos);
1304             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1305             sp_knot_show(side->knot);
1306         } else {
1307             if (side->knot->pos != side->pos) { // only if it's really moved
1308                 if (fire_move_signals) {
1309                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1310                 } else {
1311                     sp_knot_moveto(side->knot, &side->pos);
1312                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1313                 }
1314             }
1315             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1316                 sp_knot_show(side->knot);
1317             }
1318         }
1319         sp_canvas_item_show(side->line);
1320     } else {
1321         if (side->knot) {
1322             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1323                 sp_knot_hide(side->knot);
1324             }
1325         }
1326         if (side->line) {
1327             sp_canvas_item_hide(side->line);
1328         }
1329     }
1332 /**
1333  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1334  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1335  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1336  * updated; otherwise, just move the knots silently (used in batch moves).
1337  */
1338 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1340     g_assert(node != NULL);
1342     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1343         sp_knot_show(node->knot);
1344     }
1346     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1347         if (fire_move_signals)
1348             sp_knot_set_position(node->knot, &node->pos, 0);
1349         else 
1350             sp_knot_moveto(node->knot, &node->pos);
1351     }
1353     gboolean show_handles = node->selected;
1354     if (node->p.other != NULL) {
1355         if (node->p.other->selected) show_handles = TRUE;
1356     }
1357     if (node->n.other != NULL) {
1358         if (node->n.other->selected) show_handles = TRUE;
1359     }
1361     if (node->subpath->nodepath->show_handles == false)
1362         show_handles = FALSE;
1364     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1365     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1368 /**
1369  * Call sp_node_update_handles() for all nodes on subpath.
1370  */
1371 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1373     g_assert(subpath != NULL);
1375     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1376         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1377     }
1380 /**
1381  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1382  */
1383 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1385     g_assert(nodepath != NULL);
1387     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1388         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1389     }
1392 void
1393 sp_nodepath_show_handles(bool show)
1395     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1396     if (nodepath == NULL) return;
1398     nodepath->show_handles = show;
1399     sp_nodepath_update_handles(nodepath);
1402 /**
1403  * Adds all selected nodes in nodepath to list.
1404  */
1405 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1407     StlConv<Node *>::list(l, selected);
1408 /// \todo this adds a copying, rework when the selection becomes a stl list
1411 /**
1412  * Align selected nodes on the specified axis.
1413  */
1414 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1416     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1417         return;
1418     }
1420     if ( !nodepath->selected->next ) { // only one node selected
1421         return;
1422     }
1423    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1424     NR::Point dest(pNode->pos);
1425     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1426         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1427         if (pNode) {
1428             dest[axis] = pNode->pos[axis];
1429             sp_node_moveto(pNode, dest);
1430         }
1431     }
1433     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1436 /// Helper struct.
1437 struct NodeSort
1439    Inkscape::NodePath::Node *_node;
1440     NR::Coord _coord;
1441     /// \todo use vectorof pointers instead of calling copy ctor
1442     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1443         _node(node), _coord(node->pos[axis])
1444     {}
1446 };
1448 static bool operator<(NodeSort const &a, NodeSort const &b)
1450     return (a._coord < b._coord);
1453 /**
1454  * Distribute selected nodes on the specified axis.
1455  */
1456 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1458     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1459         return;
1460     }
1462     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1463         return;
1464     }
1466    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1467     std::vector<NodeSort> sorted;
1468     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1469         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1470         if (pNode) {
1471             NodeSort n(pNode, axis);
1472             sorted.push_back(n);
1473             //dest[axis] = pNode->pos[axis];
1474             //sp_node_moveto(pNode, dest);
1475         }
1476     }
1477     std::sort(sorted.begin(), sorted.end());
1478     unsigned int len = sorted.size();
1479     //overall bboxes span
1480     float dist = (sorted.back()._coord -
1481                   sorted.front()._coord);
1482     //new distance between each bbox
1483     float step = (dist) / (len - 1);
1484     float pos = sorted.front()._coord;
1485     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1486           it < sorted.end();
1487           it ++ )
1488     {
1489         NR::Point dest((*it)._node->pos);
1490         dest[axis] = pos;
1491         sp_node_moveto((*it)._node, dest);
1492         pos += step;
1493     }
1495     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1499 /**
1500  * Call sp_nodepath_line_add_node() for all selected segments.
1501  */
1502 void
1503 sp_node_selected_add_node(void)
1505     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1506     if (!nodepath) {
1507         return;
1508     }
1510     GList *nl = NULL;
1512     int n_added = 0;
1514     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1515        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1516         g_assert(t->selected);
1517         if (t->p.other && t->p.other->selected) {
1518             nl = g_list_prepend(nl, t);
1519         }
1520     }
1522     while (nl) {
1523        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1524        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1525        sp_nodepath_node_select(n, TRUE, FALSE);
1526        n_added ++;
1527        nl = g_list_remove(nl, t);
1528     }
1530     /** \todo fixme: adjust ? */
1531     sp_nodepath_update_handles(nodepath);
1533     if (n_added > 1) {
1534         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1535     } else if (n_added > 0) {
1536         sp_nodepath_update_repr(nodepath, _("Add node"));
1537     }
1539     sp_nodepath_update_statusbar(nodepath);
1542 /**
1543  * Select segment nearest to point
1544  */
1545 void
1546 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1548     if (!nodepath) {
1549         return;
1550     }
1552     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1554     //find segment to segment
1555     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1557     //fixme: this can return NULL, so check before proceeding.
1558     g_return_if_fail(e != NULL);
1559     
1560     gboolean force = FALSE;
1561     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1562         force = TRUE;
1563     }
1564     sp_nodepath_node_select(e, (gboolean) toggle, force);
1565     if (e->p.other)
1566         sp_nodepath_node_select(e->p.other, TRUE, force);
1568     sp_nodepath_update_handles(nodepath);
1570     sp_nodepath_update_statusbar(nodepath);
1573 /**
1574  * Add a node nearest to point
1575  */
1576 void
1577 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1579     if (!nodepath) {
1580         return;
1581     }
1583     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1585     //find segment to split
1586     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1588     //don't know why but t seems to flip for lines
1589     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1590         position.t = 1.0 - position.t;
1591     }
1592     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1593     sp_nodepath_node_select(n, FALSE, TRUE);
1595     /* fixme: adjust ? */
1596     sp_nodepath_update_handles(nodepath);
1598     sp_nodepath_update_repr(nodepath, _("Add node"));
1600     sp_nodepath_update_statusbar(nodepath);
1603 /*
1604  * Adjusts a segment so that t moves by a certain delta for dragging
1605  * converts lines to curves
1606  *
1607  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1608  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1609  */
1610 void
1611 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1613     //fixme: e and e->p can be NULL, so check for those before proceeding
1614     g_return_if_fail(e != NULL);
1615     g_return_if_fail(&e->p != NULL);
1616     
1617     /* feel good is an arbitrary parameter that distributes the delta between handles
1618      * if t of the drag point is less than 1/6 distance form the endpoint only
1619      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1620      */
1621     double feel_good;
1622     if (t <= 1.0 / 6.0)
1623         feel_good = 0;
1624     else if (t <= 0.5)
1625         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1626     else if (t <= 5.0 / 6.0)
1627         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1628     else
1629         feel_good = 1;
1631     //if we're dragging a line convert it to a curve
1632     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1633         sp_nodepath_set_line_type(e, NR_CURVETO);
1634     }
1636     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1637     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1638     e->p.other->n.pos += offsetcoord0;
1639     e->p.pos += offsetcoord1;
1641     // adjust handles of adjacent nodes where necessary
1642     sp_node_adjust_handle(e,1);
1643     sp_node_adjust_handle(e->p.other,-1);
1645     sp_nodepath_update_handles(e->subpath->nodepath);
1647     update_object(e->subpath->nodepath);
1649     sp_nodepath_update_statusbar(e->subpath->nodepath);
1653 /**
1654  * Call sp_nodepath_break() for all selected segments.
1655  */
1656 void sp_node_selected_break()
1658     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1659     if (!nodepath) return;
1661     GList *temp = NULL;
1662     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1663        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1664        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1665         if (nn == NULL) continue; // no break, no new node
1666         temp = g_list_prepend(temp, nn);
1667     }
1669     if (temp) {
1670         sp_nodepath_deselect(nodepath);
1671     }
1672     for (GList *l = temp; l != NULL; l = l->next) {
1673         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1674     }
1676     sp_nodepath_update_handles(nodepath);
1678     sp_nodepath_update_repr(nodepath, _("Break path"));
1681 /**
1682  * Duplicate the selected node(s).
1683  */
1684 void sp_node_selected_duplicate()
1686     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1687     if (!nodepath) {
1688         return;
1689     }
1691     GList *temp = NULL;
1692     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1693        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1694        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1695         if (nn == NULL) continue; // could not duplicate
1696         temp = g_list_prepend(temp, nn);
1697     }
1699     if (temp) {
1700         sp_nodepath_deselect(nodepath);
1701     }
1702     for (GList *l = temp; l != NULL; l = l->next) {
1703         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1704     }
1706     sp_nodepath_update_handles(nodepath);
1708     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1711 /**
1712  *  Join two nodes by merging them into one.
1713  */
1714 void sp_node_selected_join()
1716     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1717     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1719     if (g_list_length(nodepath->selected) != 2) {
1720         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1721         return;
1722     }
1724    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1725    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1727     g_assert(a != b);
1728     g_assert(a->p.other || a->n.other);
1729     g_assert(b->p.other || b->n.other);
1731     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1732         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1733         return;
1734     }
1736     /* a and b are endpoints */
1738     NR::Point c;
1739     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1740         c = a->pos;
1741     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1742         c = b->pos;
1743     } else {
1744         c = (a->pos + b->pos) / 2;
1745     }
1747     if (a->subpath == b->subpath) {
1748        Inkscape::NodePath::SubPath *sp = a->subpath;
1749         sp_nodepath_subpath_close(sp);
1750         sp_node_moveto (sp->first, c);
1752         sp_nodepath_update_handles(sp->nodepath);
1753         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1754         return;
1755     }
1757     /* a and b are separate subpaths */
1758    Inkscape::NodePath::SubPath *sa = a->subpath;
1759    Inkscape::NodePath::SubPath *sb = b->subpath;
1760     NR::Point p;
1761    Inkscape::NodePath::Node *n;
1762     NRPathcode code;
1763     if (a == sa->first) {
1764         p = sa->first->n.pos;
1765         code = (NRPathcode)sa->first->n.other->code;
1766        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1767         n = sa->last;
1768         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1769         n = n->p.other;
1770         while (n) {
1771             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1772             n = n->p.other;
1773             if (n == sa->first) n = NULL;
1774         }
1775         sp_nodepath_subpath_destroy(sa);
1776         sa = t;
1777     } else if (a == sa->last) {
1778         p = sa->last->p.pos;
1779         code = (NRPathcode)sa->last->code;
1780         sp_nodepath_node_destroy(sa->last);
1781     } else {
1782         code = NR_END;
1783         g_assert_not_reached();
1784     }
1786     if (b == sb->first) {
1787         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1788         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1789             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1790         }
1791     } else if (b == sb->last) {
1792         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1793         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1794             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1795         }
1796     } else {
1797         g_assert_not_reached();
1798     }
1799     /* and now destroy sb */
1801     sp_nodepath_subpath_destroy(sb);
1803     sp_nodepath_update_handles(sa->nodepath);
1805     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1807     sp_nodepath_update_statusbar(nodepath);
1810 /**
1811  *  Join two nodes by adding a segment between them.
1812  */
1813 void sp_node_selected_join_segment()
1815     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1816     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1818     if (g_list_length(nodepath->selected) != 2) {
1819         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1820         return;
1821     }
1823    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1824    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1826     g_assert(a != b);
1827     g_assert(a->p.other || a->n.other);
1828     g_assert(b->p.other || b->n.other);
1830     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1831         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1832         return;
1833     }
1835     if (a->subpath == b->subpath) {
1836        Inkscape::NodePath::SubPath *sp = a->subpath;
1838         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1839         sp->closed = TRUE;
1841         sp->first->p.other = sp->last;
1842         sp->last->n.other  = sp->first;
1844         sp_node_handle_mirror_p_to_n(sp->last);
1845         sp_node_handle_mirror_n_to_p(sp->first);
1847         sp->first->code = sp->last->code;
1848         sp->first       = sp->last;
1850         sp_nodepath_update_handles(sp->nodepath);
1852         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1854         return;
1855     }
1857     /* a and b are separate subpaths */
1858    Inkscape::NodePath::SubPath *sa = a->subpath;
1859    Inkscape::NodePath::SubPath *sb = b->subpath;
1861    Inkscape::NodePath::Node *n;
1862     NR::Point p;
1863     NRPathcode code;
1864     if (a == sa->first) {
1865         code = (NRPathcode) sa->first->n.other->code;
1866        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1867         n = sa->last;
1868         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1869         for (n = n->p.other; n != NULL; n = n->p.other) {
1870             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1871         }
1872         sp_nodepath_subpath_destroy(sa);
1873         sa = t;
1874     } else if (a == sa->last) {
1875         code = (NRPathcode)sa->last->code;
1876     } else {
1877         code = NR_END;
1878         g_assert_not_reached();
1879     }
1881     if (b == sb->first) {
1882         n = sb->first;
1883         sp_node_handle_mirror_p_to_n(sa->last);
1884         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1885         sp_node_handle_mirror_n_to_p(sa->last);
1886         for (n = n->n.other; n != NULL; n = n->n.other) {
1887             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1888         }
1889     } else if (b == sb->last) {
1890         n = sb->last;
1891         sp_node_handle_mirror_p_to_n(sa->last);
1892         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1893         sp_node_handle_mirror_n_to_p(sa->last);
1894         for (n = n->p.other; n != NULL; n = n->p.other) {
1895             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1896         }
1897     } else {
1898         g_assert_not_reached();
1899     }
1900     /* and now destroy sb */
1902     sp_nodepath_subpath_destroy(sb);
1904     sp_nodepath_update_handles(sa->nodepath);
1906     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1909 /**
1910  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1911  */
1912 void sp_node_delete_preserve(GList *nodes_to_delete)
1914     GSList *nodepaths = NULL;
1915     
1916     while (nodes_to_delete) {
1917         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1918         Inkscape::NodePath::SubPath *sp = node->subpath;
1919         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1920         Inkscape::NodePath::Node *sample_cursor = NULL;
1921         Inkscape::NodePath::Node *sample_end = NULL;
1922         Inkscape::NodePath::Node *delete_cursor = node;
1923         bool just_delete = false;
1924         
1925         //find the start of this contiguous selection
1926         //move left to the first node that is not selected
1927         //or the start of the non-closed path
1928         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1929             delete_cursor = curr;
1930         }
1932         //just delete at the beginning of an open path
1933         if (!delete_cursor->p.other) {
1934             sample_cursor = delete_cursor;
1935             just_delete = true;
1936         } else {
1937             sample_cursor = delete_cursor->p.other;
1938         }
1939         
1940         //calculate points for each segment
1941         int rate = 5;
1942         float period = 1.0 / rate;
1943         std::vector<NR::Point> data;
1944         if (!just_delete) {
1945             data.push_back(sample_cursor->pos);
1946             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1947                 //just delete at the end of an open path
1948                 if (!sp->closed && curr == sp->last) {
1949                     just_delete = true;
1950                     break;
1951                 }
1952                 
1953                 //sample points on the contiguous selected segment
1954                 NR::Point *bez;
1955                 bez = new NR::Point [4];
1956                 bez[0] = curr->pos;
1957                 bez[1] = curr->n.pos;
1958                 bez[2] = curr->n.other->p.pos;
1959                 bez[3] = curr->n.other->pos;
1960                 for (int i=1; i<rate; i++) {
1961                     gdouble t = i * period;
1962                     NR::Point p = bezier_pt(3, bez, t);
1963                     data.push_back(p);
1964                 }
1965                 data.push_back(curr->n.other->pos);
1967                 sample_end = curr->n.other;
1968                 //break if we've come full circle or hit the end of the selection
1969                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1970                     break;
1971                 }
1972             }
1973         }
1975         if (!just_delete) {
1976             //calculate the best fitting single segment and adjust the endpoints
1977             NR::Point *adata;
1978             adata = new NR::Point [data.size()];
1979             copy(data.begin(), data.end(), adata);
1980             
1981             NR::Point *bez;
1982             bez = new NR::Point [4];
1983             //would decreasing error create a better fitting approximation?
1984             gdouble error = 1.0;
1985             gint ret;
1986             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1988             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
1989             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
1990             //the resulting nodes behave as expected.
1991             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
1992             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
1993             
1994             //adjust endpoints
1995             sample_cursor->n.pos = bez[1];
1996             sample_end->p.pos = bez[2];
1997         }
1998        
1999         //destroy this contiguous selection
2000         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2001             Inkscape::NodePath::Node *temp = delete_cursor;
2002             if (delete_cursor->n.other == delete_cursor) {
2003                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2004                 delete_cursor = NULL; 
2005             } else {
2006                 delete_cursor = delete_cursor->n.other;
2007             }
2008             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2009             sp_nodepath_node_destroy(temp);
2010         }
2012         sp_nodepath_update_handles(nodepath);
2014         if (!g_slist_find(nodepaths, nodepath))
2015             nodepaths = g_slist_prepend (nodepaths, nodepath);
2016     }
2018     for (GSList *i = nodepaths; i; i = i->next) {
2019         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2020         // different nodepaths will give us one undo event per nodepath
2021         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2023         // if the entire nodepath is removed, delete the selected object.
2024         if (nodepath->subpaths == NULL ||
2025             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2026             //at least 2
2027             sp_nodepath_get_node_count(nodepath) < 2) {
2028             SPDocument *document = sp_desktop_document (nodepath->desktop);
2029             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2030             //delete this nodepath's object, not the entire selection! (though at this time, this
2031             //does not matter)
2032             sp_selection_delete();
2033             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2034                               _("Delete nodes"));
2035         } else {
2036             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2037             sp_nodepath_update_statusbar(nodepath);
2038         }
2039     }
2041     g_slist_free (nodepaths);
2044 /**
2045  * Delete one or more selected nodes.
2046  */
2047 void sp_node_selected_delete()
2049     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2050     if (!nodepath) return;
2051     if (!nodepath->selected) return;
2053     /** \todo fixme: do it the right way */
2054     while (nodepath->selected) {
2055        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2056         sp_nodepath_node_destroy(node);
2057     }
2060     //clean up the nodepath (such as for trivial subpaths)
2061     sp_nodepath_cleanup(nodepath);
2063     sp_nodepath_update_handles(nodepath);
2065     // if the entire nodepath is removed, delete the selected object.
2066     if (nodepath->subpaths == NULL ||
2067         sp_nodepath_get_node_count(nodepath) < 2) {
2068         SPDocument *document = sp_desktop_document (nodepath->desktop);
2069         sp_selection_delete();
2070         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2071                           _("Delete nodes"));
2072         return;
2073     }
2075     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2077     sp_nodepath_update_statusbar(nodepath);
2080 /**
2081  * Delete one or more segments between two selected nodes.
2082  * This is the code for 'split'.
2083  */
2084 void
2085 sp_node_selected_delete_segment(void)
2087    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2088    Inkscape::NodePath::Node *curr, *next;     //Iterators
2090     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2091     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2093     if (g_list_length(nodepath->selected) != 2) {
2094         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2095                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2096         return;
2097     }
2099     //Selected nodes, not inclusive
2100    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2101    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2103     if ( ( a==b)                       ||  //same node
2104          (a->subpath  != b->subpath )  ||  //not the same path
2105          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2106          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2107     {
2108         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2109                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2110         return;
2111     }
2113     //###########################################
2114     //# BEGIN EDITS
2115     //###########################################
2116     //##################################
2117     //# CLOSED PATH
2118     //##################################
2119     if (a->subpath->closed) {
2122         gboolean reversed = FALSE;
2124         //Since we can go in a circle, we need to find the shorter distance.
2125         //  a->b or b->a
2126         start = end = NULL;
2127         int distance    = 0;
2128         int minDistance = 0;
2129         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2130             if (curr==b) {
2131                 //printf("a to b:%d\n", distance);
2132                 start = a;//go from a to b
2133                 end   = b;
2134                 minDistance = distance;
2135                 //printf("A to B :\n");
2136                 break;
2137             }
2138             distance++;
2139         }
2141         //try again, the other direction
2142         distance = 0;
2143         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2144             if (curr==a) {
2145                 //printf("b to a:%d\n", distance);
2146                 if (distance < minDistance) {
2147                     start    = b;  //we go from b to a
2148                     end      = a;
2149                     reversed = TRUE;
2150                     //printf("B to A\n");
2151                 }
2152                 break;
2153             }
2154             distance++;
2155         }
2158         //Copy everything from 'end' to 'start' to a new subpath
2159        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2160         for (curr=end ; curr ; curr=curr->n.other) {
2161             NRPathcode code = (NRPathcode) curr->code;
2162             if (curr == end)
2163                 code = NR_MOVETO;
2164             sp_nodepath_node_new(t, NULL,
2165                                  (Inkscape::NodePath::NodeType)curr->type, code,
2166                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2167             if (curr == start)
2168                 break;
2169         }
2170         sp_nodepath_subpath_destroy(a->subpath);
2173     }
2177     //##################################
2178     //# OPEN PATH
2179     //##################################
2180     else {
2182         //We need to get the direction of the list between A and B
2183         //Can we walk from a to b?
2184         start = end = NULL;
2185         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2186             if (curr==b) {
2187                 start = a;  //did it!  we go from a to b
2188                 end   = b;
2189                 //printf("A to B\n");
2190                 break;
2191             }
2192         }
2193         if (!start) {//didn't work?  let's try the other direction
2194             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2195                 if (curr==a) {
2196                     start = b;  //did it!  we go from b to a
2197                     end   = a;
2198                     //printf("B to A\n");
2199                     break;
2200                 }
2201             }
2202         }
2203         if (!start) {
2204             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2205                                                      _("Cannot find path between nodes."));
2206             return;
2207         }
2211         //Copy everything after 'end' to a new subpath
2212        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2213         for (curr=end ; curr ; curr=curr->n.other) {
2214             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2215                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2216         }
2218         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2219         for (curr = start->n.other ; curr  ; curr=next) {
2220             next = curr->n.other;
2221             sp_nodepath_node_destroy(curr);
2222         }
2224     }
2225     //###########################################
2226     //# END EDITS
2227     //###########################################
2229     //clean up the nodepath (such as for trivial subpaths)
2230     sp_nodepath_cleanup(nodepath);
2232     sp_nodepath_update_handles(nodepath);
2234     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2236     sp_nodepath_update_statusbar(nodepath);
2239 /**
2240  * Call sp_nodepath_set_line() for all selected segments.
2241  */
2242 void
2243 sp_node_selected_set_line_type(NRPathcode code)
2245     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2246     if (nodepath == NULL) return;
2248     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2249        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2250         g_assert(n->selected);
2251         if (n->p.other && n->p.other->selected) {
2252             sp_nodepath_set_line_type(n, code);
2253         }
2254     }
2256     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2259 /**
2260  * Call sp_nodepath_convert_node_type() for all selected nodes.
2261  */
2262 void
2263 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
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         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2270     }
2272     sp_nodepath_update_repr(nodepath, _("Change node type"));
2275 /**
2276  * Change select status of node, update its own and neighbour handles.
2277  */
2278 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2280     node->selected = selected;
2282     if (selected) {
2283         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2284         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2285         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2286         sp_knot_update_ctrl(node->knot);
2287     } else {
2288         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2289         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2290         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2291         sp_knot_update_ctrl(node->knot);
2292     }
2294     sp_node_update_handles(node);
2295     if (node->n.other) sp_node_update_handles(node->n.other);
2296     if (node->p.other) sp_node_update_handles(node->p.other);
2299 /**
2300 \brief Select a node
2301 \param node     The node to select
2302 \param incremental   If true, add to selection, otherwise deselect others
2303 \param override   If true, always select this node, otherwise toggle selected status
2304 */
2305 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2307     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2309     if (incremental) {
2310         if (override) {
2311             if (!g_list_find(nodepath->selected, node)) {
2312                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2313             }
2314             sp_node_set_selected(node, TRUE);
2315         } else { // toggle
2316             if (node->selected) {
2317                 g_assert(g_list_find(nodepath->selected, node));
2318                 nodepath->selected = g_list_remove(nodepath->selected, node);
2319             } else {
2320                 g_assert(!g_list_find(nodepath->selected, node));
2321                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2322             }
2323             sp_node_set_selected(node, !node->selected);
2324         }
2325     } else {
2326         sp_nodepath_deselect(nodepath);
2327         nodepath->selected = g_list_prepend(nodepath->selected, node);
2328         sp_node_set_selected(node, TRUE);
2329     }
2331     sp_nodepath_update_statusbar(nodepath);
2335 /**
2336 \brief Deselect all nodes in the nodepath
2337 */
2338 void
2339 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2341     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2343     while (nodepath->selected) {
2344         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2345         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2346     }
2347     sp_nodepath_update_statusbar(nodepath);
2350 /**
2351 \brief Select or invert selection of all nodes in the nodepath
2352 */
2353 void
2354 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2356     if (!nodepath) return;
2358     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2359        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2360         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2361            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2362            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2363         }
2364     }
2367 /**
2368  * If nothing selected, does the same as sp_nodepath_select_all();
2369  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2370  * (i.e., similar to "select all in layer", with the "selected" subpaths
2371  * being treated as "layers" in the path).
2372  */
2373 void
2374 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2376     if (!nodepath) return;
2378     if (g_list_length (nodepath->selected) == 0) {
2379         sp_nodepath_select_all (nodepath, invert);
2380         return;
2381     }
2383     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2384     GSList *subpaths = NULL;
2386     for (GList *l = copy; l != NULL; l = l->next) {
2387         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2388         Inkscape::NodePath::SubPath *subpath = n->subpath;
2389         if (!g_slist_find (subpaths, subpath))
2390             subpaths = g_slist_prepend (subpaths, subpath);
2391     }
2393     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2394         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2395         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2396             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2397             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2398         }
2399     }
2401     g_slist_free (subpaths);
2402     g_list_free (copy);
2405 /**
2406  * \brief Select the node after the last selected; if none is selected,
2407  * select the first within path.
2408  */
2409 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2411     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2413    Inkscape::NodePath::Node *last = NULL;
2414     if (nodepath->selected) {
2415         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2416            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2417             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2418             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2419                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2420                 if (node->selected) {
2421                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2422                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2423                             if (spl->next) { // there's a next subpath
2424                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2425                                 last = subpath_next->first;
2426                             } else if (spl->prev) { // there's a previous subpath
2427                                 last = NULL; // to be set later to the first node of first subpath
2428                             } else {
2429                                 last = node->n.other;
2430                             }
2431                         } else {
2432                             last = node->n.other;
2433                         }
2434                     } else {
2435                         if (node->n.other) {
2436                             last = node->n.other;
2437                         } else {
2438                             if (spl->next) { // there's a next subpath
2439                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2440                                 last = subpath_next->first;
2441                             } else if (spl->prev) { // there's a previous subpath
2442                                 last = NULL; // to be set later to the first node of first subpath
2443                             } else {
2444                                 last = (Inkscape::NodePath::Node *) subpath->first;
2445                             }
2446                         }
2447                     }
2448                 }
2449             }
2450         }
2451         sp_nodepath_deselect(nodepath);
2452     }
2454     if (last) { // there's at least one more node after selected
2455         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2456     } else { // no more nodes, select the first one in first subpath
2457        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2458         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2459     }
2462 /**
2463  * \brief Select the node before the first selected; if none is selected,
2464  * select the last within path
2465  */
2466 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2468     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2470    Inkscape::NodePath::Node *last = NULL;
2471     if (nodepath->selected) {
2472         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2473            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2474             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2475                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2476                 if (node->selected) {
2477                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2478                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2479                             if (spl->prev) { // there's a prev subpath
2480                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2481                                 last = subpath_prev->last;
2482                             } else if (spl->next) { // there's a next subpath
2483                                 last = NULL; // to be set later to the last node of last subpath
2484                             } else {
2485                                 last = node->p.other;
2486                             }
2487                         } else {
2488                             last = node->p.other;
2489                         }
2490                     } else {
2491                         if (node->p.other) {
2492                             last = node->p.other;
2493                         } else {
2494                             if (spl->prev) { // there's a prev subpath
2495                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2496                                 last = subpath_prev->last;
2497                             } else if (spl->next) { // there's a next subpath
2498                                 last = NULL; // to be set later to the last node of last subpath
2499                             } else {
2500                                 last = (Inkscape::NodePath::Node *) subpath->last;
2501                             }
2502                         }
2503                     }
2504                 }
2505             }
2506         }
2507         sp_nodepath_deselect(nodepath);
2508     }
2510     if (last) { // there's at least one more node before selected
2511         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2512     } else { // no more nodes, select the last one in last subpath
2513         GList *spl = g_list_last(nodepath->subpaths);
2514        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2515         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2516     }
2519 /**
2520  * \brief Select all nodes that are within the rectangle.
2521  */
2522 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2524     if (!incremental) {
2525         sp_nodepath_deselect(nodepath);
2526     }
2528     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2529        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2530         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2531            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2533             if (b.contains(node->pos)) {
2534                 sp_nodepath_node_select(node, TRUE, TRUE);
2535             }
2536         }
2537     }
2541 void
2542 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2544     g_assert (n);
2545     g_assert (nodepath);
2546     g_assert (n->subpath->nodepath == nodepath);
2548     if (g_list_length (nodepath->selected) == 0) {
2549         if (grow > 0) {
2550             sp_nodepath_node_select(n, TRUE, TRUE);
2551         }
2552         return;
2553     }
2555     if (g_list_length (nodepath->selected) == 1) {
2556         if (grow < 0) {
2557             sp_nodepath_deselect (nodepath);
2558             return;
2559         }
2560     }
2562         double n_sel_range = 0, p_sel_range = 0;
2563             Inkscape::NodePath::Node *farthest_n_node = n;
2564             Inkscape::NodePath::Node *farthest_p_node = n;
2566         // Calculate ranges
2567         {
2568             double n_range = 0, p_range = 0;
2569             bool n_going = true, p_going = true;
2570             Inkscape::NodePath::Node *n_node = n;
2571             Inkscape::NodePath::Node *p_node = n;
2572             do {
2573                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2574                 if (n_node && n_going)
2575                     n_node = n_node->n.other;
2576                 if (n_node == NULL) {
2577                     n_going = false;
2578                 } else {
2579                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2580                     if (n_node->selected) {
2581                         n_sel_range = n_range;
2582                         farthest_n_node = n_node;
2583                     }
2584                     if (n_node == p_node) {
2585                         n_going = false;
2586                         p_going = false;
2587                     }
2588                 }
2589                 if (p_node && p_going)
2590                     p_node = p_node->p.other;
2591                 if (p_node == NULL) {
2592                     p_going = false;
2593                 } else {
2594                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2595                     if (p_node->selected) {
2596                         p_sel_range = p_range;
2597                         farthest_p_node = p_node;
2598                     }
2599                     if (p_node == n_node) {
2600                         n_going = false;
2601                         p_going = false;
2602                     }
2603                 }
2604             } while (n_going || p_going);
2605         }
2607     if (grow > 0) {
2608         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2609                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2610         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2611                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2612         }
2613     } else {
2614         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2615                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2616         } else if (farthest_p_node && farthest_p_node->selected) {
2617                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2618         }
2619     }
2622 void
2623 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2625     g_assert (n);
2626     g_assert (nodepath);
2627     g_assert (n->subpath->nodepath == nodepath);
2629     if (g_list_length (nodepath->selected) == 0) {
2630         if (grow > 0) {
2631             sp_nodepath_node_select(n, TRUE, TRUE);
2632         }
2633         return;
2634     }
2636     if (g_list_length (nodepath->selected) == 1) {
2637         if (grow < 0) {
2638             sp_nodepath_deselect (nodepath);
2639             return;
2640         }
2641     }
2643     Inkscape::NodePath::Node *farthest_selected = NULL;
2644     double farthest_dist = 0;
2646     Inkscape::NodePath::Node *closest_unselected = NULL;
2647     double closest_dist = NR_HUGE;
2649     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2650        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2651         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2652            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2653            if (node == n)
2654                continue;
2655            if (node->selected) {
2656                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2657                    farthest_dist = NR::L2(node->pos - n->pos);
2658                    farthest_selected = node;
2659                }
2660            } else {
2661                if (NR::L2(node->pos - n->pos) < closest_dist) {
2662                    closest_dist = NR::L2(node->pos - n->pos);
2663                    closest_unselected = node;
2664                }
2665            }
2666         }
2667     }
2669     if (grow > 0) {
2670         if (closest_unselected) {
2671             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2672         }
2673     } else {
2674         if (farthest_selected) {
2675             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2676         }
2677     }
2681 /**
2682 \brief  Saves all nodes' and handles' current positions in their origin members
2683 */
2684 void
2685 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2687     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2688        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2689         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2690            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2691            n->origin = n->pos;
2692            n->p.origin = n->p.pos;
2693            n->n.origin = n->n.pos;
2694         }
2695     }
2696
2698 /**
2699 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2700 */
2701 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2703     if (!nodepath->selected) {
2704         return NULL;
2705     }
2707     GList *r = NULL;
2708     guint i = 0;
2709     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2710        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2711         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2712            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2713             i++;
2714             if (node->selected) {
2715                 r = g_list_append(r, GINT_TO_POINTER(i));
2716             }
2717         }
2718     }
2719     return r;
2722 /**
2723 \brief  Restores selection by selecting nodes whose positions are in the list
2724 */
2725 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2727     sp_nodepath_deselect(nodepath);
2729     guint i = 0;
2730     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2731        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2732         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2733            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2734             i++;
2735             if (g_list_find(r, GINT_TO_POINTER(i))) {
2736                 sp_nodepath_node_select(node, TRUE, TRUE);
2737             }
2738         }
2739     }
2743 /**
2744 \brief Adjusts handle according to node type and line code.
2745 */
2746 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2748     double len, otherlen, linelen;
2750     g_assert(node);
2752    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2753    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2755     /** \todo fixme: */
2756     if (me->other == NULL) return;
2757     if (other->other == NULL) return;
2759     /* I have line */
2761     NRPathcode mecode, ocode;
2762     if (which_adjust == 1) {
2763         mecode = (NRPathcode)me->other->code;
2764         ocode = (NRPathcode)node->code;
2765     } else {
2766         mecode = (NRPathcode)node->code;
2767         ocode = (NRPathcode)other->other->code;
2768     }
2770     if (mecode == NR_LINETO) return;
2772     /* I am curve */
2774     if (other->other == NULL) return;
2776     /* Other has line */
2778     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2780     NR::Point delta;
2781     if (ocode == NR_LINETO) {
2782         /* other is lineto, we are either smooth or symm */
2783        Inkscape::NodePath::Node *othernode = other->other;
2784         len = NR::L2(me->pos - node->pos);
2785         delta = node->pos - othernode->pos;
2786         linelen = NR::L2(delta);
2787         if (linelen < 1e-18) 
2788             return;
2789         me->pos = node->pos + (len / linelen)*delta;
2790         return;
2791     }
2793     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2795         me->pos = 2 * node->pos - other->pos;
2796         return;
2797     }
2799     /* We are smooth */
2801     len = NR::L2(me->pos - node->pos);
2802     delta = other->pos - node->pos;
2803     otherlen = NR::L2(delta);
2804     if (otherlen < 1e-18) return;
2806     me->pos = node->pos - (len / otherlen) * delta;
2809 /**
2810  \brief Adjusts both handles according to node type and line code
2811  */
2812 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2814     g_assert(node);
2816     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2818     /* we are either smooth or symm */
2820     if (node->p.other == NULL) return;
2822     if (node->n.other == NULL) return;
2824     if (node->code == NR_LINETO) {
2825         if (node->n.other->code == NR_LINETO) return;
2826         sp_node_adjust_handle(node, 1);
2827         return;
2828     }
2830     if (node->n.other->code == NR_LINETO) {
2831         if (node->code == NR_LINETO) return;
2832         sp_node_adjust_handle(node, -1);
2833         return;
2834     }
2836     /* both are curves */
2837     NR::Point const delta( node->n.pos - node->p.pos );
2839     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2840         node->p.pos = node->pos - delta / 2;
2841         node->n.pos = node->pos + delta / 2;
2842         return;
2843     }
2845     /* We are smooth */
2846     double plen = NR::L2(node->p.pos - node->pos);
2847     if (plen < 1e-18) return;
2848     double nlen = NR::L2(node->n.pos - node->pos);
2849     if (nlen < 1e-18) return;
2850     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2851     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2854 /**
2855  * Node event callback.
2856  */
2857 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2859     gboolean ret = FALSE;
2860     switch (event->type) {
2861         case GDK_ENTER_NOTIFY:
2862             active_node = n;
2863             break;
2864         case GDK_LEAVE_NOTIFY:
2865             active_node = NULL;
2866             break;
2867         case GDK_KEY_PRESS:
2868             switch (get_group0_keyval (&event->key)) {
2869                 case GDK_space:
2870                     if (event->key.state & GDK_BUTTON1_MASK) {
2871                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2872                         stamp_repr(nodepath);
2873                         ret = TRUE;
2874                     }
2875                     break;
2876                 case GDK_Page_Up:
2877                     if (event->key.state & GDK_CONTROL_MASK) {
2878                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2879                     } else {
2880                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2881                     }
2882                     break;
2883                 case GDK_Page_Down:
2884                     if (event->key.state & GDK_CONTROL_MASK) {
2885                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2886                     } else {
2887                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2888                     }
2889                     break;
2890                 default:
2891                     break;
2892             }
2893             break;
2894         default:
2895             break;
2896     }
2898     return ret;
2901 /**
2902  * Handle keypress on node; directly called.
2903  */
2904 gboolean node_key(GdkEvent *event)
2906     Inkscape::NodePath::Path *np;
2908     // there is no way to verify nodes so set active_node to nil when deleting!!
2909     if (active_node == NULL) return FALSE;
2911     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2912         gint ret = FALSE;
2913         switch (get_group0_keyval (&event->key)) {
2914             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2915             case GDK_BackSpace:
2916                 np = active_node->subpath->nodepath;
2917                 sp_nodepath_node_destroy(active_node);
2918                 sp_nodepath_update_repr(np, _("Delete node"));
2919                 active_node = NULL;
2920                 ret = TRUE;
2921                 break;
2922             case GDK_c:
2923                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2924                 ret = TRUE;
2925                 break;
2926             case GDK_s:
2927                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2928                 ret = TRUE;
2929                 break;
2930             case GDK_y:
2931                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2932                 ret = TRUE;
2933                 break;
2934             case GDK_b:
2935                 sp_nodepath_node_break(active_node);
2936                 ret = TRUE;
2937                 break;
2938         }
2939         return ret;
2940     }
2941     return FALSE;
2944 /**
2945  * Mouseclick on node callback.
2946  */
2947 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2949    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2951     if (state & GDK_CONTROL_MASK) {
2952         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2954         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2955             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2956                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2957             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2958                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2959             } else {
2960                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2961             }
2962             sp_nodepath_update_repr(nodepath, _("Change node type"));
2963             sp_nodepath_update_statusbar(nodepath);
2965         } else { //ctrl+alt+click: delete node
2966             GList *node_to_delete = NULL;
2967             node_to_delete = g_list_append(node_to_delete, n);
2968             sp_node_delete_preserve(node_to_delete);
2969         }
2971     } else {
2972         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2973     }
2976 /**
2977  * Mouse grabbed node callback.
2978  */
2979 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2981    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2983     if (!n->selected) {
2984         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2985     }
2987     sp_nodepath_remember_origins (n->subpath->nodepath);
2990 /**
2991  * Mouse ungrabbed node callback.
2992  */
2993 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2995    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2997    n->dragging_out = NULL;
2999    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3002 /**
3003  * The point on a line, given by its angle, closest to the given point.
3004  * \param p  A point.
3005  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3006  * \param closest  Pointer to the point struct where the result is stored.
3007  * \todo FIXME: use dot product perhaps?
3008  */
3009 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3011     if (a == HUGE_VAL) { // vertical
3012         *closest = NR::Point(0, (*p)[NR::Y]);
3013     } else {
3014         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3015         (*closest)[NR::Y] = a * (*closest)[NR::X];
3016     }
3019 /**
3020  * Distance from the point to a line given by its angle.
3021  * \param p  A point.
3022  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3023  */
3024 static double point_line_distance(NR::Point *p, double a)
3026     NR::Point c;
3027     point_line_closest(p, a, &c);
3028     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]));
3031 /**
3032  * Callback for node "request" signal.
3033  * \todo fixme: This goes to "moved" event? (lauris)
3034  */
3035 static gboolean
3036 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3038     double yn, xn, yp, xp;
3039     double an, ap, na, pa;
3040     double d_an, d_ap, d_na, d_pa;
3041     gboolean collinear = FALSE;
3042     NR::Point c;
3043     NR::Point pr;
3045    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3047    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3048    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3050        NR::Point mouse = (*p);
3052        if (!n->dragging_out) {
3053            // This is the first drag-out event; find out which handle to drag out
3054            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3055            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3057            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3058                return FALSE;
3060            Inkscape::NodePath::NodeSide *opposite;
3061            if (appr_p > appr_n) { // closer to p
3062                n->dragging_out = &n->p;
3063                opposite = &n->n;
3064                n->code = NR_CURVETO;
3065            } else if (appr_p < appr_n) { // closer to n
3066                n->dragging_out = &n->n;
3067                opposite = &n->p;
3068                n->n.other->code = NR_CURVETO;
3069            } else { // p and n nodes are the same
3070                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3071                    n->dragging_out = &n->p;
3072                    opposite = &n->n;
3073                    n->code = NR_CURVETO;
3074                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3075                    n->dragging_out = &n->n;
3076                    opposite = &n->p;
3077                    n->n.other->code = NR_CURVETO;
3078                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3079                    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);
3080                    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);
3081                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3082                        n->dragging_out = &n->n;
3083                        opposite = &n->p;
3084                        n->n.other->code = NR_CURVETO;
3085                    } else { // closer to other's n handle
3086                        n->dragging_out = &n->p;
3087                        opposite = &n->n;
3088                        n->code = NR_CURVETO;
3089                    }
3090                }
3091            }
3093            // if there's another handle, make sure the one we drag out starts parallel to it
3094            if (opposite->pos != n->pos) {
3095                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3096            }
3098            // knots might not be created yet!
3099            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3100            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3101        }
3103        // pass this on to the handle-moved callback
3104        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3105        sp_node_update_handles(n);
3106        return TRUE;
3107    }
3109     if (state & GDK_CONTROL_MASK) { // constrained motion
3111         // calculate relative distances of handles
3112         // n handle:
3113         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3114         xn = n->n.pos[NR::X] - n->pos[NR::X];
3115         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3116         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3117             if (n->n.other) { // if there is the next point
3118                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3119                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3120                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3121             }
3122         }
3123         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3124         if (yn < 0) { xn = -xn; yn = -yn; }
3126         // p handle:
3127         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3128         xp = n->p.pos[NR::X] - n->pos[NR::X];
3129         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3130         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3131             if (n->p.other) {
3132                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3133                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3134                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3135             }
3136         }
3137         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3138         if (yp < 0) { xp = -xp; yp = -yp; }
3140         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3141             // sliding on handles, only if at least one of the handles is non-vertical
3142             // (otherwise it's the same as ctrl+drag anyway)
3144             // calculate angles of the handles
3145             if (xn == 0) {
3146                 if (yn == 0) { // no handle, consider it the continuation of the other one
3147                     an = 0;
3148                     collinear = TRUE;
3149                 }
3150                 else an = 0; // vertical; set the angle to horizontal
3151             } else an = yn/xn;
3153             if (xp == 0) {
3154                 if (yp == 0) { // no handle, consider it the continuation of the other one
3155                     ap = an;
3156                 }
3157                 else ap = 0; // vertical; set the angle to horizontal
3158             } else  ap = yp/xp;
3160             if (collinear) an = ap;
3162             // angles of the perpendiculars; HUGE_VAL means vertical
3163             if (an == 0) na = HUGE_VAL; else na = -1/an;
3164             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3166             // mouse point relative to the node's original pos
3167             pr = (*p) - n->origin;
3169             // distances to the four lines (two handles and two perpendiculars)
3170             d_an = point_line_distance(&pr, an);
3171             d_na = point_line_distance(&pr, na);
3172             d_ap = point_line_distance(&pr, ap);
3173             d_pa = point_line_distance(&pr, pa);
3175             // find out which line is the closest, save its closest point in c
3176             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3177                 point_line_closest(&pr, an, &c);
3178             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3179                 point_line_closest(&pr, ap, &c);
3180             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3181                 point_line_closest(&pr, na, &c);
3182             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3183                 point_line_closest(&pr, pa, &c);
3184             }
3186             // move the node to the closest point
3187             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3188                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3189                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3191         } else {  // constraining to hor/vert
3193             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3194                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3195             } else { // snap to vert
3196                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3197             }
3198         }
3199     } else { // move freely
3200         if (state & GDK_MOD1_MASK) { // sculpt
3201             sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3202         } else {
3203             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3204                                         (*p)[NR::X] - n->pos[NR::X],
3205                                         (*p)[NR::Y] - n->pos[NR::Y],
3206                                         (state & GDK_SHIFT_MASK) == 0);
3207         }
3208     }
3210     n->subpath->nodepath->desktop->scroll_to_point(p);
3212     return TRUE;
3215 /**
3216  * Node handle clicked callback.
3217  */
3218 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3220    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3222     if (state & GDK_CONTROL_MASK) { // "delete" handle
3223         if (n->p.knot == knot) {
3224             n->p.pos = n->pos;
3225         } else if (n->n.knot == knot) {
3226             n->n.pos = n->pos;
3227         }
3228         sp_node_update_handles(n);
3229         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3230         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3231         sp_nodepath_update_statusbar(nodepath);
3233     } else { // just select or add to selection, depending in Shift
3234         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3235     }
3238 /**
3239  * Node handle grabbed callback.
3240  */
3241 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3243    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3245     if (!n->selected) {
3246         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3247     }
3249     // remember the origin point of the handle
3250     if (n->p.knot == knot) {
3251         n->p.origin_radial = n->p.pos - n->pos;
3252     } else if (n->n.knot == knot) {
3253         n->n.origin_radial = n->n.pos - n->pos;
3254     } else {
3255         g_assert_not_reached();
3256     }
3260 /**
3261  * Node handle ungrabbed callback.
3262  */
3263 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3265    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3267     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3268     if (n->p.knot == knot) {
3269         n->p.origin_radial.a = 0;
3270         sp_knot_set_position(knot, &n->p.pos, state);
3271     } else if (n->n.knot == knot) {
3272         n->n.origin_radial.a = 0;
3273         sp_knot_set_position(knot, &n->n.pos, state);
3274     } else {
3275         g_assert_not_reached();
3276     }
3278     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3281 /**
3282  * Node handle "request" signal callback.
3283  */
3284 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3286     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3288     Inkscape::NodePath::NodeSide *me, *opposite;
3289     gint which;
3290     if (n->p.knot == knot) {
3291         me = &n->p;
3292         opposite = &n->n;
3293         which = -1;
3294     } else if (n->n.knot == knot) {
3295         me = &n->n;
3296         opposite = &n->p;
3297         which = 1;
3298     } else {
3299         me = opposite = NULL;
3300         which = 0;
3301         g_assert_not_reached();
3302     }
3304     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3306     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3308     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3309         /* We are smooth node adjacent with line */
3310         NR::Point const delta = *p - n->pos;
3311         NR::Coord const len = NR::L2(delta);
3312         Inkscape::NodePath::Node *othernode = opposite->other;
3313         NR::Point const ndelta = n->pos - othernode->pos;
3314         NR::Coord const linelen = NR::L2(ndelta);
3315         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3316             NR::Coord const scal = dot(delta, ndelta) / linelen;
3317             (*p) = n->pos + (scal / linelen) * ndelta;
3318         }
3319         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3320     } else {
3321         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3322     }
3324     sp_node_adjust_handle(n, -which);
3326     return FALSE;
3329 /**
3330  * Node handle moved callback.
3331  */
3332 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3334    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3336    Inkscape::NodePath::NodeSide *me;
3337    Inkscape::NodePath::NodeSide *other;
3338     if (n->p.knot == knot) {
3339         me = &n->p;
3340         other = &n->n;
3341     } else if (n->n.knot == knot) {
3342         me = &n->n;
3343         other = &n->p;
3344     } else {
3345         me = NULL;
3346         other = NULL;
3347         g_assert_not_reached();
3348     }
3350     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3351     Radial rme(me->pos - n->pos);
3352     Radial rother(other->pos - n->pos);
3353     Radial rnew(*p - n->pos);
3355     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3356         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3357         /* 0 interpreted as "no snapping". */
3359         // The closest PI/snaps angle, starting from zero.
3360         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3361         if (me->origin_radial.a == HUGE_VAL) {
3362             // ortho doesn't exist: original handle was zero length.
3363             rnew.a = a_snapped;
3364         } else {
3365             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3366              * its opposite and perpendiculars). */
3367             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3369             // Snap to the closest.
3370             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3371                        ? a_snapped
3372                        : a_ortho );
3373         }
3374     }
3376     if (state & GDK_MOD1_MASK) {
3377         // lock handle length
3378         rnew.r = me->origin_radial.r;
3379     }
3381     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3382         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3383         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3384         rother.a += rnew.a - rme.a;
3385         other->pos = NR::Point(rother) + n->pos;
3386         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3387         sp_knot_set_position(other->knot, &other->pos, 0);
3388     }
3390     me->pos = NR::Point(rnew) + n->pos;
3391     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3393     // this is what sp_knot_set_position does, but without emitting the signal:
3394     // we cannot emit a "moved" signal because we're now processing it
3395     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3397     knot->desktop->set_coordinate_status(me->pos);
3399     update_object(n->subpath->nodepath);
3401     /* status text */
3402     SPDesktop *desktop = n->subpath->nodepath->desktop;
3403     if (!desktop) return;
3404     SPEventContext *ec = desktop->event_context;
3405     if (!ec) return;
3406     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3407     if (!mc) return;
3409     double degrees = 180 / M_PI * rnew.a;
3410     if (degrees > 180) degrees -= 360;
3411     if (degrees < -180) degrees += 360;
3412     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3413         degrees = angle_to_compass (degrees);
3415     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3417     mc->setF(Inkscape::NORMAL_MESSAGE,
3418          _("<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);
3420     g_string_free(length, TRUE);
3423 /**
3424  * Node handle event callback.
3425  */
3426 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3428     gboolean ret = FALSE;
3429     switch (event->type) {
3430         case GDK_KEY_PRESS:
3431             switch (get_group0_keyval (&event->key)) {
3432                 case GDK_space:
3433                     if (event->key.state & GDK_BUTTON1_MASK) {
3434                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3435                         stamp_repr(nodepath);
3436                         ret = TRUE;
3437                     }
3438                     break;
3439                 default:
3440                     break;
3441             }
3442             break;
3443         default:
3444             break;
3445     }
3447     return ret;
3450 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3451                                  Radial &rme, Radial &rother, gboolean const both)
3453     rme.a += angle;
3454     if ( both
3455          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3456          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3457     {
3458         rother.a += angle;
3459     }
3462 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3463                                         Radial &rme, Radial &rother, gboolean const both)
3465     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3467     gdouble r;
3468     if ( both
3469          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3470          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3471     {
3472         r = MAX(rme.r, rother.r);
3473     } else {
3474         r = rme.r;
3475     }
3477     gdouble const weird_angle = atan2(norm_angle, r);
3478 /* Bulia says norm_angle is just the visible distance that the
3479  * object's end must travel on the screen.  Left as 'angle' for want of
3480  * a better name.*/
3482     rme.a += weird_angle;
3483     if ( both
3484          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3485          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3486     {
3487         rother.a += weird_angle;
3488     }
3491 /**
3492  * Rotate one node.
3493  */
3494 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3496     Inkscape::NodePath::NodeSide *me, *other;
3497     bool both = false;
3499     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3500     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3502     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3503         me = &(n->p);
3504         other = &(n->n);
3505     } else if (!n->p.other) {
3506         me = &(n->n);
3507         other = &(n->p);
3508     } else {
3509         if (which > 0) { // right handle
3510             if (xn > xp) {
3511                 me = &(n->n);
3512                 other = &(n->p);
3513             } else {
3514                 me = &(n->p);
3515                 other = &(n->n);
3516             }
3517         } else if (which < 0){ // left handle
3518             if (xn <= xp) {
3519                 me = &(n->n);
3520                 other = &(n->p);
3521             } else {
3522                 me = &(n->p);
3523                 other = &(n->n);
3524             }
3525         } else { // both handles
3526             me = &(n->n);
3527             other = &(n->p);
3528             both = true;
3529         }
3530     }
3532     Radial rme(me->pos - n->pos);
3533     Radial rother(other->pos - n->pos);
3535     if (screen) {
3536         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3537     } else {
3538         node_rotate_one_internal (*n, angle, rme, rother, both);
3539     }
3541     me->pos = n->pos + NR::Point(rme);
3543     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3544         other->pos =  n->pos + NR::Point(rother);
3545     }
3547     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3548     // so here we just move all the knots without emitting move signals, for speed
3549     sp_node_update_handles(n, false);
3552 /**
3553  * Rotate selected nodes.
3554  */
3555 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3557     if (!nodepath || !nodepath->selected) return;
3559     if (g_list_length(nodepath->selected) == 1) {
3560        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3561         node_rotate_one (n, angle, which, screen);
3562     } else {
3563        // rotate as an object:
3565         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3566         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3567         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3568             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3569             box.expandTo (n->pos); // contain all selected nodes
3570         }
3572         gdouble rot;
3573         if (screen) {
3574             gdouble const zoom = nodepath->desktop->current_zoom();
3575             gdouble const zmove = angle / zoom;
3576             gdouble const r = NR::L2(box.max() - box.midpoint());
3577             rot = atan2(zmove, r);
3578         } else {
3579             rot = angle;
3580         }
3582         NR::Matrix t =
3583             NR::Matrix (NR::translate(-box.midpoint())) *
3584             NR::Matrix (NR::rotate(rot)) *
3585             NR::Matrix (NR::translate(box.midpoint()));
3587         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3588             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3589             n->pos *= t;
3590             n->n.pos *= t;
3591             n->p.pos *= t;
3592             sp_node_update_handles(n, false);
3593         }
3594     }
3596     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3599 /**
3600  * Scale one node.
3601  */
3602 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3604     bool both = false;
3605     Inkscape::NodePath::NodeSide *me, *other;
3607     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3608     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3610     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3611         me = &(n->p);
3612         other = &(n->n);
3613         n->code = NR_CURVETO;
3614     } else if (!n->p.other) {
3615         me = &(n->n);
3616         other = &(n->p);
3617         if (n->n.other)
3618             n->n.other->code = NR_CURVETO;
3619     } else {
3620         if (which > 0) { // right handle
3621             if (xn > xp) {
3622                 me = &(n->n);
3623                 other = &(n->p);
3624                 if (n->n.other)
3625                     n->n.other->code = NR_CURVETO;
3626             } else {
3627                 me = &(n->p);
3628                 other = &(n->n);
3629                 n->code = NR_CURVETO;
3630             }
3631         } else if (which < 0){ // left handle
3632             if (xn <= xp) {
3633                 me = &(n->n);
3634                 other = &(n->p);
3635                 if (n->n.other)
3636                     n->n.other->code = NR_CURVETO;
3637             } else {
3638                 me = &(n->p);
3639                 other = &(n->n);
3640                 n->code = NR_CURVETO;
3641             }
3642         } else { // both handles
3643             me = &(n->n);
3644             other = &(n->p);
3645             both = true;
3646             n->code = NR_CURVETO;
3647             if (n->n.other)
3648                 n->n.other->code = NR_CURVETO;
3649         }
3650     }
3652     Radial rme(me->pos - n->pos);
3653     Radial rother(other->pos - n->pos);
3655     rme.r += grow;
3656     if (rme.r < 0) rme.r = 0;
3657     if (rme.a == HUGE_VAL) {
3658         if (me->other) { // if direction is unknown, initialize it towards the next node
3659             Radial rme_next(me->other->pos - n->pos);
3660             rme.a = rme_next.a;
3661         } else { // if there's no next, initialize to 0
3662             rme.a = 0;
3663         }
3664     }
3665     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3666         rother.r += grow;
3667         if (rother.r < 0) rother.r = 0;
3668         if (rother.a == HUGE_VAL) {
3669             rother.a = rme.a + M_PI;
3670         }
3671     }
3673     me->pos = n->pos + NR::Point(rme);
3675     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3676         other->pos = n->pos + NR::Point(rother);
3677     }
3679     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3680     // so here we just move all the knots without emitting move signals, for speed
3681     sp_node_update_handles(n, false);
3684 /**
3685  * Scale selected nodes.
3686  */
3687 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3689     if (!nodepath || !nodepath->selected) return;
3691     if (g_list_length(nodepath->selected) == 1) {
3692         // scale handles of the single selected node
3693         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3694         node_scale_one (n, grow, which);
3695     } else {
3696         // scale nodes as an "object":
3698         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3699         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3700         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3701             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3702             box.expandTo (n->pos); // contain all selected nodes
3703         }
3705         double scale = (box.maxExtent() + grow)/box.maxExtent();
3707         NR::Matrix t =
3708             NR::Matrix (NR::translate(-box.midpoint())) *
3709             NR::Matrix (NR::scale(scale, scale)) *
3710             NR::Matrix (NR::translate(box.midpoint()));
3712         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3713             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3714             n->pos *= t;
3715             n->n.pos *= t;
3716             n->p.pos *= t;
3717             sp_node_update_handles(n, false);
3718         }
3719     }
3721     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3724 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3726     if (!nodepath) return;
3727     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3730 /**
3731  * Flip selected nodes horizontally/vertically.
3732  */
3733 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3735     if (!nodepath || !nodepath->selected) return;
3737     if (g_list_length(nodepath->selected) == 1) {
3738         // flip handles of the single selected node
3739         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3740         double temp = n->p.pos[axis];
3741         n->p.pos[axis] = n->n.pos[axis];
3742         n->n.pos[axis] = temp;
3743         sp_node_update_handles(n, false);
3744     } else {
3745         // scale nodes as an "object":
3747         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3748         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3749         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3750             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3751             box.expandTo (n->pos); // contain all selected nodes
3752         }
3754         NR::Matrix t =
3755             NR::Matrix (NR::translate(-box.midpoint())) *
3756             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3757             NR::Matrix (NR::translate(box.midpoint()));
3759         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3760             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3761             n->pos *= t;
3762             n->n.pos *= t;
3763             n->p.pos *= t;
3764             sp_node_update_handles(n, false);
3765         }
3766     }
3768     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3771 //-----------------------------------------------
3772 /**
3773  * Return new subpath under given nodepath.
3774  */
3775 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3777     g_assert(nodepath);
3778     g_assert(nodepath->desktop);
3780    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3782     s->nodepath = nodepath;
3783     s->closed = FALSE;
3784     s->nodes = NULL;
3785     s->first = NULL;
3786     s->last = NULL;
3788     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3789     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3790     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3792     return s;
3795 /**
3796  * Destroy nodes in subpath, then subpath itself.
3797  */
3798 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3800     g_assert(subpath);
3801     g_assert(subpath->nodepath);
3802     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3804     while (subpath->nodes) {
3805         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3806     }
3808     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3810     g_free(subpath);
3813 /**
3814  * Link head to tail in subpath.
3815  */
3816 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3818     g_assert(!sp->closed);
3819     g_assert(sp->last != sp->first);
3820     g_assert(sp->first->code == NR_MOVETO);
3822     sp->closed = TRUE;
3824     //Link the head to the tail
3825     sp->first->p.other = sp->last;
3826     sp->last->n.other  = sp->first;
3827     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3828     sp->first          = sp->last;
3830     //Remove the extra end node
3831     sp_nodepath_node_destroy(sp->last->n.other);
3834 /**
3835  * Open closed (loopy) subpath at node.
3836  */
3837 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3839     g_assert(sp->closed);
3840     g_assert(n->subpath == sp);
3841     g_assert(sp->first == sp->last);
3843     /* We create new startpoint, current node will become last one */
3845    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3846                                                 &n->pos, &n->pos, &n->n.pos);
3849     sp->closed        = FALSE;
3851     //Unlink to make a head and tail
3852     sp->first         = new_path;
3853     sp->last          = n;
3854     n->n.other        = NULL;
3855     new_path->p.other = NULL;
3858 /**
3859  * Returns area in triangle given by points; may be negative.
3860  */
3861 inline double
3862 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3864     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]);
3867 /**
3868  * Return new node in subpath with given properties.
3869  * \param pos Position of node.
3870  * \param ppos Handle position in previous direction
3871  * \param npos Handle position in previous direction
3872  */
3873 Inkscape::NodePath::Node *
3874 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)
3876     g_assert(sp);
3877     g_assert(sp->nodepath);
3878     g_assert(sp->nodepath->desktop);
3880     if (nodechunk == NULL)
3881         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3883     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3885     n->subpath  = sp;
3887     if (type != Inkscape::NodePath::NODE_NONE) {
3888         // use the type from sodipodi:nodetypes
3889         n->type = type;
3890     } else {
3891         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3892             // points are (almost) collinear
3893             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3894                 // endnode, or a node with a retracted handle
3895                 n->type = Inkscape::NodePath::NODE_CUSP;
3896             } else {
3897                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3898             }
3899         } else {
3900             n->type = Inkscape::NodePath::NODE_CUSP;
3901         }
3902     }
3904     n->code     = code;
3905     n->selected = FALSE;
3906     n->pos      = *pos;
3907     n->p.pos    = *ppos;
3908     n->n.pos    = *npos;
3910     n->dragging_out = NULL;
3912     Inkscape::NodePath::Node *prev;
3913     if (next) {
3914         //g_assert(g_list_find(sp->nodes, next));
3915         prev = next->p.other;
3916     } else {
3917         prev = sp->last;
3918     }
3920     if (prev)
3921         prev->n.other = n;
3922     else
3923         sp->first = n;
3925     if (next)
3926         next->p.other = n;
3927     else
3928         sp->last = n;
3930     n->p.other = prev;
3931     n->n.other = next;
3933     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"));
3934     sp_knot_set_position(n->knot, pos, 0);
3936     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3937     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3938     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3939     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3940     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3941     sp_knot_update_ctrl(n->knot);
3943     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3944     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3945     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3946     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3947     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3948     sp_knot_show(n->knot);
3950     // We only create handle knots and lines on demand
3951     n->p.knot = NULL;
3952     n->p.line = NULL;
3953     n->n.knot = NULL;
3954     n->n.line = NULL;
3956     sp->nodes = g_list_prepend(sp->nodes, n);
3958     return n;
3961 /**
3962  * Destroy node and its knots, link neighbors in subpath.
3963  */
3964 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3966     g_assert(node);
3967     g_assert(node->subpath);
3968     g_assert(SP_IS_KNOT(node->knot));
3970    Inkscape::NodePath::SubPath *sp = node->subpath;
3972     if (node->selected) { // first, deselect
3973         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3974         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3975     }
3977     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3979     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
3980     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
3981     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
3982     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
3983     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
3984     g_object_unref(G_OBJECT(node->knot));
3986     if (node->p.knot) {
3987         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
3988         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
3989         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
3990         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
3991         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
3992         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
3993         g_object_unref(G_OBJECT(node->p.knot));
3994     }
3996     if (node->n.knot) {
3997         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
3998         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
3999         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4000         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4001         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4002         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4003         g_object_unref(G_OBJECT(node->n.knot));
4004     }
4006     if (node->p.line)
4007         gtk_object_destroy(GTK_OBJECT(node->p.line));
4008     if (node->n.line)
4009         gtk_object_destroy(GTK_OBJECT(node->n.line));
4011     if (sp->nodes) { // there are others nodes on the subpath
4012         if (sp->closed) {
4013             if (sp->first == node) {
4014                 g_assert(sp->last == node);
4015                 sp->first = node->n.other;
4016                 sp->last = sp->first;
4017             }
4018             node->p.other->n.other = node->n.other;
4019             node->n.other->p.other = node->p.other;
4020         } else {
4021             if (sp->first == node) {
4022                 sp->first = node->n.other;
4023                 sp->first->code = NR_MOVETO;
4024             }
4025             if (sp->last == node) sp->last = node->p.other;
4026             if (node->p.other) node->p.other->n.other = node->n.other;
4027             if (node->n.other) node->n.other->p.other = node->p.other;
4028         }
4029     } else { // this was the last node on subpath
4030         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4031     }
4033     g_mem_chunk_free(nodechunk, node);
4036 /**
4037  * Returns one of the node's two sides.
4038  * \param which Indicates which side.
4039  * \return Pointer to previous node side if which==-1, next if which==1.
4040  */
4041 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4043     g_assert(node);
4045     switch (which) {
4046         case -1:
4047             return &node->p;
4048         case 1:
4049             return &node->n;
4050         default:
4051             break;
4052     }
4054     g_assert_not_reached();
4056     return NULL;
4059 /**
4060  * Return the other side of the node, given one of its sides.
4061  */
4062 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4064     g_assert(node);
4066     if (me == &node->p) return &node->n;
4067     if (me == &node->n) return &node->p;
4069     g_assert_not_reached();
4071     return NULL;
4074 /**
4075  * Return NRPathcode on the given side of the node.
4076  */
4077 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4079     g_assert(node);
4081     if (me == &node->p) {
4082         if (node->p.other) return (NRPathcode)node->code;
4083         return NR_MOVETO;
4084     }
4086     if (me == &node->n) {
4087         if (node->n.other) return (NRPathcode)node->n.other->code;
4088         return NR_MOVETO;
4089     }
4091     g_assert_not_reached();
4093     return NR_END;
4096 /**
4097  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4098  */
4099 Inkscape::NodePath::Node *
4100 sp_nodepath_get_node_by_index(int index)
4102     Inkscape::NodePath::Node *e = NULL;
4104     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4105     if (!nodepath) {
4106         return e;
4107     }
4109     //find segment
4110     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4112         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4113         int n = g_list_length(sp->nodes);
4114         if (sp->closed) {
4115             n++;
4116         }
4118         //if the piece belongs to this subpath grab it
4119         //otherwise move onto the next subpath
4120         if (index < n) {
4121             e = sp->first;
4122             for (int i = 0; i < index; ++i) {
4123                 e = e->n.other;
4124             }
4125             break;
4126         } else {
4127             if (sp->closed) {
4128                 index -= (n+1);
4129             } else {
4130                 index -= n;
4131             }
4132         }
4133     }
4135     return e;
4138 /**
4139  * Returns plain text meaning of node type.
4140  */
4141 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4143     unsigned retracted = 0;
4144     bool endnode = false;
4146     for (int which = -1; which <= 1; which += 2) {
4147         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4148         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4149             retracted ++;
4150         if (!side->other)
4151             endnode = true;
4152     }
4154     if (retracted == 0) {
4155         if (endnode) {
4156                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4157                 return _("end node");
4158         } else {
4159             switch (node->type) {
4160                 case Inkscape::NodePath::NODE_CUSP:
4161                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4162                     return _("cusp");
4163                 case Inkscape::NodePath::NODE_SMOOTH:
4164                     // TRANSLATORS: "smooth" is an adjective here
4165                     return _("smooth");
4166                 case Inkscape::NodePath::NODE_SYMM:
4167                     return _("symmetric");
4168             }
4169         }
4170     } else if (retracted == 1) {
4171         if (endnode) {
4172             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4173             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4174         } else {
4175             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4176         }
4177     } else {
4178         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4179     }
4181     return NULL;
4184 /**
4185  * Handles content of statusbar as long as node tool is active.
4186  */
4187 void
4188 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
4190     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");
4191     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4193     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4194     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4195     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4196     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4198     SPDesktop *desktop = NULL;
4199     if (nodepath) {
4200         desktop = nodepath->desktop;
4201     } else {
4202         desktop = SP_ACTIVE_DESKTOP;
4203     }
4205     SPEventContext *ec = desktop->event_context;
4206     if (!ec) return;
4207     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4208     if (!mc) return;
4210     if (selected_nodes == 0) {
4211         Inkscape::Selection *sel = desktop->selection;
4212         if (!sel || sel->isEmpty()) {
4213             mc->setF(Inkscape::NORMAL_MESSAGE,
4214                      _("Select a single object to edit its nodes or handles."));
4215         } else {
4216             if (nodepath) {
4217             mc->setF(Inkscape::NORMAL_MESSAGE,
4218                      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.",
4219                               "<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.",
4220                               total_nodes),
4221                      total_nodes);
4222             } else {
4223                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4224                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4225                 } else {
4226                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4227                 }
4228             }
4229         }
4230     } else if (nodepath && selected_nodes == 1) {
4231         mc->setF(Inkscape::NORMAL_MESSAGE,
4232                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4233                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4234                           total_nodes),
4235                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4236     } else {
4237         if (selected_subpaths > 1) {
4238             mc->setF(Inkscape::NORMAL_MESSAGE,
4239                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4240                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4241                               total_nodes),
4242                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4243         } else {
4244             mc->setF(Inkscape::NORMAL_MESSAGE,
4245                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4246                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4247                               total_nodes),
4248                      selected_nodes, total_nodes, when_selected);
4249         }
4250     }
4254 /*
4255   Local Variables:
4256   mode:c++
4257   c-file-style:"stroustrup"
4258   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4259   indent-tabs-mode:nil
4260   fill-column:99
4261   End:
4262 */
4263 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :