Code

Remove warnings
[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  * This code is in public domain
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"
44 class NR::Matrix;
46 /// \todo
47 /// evil evil evil. FIXME: conflict of two different Path classes!
48 /// There is a conflict in the namespace between two classes named Path.
49 /// #include "sp-flowtext.h"
50 /// #include "sp-flowregion.h"
52 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
53 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
54 GType sp_flowregion_get_type (void);
55 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
56 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
57 GType sp_flowtext_get_type (void);
58 // end evil workaround
60 #include "helper/stlport.h"
63 /// \todo fixme: Implement these via preferences */
65 #define NODE_FILL          0xbfbfbf00
66 #define NODE_STROKE        0x000000ff
67 #define NODE_FILL_HI       0xff000000
68 #define NODE_STROKE_HI     0x000000ff
69 #define NODE_FILL_SEL      0x0000ffff
70 #define NODE_STROKE_SEL    0x000000ff
71 #define NODE_FILL_SEL_HI   0xff000000
72 #define NODE_STROKE_SEL_HI 0x000000ff
73 #define KNOT_FILL          0xffffffff
74 #define KNOT_STROKE        0x000000ff
75 #define KNOT_FILL_HI       0xff000000
76 #define KNOT_STROKE_HI     0x000000ff
78 static GMemChunk *nodechunk = NULL;
80 /* Creation from object */
82 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
83 static gchar *parse_nodetypes(gchar const *types, gint length);
85 /* Object updating */
87 static void stamp_repr(Inkscape::NodePath::Path *np);
88 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
89 static gchar *create_typestr(Inkscape::NodePath::Path *np);
91 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
93 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
95 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
97 /* Adjust handle placement, if the node or the other handle is moved */
98 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
99 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
101 /* Node event callbacks */
102 static void node_clicked(SPKnot *knot, guint state, gpointer data);
103 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
104 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
105 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
107 /* Handle event callbacks */
108 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
109 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
110 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
111 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
112 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
113 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
115 /* Constructors and destructors */
117 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
118 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
119 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
120 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
121 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
122                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
123 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
125 /* Helpers */
127 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
128 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
129 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
131 // active_node indicates mouseover node
132 static Inkscape::NodePath::Node *active_node = NULL;
134 /**
135  * \brief Creates new nodepath from item
136  */
137 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item)
139     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
141     /** \todo
142      * FIXME: remove this. We don't want to edit paths inside flowtext.
143      * Instead we will build our flowtext with cloned paths, so that the
144      * real paths are outside the flowtext and thus editable as usual.
145      */
146     if (SP_IS_FLOWTEXT(item)) {
147         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
148             if SP_IS_FLOWREGION(child) {
149                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
150                 if (grandchild && SP_IS_PATH(grandchild)) {
151                     item = SP_ITEM(grandchild);
152                     break;
153                 }
154             }
155         }
156     }
158     if (!SP_IS_PATH(item))
159         return NULL;
160     SPPath *path = SP_PATH(item);
161     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
162     if (curve == NULL)
163         return NULL;
165     NArtBpath *bpath = sp_curve_first_bpath(curve);
166     gint length = curve->end;
167     if (length == 0)
168         return NULL; // prevent crash for one-node paths
170     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
171     gchar *typestr = parse_nodetypes(nodetypes, length);
173     //Create new nodepath
174     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
175     if (!np)
176         return NULL;
178     // Set defaults
179     np->desktop     = desktop;
180     np->path        = path;
181     np->subpaths    = NULL;
182     np->selected    = NULL;
183     np->nodeContext = NULL; //Let the context that makes this set it
184     np->livarot_path = NULL;
185     np->local_change = 0;
187     // we need to update item's transform from the repr here,
188     // because they may be out of sync when we respond
189     // to a change in repr by regenerating nodepath     --bb
190     sp_object_read_attr(SP_OBJECT(item), "transform");
192     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
193     np->d2i  = np->i2d.inverse();
194     np->repr = repr;
196     // create the subpath(s) from the bpath
197     NArtBpath *b = bpath;
198     while (b->code != NR_END) {
199         b = subpath_from_bpath(np, b, typestr + (b - bpath));
200     }
202     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
203     np->subpaths = g_list_reverse(np->subpaths);
205     g_free(typestr);
206     sp_curve_unref(curve);
208     // create the livarot representation from the same item
209     np->livarot_path = Path_for_item(item, true, true);
210     if (np->livarot_path)
211         np->livarot_path->ConvertWithBackData(0.01);
213     return np;
216 /**
217  * Destroys nodepath's subpaths, then itself, also tell context about it.
218  */
219 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
221     if (!np)  //soft fail, like delete
222         return;
224     while (np->subpaths) {
225         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
226     }
228     //Inform the context that made me, if any, that I am gone.
229     if (np->nodeContext)
230         np->nodeContext->nodepath = NULL;
232     g_assert(!np->selected);
234     if (np->livarot_path) {
235         delete np->livarot_path;
236         np->livarot_path = NULL;
237     }
239     np->desktop = NULL;
241     g_free(np);
245 /**
246  *  Return the node count of a given NodeSubPath.
247  */
248 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
250     if (!subpath)
251         return 0;
252     gint nodeCount = g_list_length(subpath->nodes);
253     return nodeCount;
256 /**
257  *  Return the node count of a given NodePath.
258  */
259 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
261     if (!np)
262         return 0;
263     gint nodeCount = 0;
264     for (GList *item = np->subpaths ; item ; item=item->next) {
265        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
266         nodeCount += g_list_length(subpath->nodes);
267     }
268     return nodeCount;
272 /**
273  * Clean up a nodepath after editing.
274  *
275  * Currently we are deleting trivial subpaths.
276  */
277 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
279     GList *badSubPaths = NULL;
281     //Check all subpaths to be >=2 nodes
282     for (GList *l = nodepath->subpaths; l ; l=l->next) {
283        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
284         if (sp_nodepath_subpath_get_node_count(sp)<2)
285             badSubPaths = g_list_append(badSubPaths, sp);
286     }
288     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
289     //also removes the subpath from nodepath->subpaths
290     for (GList *l = badSubPaths; l ; l=l->next) {
291        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
292         sp_nodepath_subpath_destroy(sp);
293     }
295     g_list_free(badSubPaths);
298 /**
299  * Create new nodepath from b, make it subpath of np.
300  * \param t The node type.
301  * \todo Fixme: t should be a proper type, rather than gchar
302  */
303 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
305     NR::Point ppos, pos, npos;
307     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
309     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
310     bool const closed = (b->code == NR_MOVETO);
312     pos = NR::Point(b->x3, b->y3) * np->i2d;
313     if (b[1].code == NR_CURVETO) {
314         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
315     } else {
316         npos = pos;
317     }
318     Inkscape::NodePath::Node *n;
319     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
320     g_assert(sp->first == n);
321     g_assert(sp->last  == n);
323     b++;
324     t++;
325     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
326         pos = NR::Point(b->x3, b->y3) * np->i2d;
327         if (b->code == NR_CURVETO) {
328             ppos = NR::Point(b->x2, b->y2) * np->i2d;
329         } else {
330             ppos = pos;
331         }
332         if (b[1].code == NR_CURVETO) {
333             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
334         } else {
335             npos = pos;
336         }
337         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
338         b++;
339         t++;
340     }
342     if (closed) sp_nodepath_subpath_close(sp);
344     return b;
347 /**
348  * Convert from sodipodi:nodetypes to new style type string.
349  */
350 static gchar *parse_nodetypes(gchar const *types, gint length)
352     g_assert(length > 0);
354     gchar *typestr = g_new(gchar, length + 1);
356     gint pos = 0;
358     if (types) {
359         for (gint i = 0; types[i] && ( i < length ); i++) {
360             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
361             if (types[i] != '\0') {
362                 switch (types[i]) {
363                     case 's':
364                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
365                         break;
366                     case 'z':
367                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
368                         break;
369                     case 'c':
370                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
371                         break;
372                     default:
373                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
374                         break;
375                 }
376             }
377         }
378     }
380     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
382     return typestr;
385 /**
386  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
387  * updated but repr is not (for speed). Used during curve and node drag.
388  */
389 static void update_object(Inkscape::NodePath::Path *np)
391     g_assert(np);
393     SPCurve *curve = create_curve(np);
395     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
397     sp_curve_unref(curve);
400 /**
401  * Update XML path node with data from path object.
402  */
403 static void update_repr_internal(Inkscape::NodePath::Path *np)
405     g_assert(np);
407     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
409     SPCurve *curve = create_curve(np);
410     gchar *typestr = create_typestr(np);
411     gchar *svgpath = sp_svg_write_path(curve->bpath);
413     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
414         np->local_change++;
415         repr->setAttribute("d", svgpath);
416     }
418     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
419         np->local_change++;
420         repr->setAttribute("sodipodi:nodetypes", typestr);
421     }
423     g_free(svgpath);
424     g_free(typestr);
425     sp_curve_unref(curve);
428 /**
429  * Update XML path node with data from path object, commit changes forever.
430  */
431 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np)
433     update_repr_internal(np);
434     sp_document_done(SP_DT_DOCUMENT(np->desktop));
436     if (np->livarot_path) {
437         delete np->livarot_path;
438         np->livarot_path = NULL;
439     }
441     if (np->path && SP_IS_ITEM(np->path)) {
442         np->livarot_path = Path_for_item (np->path, true, true);
443         if (np->livarot_path)
444             np->livarot_path->ConvertWithBackData(0.01);
445     }
448 /**
449  * Update XML path node with data from path object, commit changes with undo.
450  */
451 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key)
453     update_repr_internal(np);
454     sp_document_maybe_done(SP_DT_DOCUMENT(np->desktop), key);
456     if (np->livarot_path) {
457         delete np->livarot_path;
458         np->livarot_path = NULL;
459     }
461     if (np->path && SP_IS_ITEM(np->path)) {
462         np->livarot_path = Path_for_item (np->path, true, true);
463         if (np->livarot_path)
464             np->livarot_path->ConvertWithBackData(0.01);
465     }
468 /**
469  * Make duplicate of path, replace corresponding XML node in tree, commit.
470  */
471 static void stamp_repr(Inkscape::NodePath::Path *np)
473     g_assert(np);
475     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
476     Inkscape::XML::Node *new_repr = old_repr->duplicate();
478     // remember the position of the item
479     gint pos = old_repr->position();
480     // remember parent
481     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
483     SPCurve *curve = create_curve(np);
484     gchar *typestr = create_typestr(np);
486     gchar *svgpath = sp_svg_write_path(curve->bpath);
488     new_repr->setAttribute("d", svgpath);
489     new_repr->setAttribute("sodipodi:nodetypes", typestr);
491     // add the new repr to the parent
492     parent->appendChild(new_repr);
493     // move to the saved position
494     new_repr->setPosition(pos > 0 ? pos : 0);
496     sp_document_done(SP_DT_DOCUMENT(np->desktop));
498     Inkscape::GC::release(new_repr);
499     g_free(svgpath);
500     g_free(typestr);
501     sp_curve_unref(curve);
504 /**
505  * Create curve from path.
506  */
507 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
509     SPCurve *curve = sp_curve_new();
511     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
512        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
513         sp_curve_moveto(curve,
514                         sp->first->pos * np->d2i);
515        Inkscape::NodePath::Node *n = sp->first->n.other;
516         while (n) {
517             NR::Point const end_pt = n->pos * np->d2i;
518             switch (n->code) {
519                 case NR_LINETO:
520                     sp_curve_lineto(curve, end_pt);
521                     break;
522                 case NR_CURVETO:
523                     sp_curve_curveto(curve,
524                                      n->p.other->n.pos * np->d2i,
525                                      n->p.pos * np->d2i,
526                                      end_pt);
527                     break;
528                 default:
529                     g_assert_not_reached();
530                     break;
531             }
532             if (n != sp->last) {
533                 n = n->n.other;
534             } else {
535                 n = NULL;
536             }
537         }
538         if (sp->closed) {
539             sp_curve_closepath(curve);
540         }
541     }
543     return curve;
546 /**
547  * Convert path type string to sodipodi:nodetypes style.
548  */
549 static gchar *create_typestr(Inkscape::NodePath::Path *np)
551     gchar *typestr = g_new(gchar, 32);
552     gint len = 32;
553     gint pos = 0;
555     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
556        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
558         if (pos >= len) {
559             typestr = g_renew(gchar, typestr, len + 32);
560             len += 32;
561         }
563         typestr[pos++] = 'c';
565        Inkscape::NodePath::Node *n;
566         n = sp->first->n.other;
567         while (n) {
568             gchar code;
570             switch (n->type) {
571                 case Inkscape::NodePath::NODE_CUSP:
572                     code = 'c';
573                     break;
574                 case Inkscape::NodePath::NODE_SMOOTH:
575                     code = 's';
576                     break;
577                 case Inkscape::NodePath::NODE_SYMM:
578                     code = 'z';
579                     break;
580                 default:
581                     g_assert_not_reached();
582                     code = '\0';
583                     break;
584             }
586             if (pos >= len) {
587                 typestr = g_renew(gchar, typestr, len + 32);
588                 len += 32;
589             }
591             typestr[pos++] = code;
593             if (n != sp->last) {
594                 n = n->n.other;
595             } else {
596                 n = NULL;
597             }
598         }
599     }
601     if (pos >= len) {
602         typestr = g_renew(gchar, typestr, len + 1);
603         len += 1;
604     }
606     typestr[pos++] = '\0';
608     return typestr;
611 /**
612  * Returns current path in context.
613  */
614 static Inkscape::NodePath::Path *sp_nodepath_current()
616     if (!SP_ACTIVE_DESKTOP) {
617         return NULL;
618     }
620     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
622     if (!SP_IS_NODE_CONTEXT(event_context)) {
623         return NULL;
624     }
626     return SP_NODE_CONTEXT(event_context)->nodepath;
631 /**
632  \brief Fills node and handle positions for three nodes, splitting line
633   marked by end at distance t.
634  */
635 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
637     g_assert(new_path != NULL);
638     g_assert(end      != NULL);
640     g_assert(end->p.other == new_path);
641    Inkscape::NodePath::Node *start = new_path->p.other;
642     g_assert(start);
644     if (end->code == NR_LINETO) {
645         new_path->type =Inkscape::NodePath::NODE_CUSP;
646         new_path->code = NR_LINETO;
647         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
648     } else {
649         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
650         new_path->code = NR_CURVETO;
651         gdouble s      = 1 - t;
652         for (int dim = 0; dim < 2; dim++) {
653             NR::Coord const f000 = start->pos[dim];
654             NR::Coord const f001 = start->n.pos[dim];
655             NR::Coord const f011 = end->p.pos[dim];
656             NR::Coord const f111 = end->pos[dim];
657             NR::Coord const f00t = s * f000 + t * f001;
658             NR::Coord const f01t = s * f001 + t * f011;
659             NR::Coord const f11t = s * f011 + t * f111;
660             NR::Coord const f0tt = s * f00t + t * f01t;
661             NR::Coord const f1tt = s * f01t + t * f11t;
662             NR::Coord const fttt = s * f0tt + t * f1tt;
663             start->n.pos[dim]    = f00t;
664             new_path->p.pos[dim] = f0tt;
665             new_path->pos[dim]   = fttt;
666             new_path->n.pos[dim] = f1tt;
667             end->p.pos[dim]      = f11t;
668         }
669     }
672 /**
673  * Adds new node on direct line between two nodes, activates handles of all
674  * three nodes.
675  */
676 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
678     g_assert(end);
679     g_assert(end->subpath);
680     g_assert(g_list_find(end->subpath->nodes, end));
682    Inkscape::NodePath::Node *start = end->p.other;
683     g_assert( start->n.other == end );
684    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
685                                                end,
686                                               Inkscape::NodePath::NODE_SMOOTH,
687                                                (NRPathcode)end->code,
688                                                &start->pos, &start->pos, &start->n.pos);
689     sp_nodepath_line_midpoint(newnode, end, t);
691     sp_node_update_handles(start);
692     sp_node_update_handles(newnode);
693     sp_node_update_handles(end);
695     return newnode;
698 /**
699 \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
700 */
701 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
703     g_assert(node);
704     g_assert(node->subpath);
705     g_assert(g_list_find(node->subpath->nodes, node));
707    Inkscape::NodePath::SubPath *sp = node->subpath;
708     Inkscape::NodePath::Path *np    = sp->nodepath;
710     if (sp->closed) {
711         sp_nodepath_subpath_open(sp, node);
712         return sp->first;
713     } else {
714         // no break for end nodes
715         if (node == sp->first) return NULL;
716         if (node == sp->last ) return NULL;
718         // create a new subpath
719        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
721         // duplicate the break node as start of the new subpath
722        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
724         while (node->n.other) { // copy the remaining nodes into the new subpath
725            Inkscape::NodePath::Node *n  = node->n.other;
726            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);
727             if (n->selected) {
728                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
729             }
730             sp_nodepath_node_destroy(n); // remove the point on the original subpath
731         }
733         return newnode;
734     }
737 /**
738  * Duplicate node and connect to neighbours.
739  */
740 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
742     g_assert(node);
743     g_assert(node->subpath);
744     g_assert(g_list_find(node->subpath->nodes, node));
746    Inkscape::NodePath::SubPath *sp = node->subpath;
748     NRPathcode code = (NRPathcode) node->code;
749     if (code == NR_MOVETO) { // if node is the endnode,
750         node->code = NR_LINETO; // new one is inserted before it, so change that to line
751     }
753     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
755     if (!node->n.other || !node->p.other) // if node is an endnode, select it
756         return node;
757     else
758         return newnode; // otherwise select the newly created node
761 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
763     node->p.pos = (node->pos + (node->pos - node->n.pos));
766 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
768     node->n.pos = (node->pos + (node->pos - node->p.pos));
771 /**
772  * Change line type at node, with side effects on neighbours.
773  */
774 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
776     g_assert(end);
777     g_assert(end->subpath);
778     g_assert(end->p.other);
780     if (end->code == static_cast< guint > ( code ) )
781         return;
783    Inkscape::NodePath::Node *start = end->p.other;
785     end->code = code;
787     if (code == NR_LINETO) {
788         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
789         if (end->n.other) {
790             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
791         }
792         sp_node_adjust_handle(start, -1);
793         sp_node_adjust_handle(end, 1);
794     } else {
795         NR::Point delta = end->pos - start->pos;
796         start->n.pos = start->pos + delta / 3;
797         end->p.pos = end->pos - delta / 3;
798         sp_node_adjust_handle(start, 1);
799         sp_node_adjust_handle(end, -1);
800     }
802     sp_node_update_handles(start);
803     sp_node_update_handles(end);
806 /**
807  * Change node type, and its handles accordingly.
808  */
809 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
811     g_assert(node);
812     g_assert(node->subpath);
814     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
815         return node;
817     if ((node->p.other != NULL) && (node->n.other != NULL)) {
818         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
819             type =Inkscape::NodePath::NODE_CUSP;
820         }
821     }
823     node->type = type;
825     if (node->type == Inkscape::NodePath::NODE_CUSP) {
826         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
827         node->knot->setSize (node->selected? 11 : 9);
828         sp_knot_update_ctrl(node->knot);
829     } else {
830         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
831         node->knot->setSize (node->selected? 9 : 7);
832         sp_knot_update_ctrl(node->knot);
833     }
835     // if one of handles is mouseovered, preserve its position
836     if (node->p.knot && SP_KNOT_IS_MOSEOVER(node->p.knot)) {
837         sp_node_adjust_handle(node, 1);
838     } else if (node->n.knot && SP_KNOT_IS_MOSEOVER(node->n.knot)) {
839         sp_node_adjust_handle(node, -1);
840     } else {
841         sp_node_adjust_handles(node);
842     }
844     sp_node_update_handles(node);
846     sp_nodepath_update_statusbar(node->subpath->nodepath);
848     return node;
851 /**
852  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
853  * adjacent segments from lines to curves.
854 */
855 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
857     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
858         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
859             // convert adjacent segment BEFORE to curve
860             node->code = NR_CURVETO;
861             NR::Point delta;
862             if (node->n.other != NULL)
863                 delta = node->n.other->pos - node->p.other->pos;
864             else
865                 delta = node->pos - node->p.other->pos;
866             node->p.pos = node->pos - delta / 4;
867             sp_node_update_handles(node);
868         }
870         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
871             // convert adjacent segment AFTER to curve
872             node->n.other->code = NR_CURVETO;
873             NR::Point delta;
874             if (node->p.other != NULL)
875                 delta = node->p.other->pos - node->n.other->pos;
876             else
877                 delta = node->pos - node->n.other->pos;
878             node->n.pos = node->pos - delta / 4;
879             sp_node_update_handles(node);
880         }
881     }
883     sp_nodepath_set_node_type (node, type);
886 /**
887  * Move node to point, and adjust its and neighbouring handles.
888  */
889 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
891     NR::Point delta = p - node->pos;
892     node->pos = p;
894     node->p.pos += delta;
895     node->n.pos += delta;
897     if (node->p.other) {
898         if (node->code == NR_LINETO) {
899             sp_node_adjust_handle(node, 1);
900             sp_node_adjust_handle(node->p.other, -1);
901         }
902     }
903     if (node->n.other) {
904         if (node->n.other->code == NR_LINETO) {
905             sp_node_adjust_handle(node, -1);
906             sp_node_adjust_handle(node->n.other, 1);
907         }
908     }
910     // this function is only called from batch movers that will update display at the end
911     // themselves, so here we just move all the knots without emitting move signals, for speed
912     sp_node_update_handles(node, false);
915 /**
916  * Call sp_node_moveto() for node selection and handle possible snapping.
917  */
918 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
919                                             bool const snap = true)
921     NR::Coord best[2] = { NR_HUGE, NR_HUGE };
922     NR::Point delta(dx, dy);
923     NR::Point best_pt = delta;
925     if (snap) {
926         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
927            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
928             NR::Point p = n->pos + delta;
929             for (int dim = 0; dim < 2; dim++) {
930                 NR::Coord dist = namedview_dim_snap(nodepath->desktop->namedview,
931                                                     Inkscape::Snapper::SNAP_POINT, p,
932                                                     NR::Dim2(dim), nodepath->path);
933                 if (dist < best[dim]) {
934                     best[dim] = dist;
935                     best_pt[dim] = p[dim] - n->pos[dim];
936                 }
937             }
938         }
939     }
941     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
942        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
943         sp_node_moveto(n, n->pos + best_pt);
944     }
946     // do not update repr here so that node dragging is acceptably fast
947     update_object(nodepath);
950 /**
951  * Move node selection to point, adjust its and neighbouring handles,
952  * handle possible snapping, and commit the change with possible undo.
953  */
954 void
955 sp_node_selected_move(gdouble dx, gdouble dy)
957     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
958     if (!nodepath) return;
960     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
962     if (dx == 0) {
963         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
964     } else if (dy == 0) {
965         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
966     } else {
967         sp_nodepath_update_repr(nodepath);
968     }
971 /**
972  * Move node selection off screen and commit the change.
973  */
974 void
975 sp_node_selected_move_screen(gdouble dx, gdouble dy)
977     // borrowed from sp_selection_move_screen in selection-chemistry.c
978     // we find out the current zoom factor and divide deltas by it
979     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
981     gdouble zoom = desktop->current_zoom();
982     gdouble zdx = dx / zoom;
983     gdouble zdy = dy / zoom;
985     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
986     if (!nodepath) return;
988     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
990     if (dx == 0) {
991         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
992     } else if (dy == 0) {
993         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
994     } else {
995         sp_nodepath_update_repr(nodepath);
996     }
999 /** If they don't yet exist, creates knot and line for the given side of the node */
1000 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1002     if (!side->knot) {
1003         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"));
1005         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1006         side->knot->setSize (7);
1007         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1008         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1009         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1010         sp_knot_update_ctrl(side->knot);
1012         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1013         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1014         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1015         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1016         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1017         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1018     }
1020     if (!side->line) {
1021         side->line = sp_canvas_item_new(SP_DT_CONTROLS(desktop),
1022                                         SP_TYPE_CTRLLINE, NULL);
1023     }
1026 /**
1027  * Ensure the given handle of the node is visible/invisible, update its screen position
1028  */
1029 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1031     g_assert(node != NULL);
1033    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1034     NRPathcode code = sp_node_path_code_from_side(node, side);
1036     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1038     if (show_handle) {
1039         if (!side->knot) { // No handle knot at all
1040             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1041             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1042             side->knot->pos = side->pos;
1043             if (side->knot->item) 
1044                 SP_CTRL(side->knot->item)->moveto(side->pos);
1045             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1046             sp_knot_show(side->knot);
1047         } else {
1048             if (side->knot->pos != side->pos) { // only if it's really moved
1049                 if (fire_move_signals) {
1050                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1051                 } else {
1052                     sp_knot_moveto(side->knot, &side->pos);
1053                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1054                 }
1055             }
1056             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1057                 sp_knot_show(side->knot);
1058             }
1059         }
1060         sp_canvas_item_show(side->line);
1061     } else {
1062         if (side->knot) {
1063             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1064                 sp_knot_hide(side->knot);
1065             }
1066         }
1067         if (side->line) {
1068             sp_canvas_item_hide(side->line);
1069         }
1070     }
1073 /**
1074  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1075  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1076  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1077  * updated; otherwise, just move the knots silently (used in batch moves).
1078  */
1079 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1081     g_assert(node != NULL);
1083     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1084         sp_knot_show(node->knot);
1085     }
1087     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1088         if (fire_move_signals)
1089             sp_knot_set_position(node->knot, &node->pos, 0);
1090         else 
1091             sp_knot_moveto(node->knot, &node->pos);
1092     }
1094     gboolean show_handles = node->selected;
1095     if (node->p.other != NULL) {
1096         if (node->p.other->selected) show_handles = TRUE;
1097     }
1098     if (node->n.other != NULL) {
1099         if (node->n.other->selected) show_handles = TRUE;
1100     }
1102     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1103     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1106 /**
1107  * Call sp_node_update_handles() for all nodes on subpath.
1108  */
1109 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1111     g_assert(subpath != NULL);
1113     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1114         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1115     }
1118 /**
1119  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1120  */
1121 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1123     g_assert(nodepath != NULL);
1125     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1126         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1127     }
1130 /**
1131  * Adds all selected nodes in nodepath to list.
1132  */
1133 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1135     StlConv<Node *>::list(l, selected);
1136 /// \todo this adds a copying, rework when the selection becomes a stl list
1139 /**
1140  * Align selected nodes on the specified axis.
1141  */
1142 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1144     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1145         return;
1146     }
1148     if ( !nodepath->selected->next ) { // only one node selected
1149         return;
1150     }
1151    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1152     NR::Point dest(pNode->pos);
1153     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1154         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1155         if (pNode) {
1156             dest[axis] = pNode->pos[axis];
1157             sp_node_moveto(pNode, dest);
1158         }
1159     }
1161     sp_nodepath_update_repr(nodepath);
1164 /// Helper struct.
1165 struct NodeSort
1167    Inkscape::NodePath::Node *_node;
1168     NR::Coord _coord;
1169     /// \todo use vectorof pointers instead of calling copy ctor
1170     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1171         _node(node), _coord(node->pos[axis])
1172     {}
1174 };
1176 static bool operator<(NodeSort const &a, NodeSort const &b)
1178     return (a._coord < b._coord);
1181 /**
1182  * Distribute selected nodes on the specified axis.
1183  */
1184 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1186     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1187         return;
1188     }
1190     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1191         return;
1192     }
1194    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1195     std::vector<NodeSort> sorted;
1196     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1197         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1198         if (pNode) {
1199             NodeSort n(pNode, axis);
1200             sorted.push_back(n);
1201             //dest[axis] = pNode->pos[axis];
1202             //sp_node_moveto(pNode, dest);
1203         }
1204     }
1205     std::sort(sorted.begin(), sorted.end());
1206     unsigned int len = sorted.size();
1207     //overall bboxes span
1208     float dist = (sorted.back()._coord -
1209                   sorted.front()._coord);
1210     //new distance between each bbox
1211     float step = (dist) / (len - 1);
1212     float pos = sorted.front()._coord;
1213     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1214           it < sorted.end();
1215           it ++ )
1216     {
1217         NR::Point dest((*it)._node->pos);
1218         dest[axis] = pos;
1219         sp_node_moveto((*it)._node, dest);
1220         pos += step;
1221     }
1223     sp_nodepath_update_repr(nodepath);
1227 /**
1228  * Call sp_nodepath_line_add_node() for all selected segments.
1229  */
1230 void
1231 sp_node_selected_add_node(void)
1233     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1234     if (!nodepath) {
1235         return;
1236     }
1238     GList *nl = NULL;
1240     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1241        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1242         g_assert(t->selected);
1243         if (t->p.other && t->p.other->selected) {
1244             nl = g_list_prepend(nl, t);
1245         }
1246     }
1248     while (nl) {
1249        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1250        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1251         sp_nodepath_node_select(n, TRUE, FALSE);
1252         nl = g_list_remove(nl, t);
1253     }
1255     /** \todo fixme: adjust ? */
1256     sp_nodepath_update_handles(nodepath);
1258     sp_nodepath_update_repr(nodepath);
1260     sp_nodepath_update_statusbar(nodepath);
1263 /**
1264  * Select segment nearest to point
1265  */
1266 void
1267 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1269     if (!nodepath) {
1270         return;
1271     }
1273     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1275     //find segment to segment
1276     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1278     gboolean force = FALSE;
1279     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1280         force = TRUE;
1281     }
1282     sp_nodepath_node_select(e, (gboolean) toggle, force);
1283     if (e->p.other)
1284         sp_nodepath_node_select(e->p.other, TRUE, force);
1286     sp_nodepath_update_handles(nodepath);
1288     sp_nodepath_update_statusbar(nodepath);
1291 /**
1292  * Add a node nearest to point
1293  */
1294 void
1295 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1297     if (!nodepath) {
1298         return;
1299     }
1301     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1303     //find segment to split
1304     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1306     //don't know why but t seems to flip for lines
1307     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1308         position.t = 1.0 - position.t;
1309     }
1310     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1311     sp_nodepath_node_select(n, FALSE, TRUE);
1313     /* fixme: adjust ? */
1314     sp_nodepath_update_handles(nodepath);
1316     sp_nodepath_update_repr(nodepath);
1318     sp_nodepath_update_statusbar(nodepath);
1321 /*
1322  * Adjusts a segment so that t moves by a certain delta for dragging
1323  * converts lines to curves
1324  *
1325  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1326  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1327  */
1328 void
1329 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1331     /* feel good is an arbitrary parameter that distributes the delta between handles
1332      * if t of the drag point is less than 1/6 distance form the endpoint only
1333      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1334      */
1335     double feel_good;
1336     if (t <= 1.0 / 6.0)
1337         feel_good = 0;
1338     else if (t <= 0.5)
1339         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1340     else if (t <= 5.0 / 6.0)
1341         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1342     else
1343         feel_good = 1;
1345     //if we're dragging a line convert it to a curve
1346     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1347         sp_nodepath_set_line_type(e, NR_CURVETO);
1348     }
1350     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1351     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1352     e->p.other->n.pos += offsetcoord0;
1353     e->p.pos += offsetcoord1;
1355     // adjust handles of adjacent nodes where necessary
1356     sp_node_adjust_handle(e,1);
1357     sp_node_adjust_handle(e->p.other,-1);
1359     sp_nodepath_update_handles(e->subpath->nodepath);
1361     update_object(e->subpath->nodepath);
1363     sp_nodepath_update_statusbar(e->subpath->nodepath);
1367 /**
1368  * Call sp_nodepath_break() for all selected segments.
1369  */
1370 void sp_node_selected_break()
1372     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1373     if (!nodepath) return;
1375     GList *temp = NULL;
1376     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1377        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1378        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1379         if (nn == NULL) continue; // no break, no new node
1380         temp = g_list_prepend(temp, nn);
1381     }
1383     if (temp) {
1384         sp_nodepath_deselect(nodepath);
1385     }
1386     for (GList *l = temp; l != NULL; l = l->next) {
1387         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1388     }
1390     sp_nodepath_update_handles(nodepath);
1392     sp_nodepath_update_repr(nodepath);
1395 /**
1396  * Duplicate the selected node(s).
1397  */
1398 void sp_node_selected_duplicate()
1400     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1401     if (!nodepath) {
1402         return;
1403     }
1405     GList *temp = NULL;
1406     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1407        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1408        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1409         if (nn == NULL) continue; // could not duplicate
1410         temp = g_list_prepend(temp, nn);
1411     }
1413     if (temp) {
1414         sp_nodepath_deselect(nodepath);
1415     }
1416     for (GList *l = temp; l != NULL; l = l->next) {
1417         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1418     }
1420     sp_nodepath_update_handles(nodepath);
1422     sp_nodepath_update_repr(nodepath);
1425 /**
1426  *  Join two nodes by merging them into one.
1427  */
1428 void sp_node_selected_join()
1430     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1431     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1433     if (g_list_length(nodepath->selected) != 2) {
1434         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1435         return;
1436     }
1438    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1439    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1441     g_assert(a != b);
1442     g_assert(a->p.other || a->n.other);
1443     g_assert(b->p.other || b->n.other);
1445     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1446         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1447         return;
1448     }
1450     /* a and b are endpoints */
1452     NR::Point c = (a->pos + b->pos) / 2;
1454     if (a->subpath == b->subpath) {
1455        Inkscape::NodePath::SubPath *sp = a->subpath;
1456         sp_nodepath_subpath_close(sp);
1458         sp_nodepath_update_handles(sp->nodepath);
1460         sp_nodepath_update_repr(nodepath);
1462         return;
1463     }
1465     /* a and b are separate subpaths */
1466    Inkscape::NodePath::SubPath *sa = a->subpath;
1467    Inkscape::NodePath::SubPath *sb = b->subpath;
1468     NR::Point p;
1469    Inkscape::NodePath::Node *n;
1470     NRPathcode code;
1471     if (a == sa->first) {
1472         p = sa->first->n.pos;
1473         code = (NRPathcode)sa->first->n.other->code;
1474        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1475         n = sa->last;
1476         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1477         n = n->p.other;
1478         while (n) {
1479             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1480             n = n->p.other;
1481             if (n == sa->first) n = NULL;
1482         }
1483         sp_nodepath_subpath_destroy(sa);
1484         sa = t;
1485     } else if (a == sa->last) {
1486         p = sa->last->p.pos;
1487         code = (NRPathcode)sa->last->code;
1488         sp_nodepath_node_destroy(sa->last);
1489     } else {
1490         code = NR_END;
1491         g_assert_not_reached();
1492     }
1494     if (b == sb->first) {
1495         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1496         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1497             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1498         }
1499     } else if (b == sb->last) {
1500         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1501         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1502             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1503         }
1504     } else {
1505         g_assert_not_reached();
1506     }
1507     /* and now destroy sb */
1509     sp_nodepath_subpath_destroy(sb);
1511     sp_nodepath_update_handles(sa->nodepath);
1513     sp_nodepath_update_repr(nodepath);
1515     sp_nodepath_update_statusbar(nodepath);
1518 /**
1519  *  Join two nodes by adding a segment between them.
1520  */
1521 void sp_node_selected_join_segment()
1523     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1524     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1526     if (g_list_length(nodepath->selected) != 2) {
1527         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1528         return;
1529     }
1531    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1532    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1534     g_assert(a != b);
1535     g_assert(a->p.other || a->n.other);
1536     g_assert(b->p.other || b->n.other);
1538     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1539         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1540         return;
1541     }
1543     if (a->subpath == b->subpath) {
1544        Inkscape::NodePath::SubPath *sp = a->subpath;
1546         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1547         sp->closed = TRUE;
1549         sp->first->p.other = sp->last;
1550         sp->last->n.other  = sp->first;
1552         sp_node_handle_mirror_p_to_n(sp->last);
1553         sp_node_handle_mirror_n_to_p(sp->first);
1555         sp->first->code = sp->last->code;
1556         sp->first       = sp->last;
1558         sp_nodepath_update_handles(sp->nodepath);
1560         sp_nodepath_update_repr(nodepath);
1562         return;
1563     }
1565     /* a and b are separate subpaths */
1566    Inkscape::NodePath::SubPath *sa = a->subpath;
1567    Inkscape::NodePath::SubPath *sb = b->subpath;
1569    Inkscape::NodePath::Node *n;
1570     NR::Point p;
1571     NRPathcode code;
1572     if (a == sa->first) {
1573         code = (NRPathcode) sa->first->n.other->code;
1574        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1575         n = sa->last;
1576         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1577         for (n = n->p.other; n != NULL; n = n->p.other) {
1578             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1579         }
1580         sp_nodepath_subpath_destroy(sa);
1581         sa = t;
1582     } else if (a == sa->last) {
1583         code = (NRPathcode)sa->last->code;
1584     } else {
1585         code = NR_END;
1586         g_assert_not_reached();
1587     }
1589     if (b == sb->first) {
1590         n = sb->first;
1591         sp_node_handle_mirror_p_to_n(sa->last);
1592         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1593         sp_node_handle_mirror_n_to_p(sa->last);
1594         for (n = n->n.other; n != NULL; n = n->n.other) {
1595             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1596         }
1597     } else if (b == sb->last) {
1598         n = sb->last;
1599         sp_node_handle_mirror_p_to_n(sa->last);
1600         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1601         sp_node_handle_mirror_n_to_p(sa->last);
1602         for (n = n->p.other; n != NULL; n = n->p.other) {
1603             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1604         }
1605     } else {
1606         g_assert_not_reached();
1607     }
1608     /* and now destroy sb */
1610     sp_nodepath_subpath_destroy(sb);
1612     sp_nodepath_update_handles(sa->nodepath);
1614     sp_nodepath_update_repr(nodepath);
1617 /**
1618  * Delete one or more selected nodes.
1619  */
1620 void sp_node_selected_delete()
1622     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1623     if (!nodepath) return;
1624     if (!nodepath->selected) return;
1626     /** \todo fixme: do it the right way */
1627     while (nodepath->selected) {
1628        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1629         sp_nodepath_node_destroy(node);
1630     }
1633     //clean up the nodepath (such as for trivial subpaths)
1634     sp_nodepath_cleanup(nodepath);
1636     sp_nodepath_update_handles(nodepath);
1638     // if the entire nodepath is removed, delete the selected object.
1639     if (nodepath->subpaths == NULL ||
1640         sp_nodepath_get_node_count(nodepath) < 2) {
1641         SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
1642         sp_nodepath_destroy(nodepath);
1643         sp_selection_delete();
1644         sp_document_done (document);
1645         return;
1646     }
1648     sp_nodepath_update_repr(nodepath);
1650     sp_nodepath_update_statusbar(nodepath);
1653 /**
1654  * Delete one or more segments between two selected nodes.
1655  * This is the code for 'split'.
1656  */
1657 void
1658 sp_node_selected_delete_segment(void)
1660    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
1661    Inkscape::NodePath::Node *curr, *next;     //Iterators
1663     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1664     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1666     if (g_list_length(nodepath->selected) != 2) {
1667         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1668                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1669         return;
1670     }
1672     //Selected nodes, not inclusive
1673    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1674    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1676     if ( ( a==b)                       ||  //same node
1677          (a->subpath  != b->subpath )  ||  //not the same path
1678          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
1679          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
1680     {
1681         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1682                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1683         return;
1684     }
1686     //###########################################
1687     //# BEGIN EDITS
1688     //###########################################
1689     //##################################
1690     //# CLOSED PATH
1691     //##################################
1692     if (a->subpath->closed) {
1695         gboolean reversed = FALSE;
1697         //Since we can go in a circle, we need to find the shorter distance.
1698         //  a->b or b->a
1699         start = end = NULL;
1700         int distance    = 0;
1701         int minDistance = 0;
1702         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1703             if (curr==b) {
1704                 //printf("a to b:%d\n", distance);
1705                 start = a;//go from a to b
1706                 end   = b;
1707                 minDistance = distance;
1708                 //printf("A to B :\n");
1709                 break;
1710             }
1711             distance++;
1712         }
1714         //try again, the other direction
1715         distance = 0;
1716         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1717             if (curr==a) {
1718                 //printf("b to a:%d\n", distance);
1719                 if (distance < minDistance) {
1720                     start    = b;  //we go from b to a
1721                     end      = a;
1722                     reversed = TRUE;
1723                     //printf("B to A\n");
1724                 }
1725                 break;
1726             }
1727             distance++;
1728         }
1731         //Copy everything from 'end' to 'start' to a new subpath
1732        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1733         for (curr=end ; curr ; curr=curr->n.other) {
1734             NRPathcode code = (NRPathcode) curr->code;
1735             if (curr == end)
1736                 code = NR_MOVETO;
1737             sp_nodepath_node_new(t, NULL,
1738                                  (Inkscape::NodePath::NodeType)curr->type, code,
1739                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1740             if (curr == start)
1741                 break;
1742         }
1743         sp_nodepath_subpath_destroy(a->subpath);
1746     }
1750     //##################################
1751     //# OPEN PATH
1752     //##################################
1753     else {
1755         //We need to get the direction of the list between A and B
1756         //Can we walk from a to b?
1757         start = end = NULL;
1758         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1759             if (curr==b) {
1760                 start = a;  //did it!  we go from a to b
1761                 end   = b;
1762                 //printf("A to B\n");
1763                 break;
1764             }
1765         }
1766         if (!start) {//didn't work?  let's try the other direction
1767             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1768                 if (curr==a) {
1769                     start = b;  //did it!  we go from b to a
1770                     end   = a;
1771                     //printf("B to A\n");
1772                     break;
1773                 }
1774             }
1775         }
1776         if (!start) {
1777             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1778                                                      _("Cannot find path between nodes."));
1779             return;
1780         }
1784         //Copy everything after 'end' to a new subpath
1785        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1786         for (curr=end ; curr ; curr=curr->n.other) {
1787             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
1788                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1789         }
1791         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
1792         for (curr = start->n.other ; curr  ; curr=next) {
1793             next = curr->n.other;
1794             sp_nodepath_node_destroy(curr);
1795         }
1797     }
1798     //###########################################
1799     //# END EDITS
1800     //###########################################
1802     //clean up the nodepath (such as for trivial subpaths)
1803     sp_nodepath_cleanup(nodepath);
1805     sp_nodepath_update_handles(nodepath);
1807     sp_nodepath_update_repr(nodepath);
1809     // if the entire nodepath is removed, delete the selected object.
1810     if (nodepath->subpaths == NULL ||
1811         sp_nodepath_get_node_count(nodepath) < 2) {
1812         sp_nodepath_destroy(nodepath);
1813         sp_selection_delete();
1814         return;
1815     }
1817     sp_nodepath_update_statusbar(nodepath);
1820 /**
1821  * Call sp_nodepath_set_line() for all selected segments.
1822  */
1823 void
1824 sp_node_selected_set_line_type(NRPathcode code)
1826     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1827     if (nodepath == NULL) return;
1829     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1830        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1831         g_assert(n->selected);
1832         if (n->p.other && n->p.other->selected) {
1833             sp_nodepath_set_line_type(n, code);
1834         }
1835     }
1837     sp_nodepath_update_repr(nodepath);
1840 /**
1841  * Call sp_nodepath_convert_node_type() for all selected nodes.
1842  */
1843 void
1844 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
1846     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1847     if (nodepath == NULL) return;
1849     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1850         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
1851     }
1853     sp_nodepath_update_repr(nodepath);
1856 /**
1857  * Change select status of node, update its own and neighbour handles.
1858  */
1859 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
1861     node->selected = selected;
1863     if (selected) {
1864         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
1865         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
1866         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
1867         sp_knot_update_ctrl(node->knot);
1868     } else {
1869         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
1870         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
1871         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
1872         sp_knot_update_ctrl(node->knot);
1873     }
1875     sp_node_update_handles(node);
1876     if (node->n.other) sp_node_update_handles(node->n.other);
1877     if (node->p.other) sp_node_update_handles(node->p.other);
1880 /**
1881 \brief Select a node
1882 \param node     The node to select
1883 \param incremental   If true, add to selection, otherwise deselect others
1884 \param override   If true, always select this node, otherwise toggle selected status
1885 */
1886 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
1888     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
1890     if (incremental) {
1891         if (override) {
1892             if (!g_list_find(nodepath->selected, node)) {
1893                 nodepath->selected = g_list_prepend(nodepath->selected, node);
1894             }
1895             sp_node_set_selected(node, TRUE);
1896         } else { // toggle
1897             if (node->selected) {
1898                 g_assert(g_list_find(nodepath->selected, node));
1899                 nodepath->selected = g_list_remove(nodepath->selected, node);
1900             } else {
1901                 g_assert(!g_list_find(nodepath->selected, node));
1902                 nodepath->selected = g_list_prepend(nodepath->selected, node);
1903             }
1904             sp_node_set_selected(node, !node->selected);
1905         }
1906     } else {
1907         sp_nodepath_deselect(nodepath);
1908         nodepath->selected = g_list_prepend(nodepath->selected, node);
1909         sp_node_set_selected(node, TRUE);
1910     }
1912     sp_nodepath_update_statusbar(nodepath);
1916 /**
1917 \brief Deselect all nodes in the nodepath
1918 */
1919 void
1920 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
1922     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1924     while (nodepath->selected) {
1925         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
1926         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
1927     }
1928     sp_nodepath_update_statusbar(nodepath);
1931 /**
1932 \brief Select or invert selection of all nodes in the nodepath
1933 */
1934 void
1935 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
1937     if (!nodepath) return;
1939     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1940        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1941         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1942            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1943            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
1944         }
1945     }
1948 /**
1949  * If nothing selected, does the same as sp_nodepath_select_all();
1950  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
1951  * (i.e., similar to "select all in layer", with the "selected" subpaths
1952  * being treated as "layers" in the path).
1953  */
1954 void
1955 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
1957     if (!nodepath) return;
1959     if (g_list_length (nodepath->selected) == 0) {
1960         sp_nodepath_select_all (nodepath, invert);
1961         return;
1962     }
1964     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
1965     GSList *subpaths = NULL;
1967     for (GList *l = copy; l != NULL; l = l->next) {
1968         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1969         Inkscape::NodePath::SubPath *subpath = n->subpath;
1970         if (!g_slist_find (subpaths, subpath))
1971             subpaths = g_slist_prepend (subpaths, subpath);
1972     }
1974     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
1975         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
1976         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1977             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1978             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
1979         }
1980     }
1982     g_slist_free (subpaths);
1983     g_list_free (copy);
1986 /**
1987  * \brief Select the node after the last selected; if none is selected,
1988  * select the first within path.
1989  */
1990 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
1992     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1994    Inkscape::NodePath::Node *last = NULL;
1995     if (nodepath->selected) {
1996         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1997            Inkscape::NodePath::SubPath *subpath, *subpath_next;
1998             subpath = (Inkscape::NodePath::SubPath *) spl->data;
1999             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2000                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2001                 if (node->selected) {
2002                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2003                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2004                             if (spl->next) { // there's a next subpath
2005                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2006                                 last = subpath_next->first;
2007                             } else if (spl->prev) { // there's a previous subpath
2008                                 last = NULL; // to be set later to the first node of first subpath
2009                             } else {
2010                                 last = node->n.other;
2011                             }
2012                         } else {
2013                             last = node->n.other;
2014                         }
2015                     } else {
2016                         if (node->n.other) {
2017                             last = node->n.other;
2018                         } else {
2019                             if (spl->next) { // there's a next subpath
2020                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2021                                 last = subpath_next->first;
2022                             } else if (spl->prev) { // there's a previous subpath
2023                                 last = NULL; // to be set later to the first node of first subpath
2024                             } else {
2025                                 last = (Inkscape::NodePath::Node *) subpath->first;
2026                             }
2027                         }
2028                     }
2029                 }
2030             }
2031         }
2032         sp_nodepath_deselect(nodepath);
2033     }
2035     if (last) { // there's at least one more node after selected
2036         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2037     } else { // no more nodes, select the first one in first subpath
2038        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2039         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2040     }
2043 /**
2044  * \brief Select the node before the first selected; if none is selected,
2045  * select the last within path
2046  */
2047 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2049     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2051    Inkscape::NodePath::Node *last = NULL;
2052     if (nodepath->selected) {
2053         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2054            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2055             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2056                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2057                 if (node->selected) {
2058                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2059                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2060                             if (spl->prev) { // there's a prev subpath
2061                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2062                                 last = subpath_prev->last;
2063                             } else if (spl->next) { // there's a next subpath
2064                                 last = NULL; // to be set later to the last node of last subpath
2065                             } else {
2066                                 last = node->p.other;
2067                             }
2068                         } else {
2069                             last = node->p.other;
2070                         }
2071                     } else {
2072                         if (node->p.other) {
2073                             last = node->p.other;
2074                         } else {
2075                             if (spl->prev) { // there's a prev subpath
2076                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2077                                 last = subpath_prev->last;
2078                             } else if (spl->next) { // there's a next subpath
2079                                 last = NULL; // to be set later to the last node of last subpath
2080                             } else {
2081                                 last = (Inkscape::NodePath::Node *) subpath->last;
2082                             }
2083                         }
2084                     }
2085                 }
2086             }
2087         }
2088         sp_nodepath_deselect(nodepath);
2089     }
2091     if (last) { // there's at least one more node before selected
2092         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2093     } else { // no more nodes, select the last one in last subpath
2094         GList *spl = g_list_last(nodepath->subpaths);
2095        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2096         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2097     }
2100 /**
2101  * \brief Select all nodes that are within the rectangle.
2102  */
2103 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2105     if (!incremental) {
2106         sp_nodepath_deselect(nodepath);
2107     }
2109     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2110        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2111         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2112            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2114             if (b.contains(node->pos)) {
2115                 sp_nodepath_node_select(node, TRUE, TRUE);
2116             }
2117         }
2118     }
2121 /**
2122 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2123 */
2124 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2126     if (!nodepath->selected) {
2127         return NULL;
2128     }
2130     GList *r = NULL;
2131     guint i = 0;
2132     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2133        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2134         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2135            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2136             i++;
2137             if (node->selected) {
2138                 r = g_list_append(r, GINT_TO_POINTER(i));
2139             }
2140         }
2141     }
2142     return r;
2145 /**
2146 \brief  Restores selection by selecting nodes whose positions are in the list
2147 */
2148 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2150     sp_nodepath_deselect(nodepath);
2152     guint i = 0;
2153     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2154        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2155         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2156            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2157             i++;
2158             if (g_list_find(r, GINT_TO_POINTER(i))) {
2159                 sp_nodepath_node_select(node, TRUE, TRUE);
2160             }
2161         }
2162     }
2166 /**
2167 \brief Adjusts handle according to node type and line code.
2168 */
2169 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2171     double len, otherlen, linelen;
2173     g_assert(node);
2175    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2176    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2178     /** \todo fixme: */
2179     if (me->other == NULL) return;
2180     if (other->other == NULL) return;
2182     /* I have line */
2184     NRPathcode mecode, ocode;
2185     if (which_adjust == 1) {
2186         mecode = (NRPathcode)me->other->code;
2187         ocode = (NRPathcode)node->code;
2188     } else {
2189         mecode = (NRPathcode)node->code;
2190         ocode = (NRPathcode)other->other->code;
2191     }
2193     if (mecode == NR_LINETO) return;
2195     /* I am curve */
2197     if (other->other == NULL) return;
2199     /* Other has line */
2201     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2203     NR::Point delta;
2204     if (ocode == NR_LINETO) {
2205         /* other is lineto, we are either smooth or symm */
2206        Inkscape::NodePath::Node *othernode = other->other;
2207         len = NR::L2(me->pos - node->pos);
2208         delta = node->pos - othernode->pos;
2209         linelen = NR::L2(delta);
2210         if (linelen < 1e-18) 
2211             return;
2212         me->pos = node->pos + (len / linelen)*delta;
2213         return;
2214     }
2216     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2218         me->pos = 2 * node->pos - other->pos;
2219         return;
2220     }
2222     /* We are smooth */
2224     len = NR::L2(me->pos - node->pos);
2225     delta = other->pos - node->pos;
2226     otherlen = NR::L2(delta);
2227     if (otherlen < 1e-18) return;
2229     me->pos = node->pos - (len / otherlen) * delta;
2232 /**
2233  \brief Adjusts both handles according to node type and line code
2234  */
2235 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2237     g_assert(node);
2239     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2241     /* we are either smooth or symm */
2243     if (node->p.other == NULL) return;
2245     if (node->n.other == NULL) return;
2247     if (node->code == NR_LINETO) {
2248         if (node->n.other->code == NR_LINETO) return;
2249         sp_node_adjust_handle(node, 1);
2250         return;
2251     }
2253     if (node->n.other->code == NR_LINETO) {
2254         if (node->code == NR_LINETO) return;
2255         sp_node_adjust_handle(node, -1);
2256         return;
2257     }
2259     /* both are curves */
2260     NR::Point const delta( node->n.pos - node->p.pos );
2262     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2263         node->p.pos = node->pos - delta / 2;
2264         node->n.pos = node->pos + delta / 2;
2265         return;
2266     }
2268     /* We are smooth */
2269     double plen = NR::L2(node->p.pos - node->pos);
2270     if (plen < 1e-18) return;
2271     double nlen = NR::L2(node->n.pos - node->pos);
2272     if (nlen < 1e-18) return;
2273     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2274     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2277 /**
2278  * Node event callback.
2279  */
2280 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2282     gboolean ret = FALSE;
2283     switch (event->type) {
2284         case GDK_ENTER_NOTIFY:
2285             active_node = n;
2286             break;
2287         case GDK_LEAVE_NOTIFY:
2288             active_node = NULL;
2289             break;
2290         case GDK_KEY_PRESS:
2291             switch (get_group0_keyval (&event->key)) {
2292                 case GDK_space:
2293                     if (event->key.state & GDK_BUTTON1_MASK) {
2294                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2295                         stamp_repr(nodepath);
2296                         ret = TRUE;
2297                     }
2298                     break;
2299                 default:
2300                     break;
2301             }
2302             break;
2303         default:
2304             break;
2305     }
2307     return ret;
2310 /**
2311  * Handle keypress on node; directly called.
2312  */
2313 gboolean node_key(GdkEvent *event)
2315     Inkscape::NodePath::Path *np;
2317     // there is no way to verify nodes so set active_node to nil when deleting!!
2318     if (active_node == NULL) return FALSE;
2320     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2321         gint ret = FALSE;
2322         switch (get_group0_keyval (&event->key)) {
2323             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2324             case GDK_BackSpace:
2325                 np = active_node->subpath->nodepath;
2326                 sp_nodepath_node_destroy(active_node);
2327                 sp_nodepath_update_repr(np);
2328                 active_node = NULL;
2329                 ret = TRUE;
2330                 break;
2331             case GDK_c:
2332                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2333                 ret = TRUE;
2334                 break;
2335             case GDK_s:
2336                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2337                 ret = TRUE;
2338                 break;
2339             case GDK_y:
2340                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2341                 ret = TRUE;
2342                 break;
2343             case GDK_b:
2344                 sp_nodepath_node_break(active_node);
2345                 ret = TRUE;
2346                 break;
2347         }
2348         return ret;
2349     }
2350     return FALSE;
2353 /**
2354  * Mouseclick on node callback.
2355  */
2356 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2358    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2360     if (state & GDK_CONTROL_MASK) {
2361         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2363         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2364             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2365                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2366             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2367                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2368             } else {
2369                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2370             }
2371             sp_nodepath_update_repr(nodepath);
2372             sp_nodepath_update_statusbar(nodepath);
2374         } else { //ctrl+alt+click: delete node
2375             sp_nodepath_node_destroy(n);
2376             //clean up the nodepath (such as for trivial subpaths)
2377             sp_nodepath_cleanup(nodepath);
2379             // if the entire nodepath is removed, delete the selected object.
2380             if (nodepath->subpaths == NULL ||
2381                 sp_nodepath_get_node_count(nodepath) < 2) {
2382                 SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
2383                 sp_nodepath_destroy(nodepath);
2384                 sp_selection_delete();
2385                 sp_document_done (document);
2387             } else {
2388                 sp_nodepath_update_handles(nodepath);
2389                 sp_nodepath_update_repr(nodepath);
2390                 sp_nodepath_update_statusbar(nodepath);
2391             }
2392         }
2394     } else {
2395         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2396     }
2399 /**
2400  * Mouse grabbed node callback.
2401  */
2402 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2404    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2406     n->origin = knot->pos;
2408     if (!n->selected) {
2409         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2410     }
2413 /**
2414  * Mouse ungrabbed node callback.
2415  */
2416 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2418    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2420    n->dragging_out = NULL;
2422    sp_nodepath_update_repr(n->subpath->nodepath);
2425 /**
2426  * The point on a line, given by its angle, closest to the given point.
2427  * \param p  A point.
2428  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2429  * \param closest  Pointer to the point struct where the result is stored.
2430  * \todo FIXME: use dot product perhaps?
2431  */
2432 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2434     if (a == HUGE_VAL) { // vertical
2435         *closest = NR::Point(0, (*p)[NR::Y]);
2436     } else {
2437         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2438         (*closest)[NR::Y] = a * (*closest)[NR::X];
2439     }
2442 /**
2443  * Distance from the point to a line given by its angle.
2444  * \param p  A point.
2445  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2446  */
2447 static double point_line_distance(NR::Point *p, double a)
2449     NR::Point c;
2450     point_line_closest(p, a, &c);
2451     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]));
2454 /**
2455  * Callback for node "request" signal.
2456  * \todo fixme: This goes to "moved" event? (lauris)
2457  */
2458 static gboolean
2459 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2461     double yn, xn, yp, xp;
2462     double an, ap, na, pa;
2463     double d_an, d_ap, d_na, d_pa;
2464     gboolean collinear = FALSE;
2465     NR::Point c;
2466     NR::Point pr;
2468    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2470    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2471    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2473        NR::Point mouse = (*p);
2475        if (!n->dragging_out) {
2476            // This is the first drag-out event; find out which handle to drag out
2477            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2478            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2480            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2481                return FALSE;
2483            Inkscape::NodePath::NodeSide *opposite;
2484            if (appr_p > appr_n) { // closer to p
2485                n->dragging_out = &n->p;
2486                opposite = &n->n;
2487                n->code = NR_CURVETO;
2488            } else if (appr_p < appr_n) { // closer to n
2489                n->dragging_out = &n->n;
2490                opposite = &n->p;
2491                n->n.other->code = NR_CURVETO;
2492            } else { // p and n nodes are the same
2493                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2494                    n->dragging_out = &n->p;
2495                    opposite = &n->n;
2496                    n->code = NR_CURVETO;
2497                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2498                    n->dragging_out = &n->n;
2499                    opposite = &n->p;
2500                    n->n.other->code = NR_CURVETO;
2501                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2502                    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);
2503                    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);
2504                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2505                        n->dragging_out = &n->n;
2506                        opposite = &n->p;
2507                        n->n.other->code = NR_CURVETO;
2508                    } else { // closer to other's n handle
2509                        n->dragging_out = &n->p;
2510                        opposite = &n->n;
2511                        n->code = NR_CURVETO;
2512                    }
2513                }
2514            }
2516            // if there's another handle, make sure the one we drag out starts parallel to it
2517            if (opposite->pos != n->pos) {
2518                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2519            }
2521            // knots might not be created yet!
2522            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2523            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2524        }
2526        // pass this on to the handle-moved callback
2527        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2528        sp_node_update_handles(n);
2529        return TRUE;
2530    }
2532     if (state & GDK_CONTROL_MASK) { // constrained motion
2534         // calculate relative distances of handles
2535         // n handle:
2536         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2537         xn = n->n.pos[NR::X] - n->pos[NR::X];
2538         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2539         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2540             if (n->n.other) { // if there is the next point
2541                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2542                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2543                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2544             }
2545         }
2546         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2547         if (yn < 0) { xn = -xn; yn = -yn; }
2549         // p handle:
2550         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2551         xp = n->p.pos[NR::X] - n->pos[NR::X];
2552         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2553         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2554             if (n->p.other) {
2555                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2556                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2557                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2558             }
2559         }
2560         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2561         if (yp < 0) { xp = -xp; yp = -yp; }
2563         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2564             // sliding on handles, only if at least one of the handles is non-vertical
2565             // (otherwise it's the same as ctrl+drag anyway)
2567             // calculate angles of the handles
2568             if (xn == 0) {
2569                 if (yn == 0) { // no handle, consider it the continuation of the other one
2570                     an = 0;
2571                     collinear = TRUE;
2572                 }
2573                 else an = 0; // vertical; set the angle to horizontal
2574             } else an = yn/xn;
2576             if (xp == 0) {
2577                 if (yp == 0) { // no handle, consider it the continuation of the other one
2578                     ap = an;
2579                 }
2580                 else ap = 0; // vertical; set the angle to horizontal
2581             } else  ap = yp/xp;
2583             if (collinear) an = ap;
2585             // angles of the perpendiculars; HUGE_VAL means vertical
2586             if (an == 0) na = HUGE_VAL; else na = -1/an;
2587             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2589             //g_print("an %g    ap %g\n", an, ap);
2591             // mouse point relative to the node's original pos
2592             pr = (*p) - n->origin;
2594             // distances to the four lines (two handles and two perpendiculars)
2595             d_an = point_line_distance(&pr, an);
2596             d_na = point_line_distance(&pr, na);
2597             d_ap = point_line_distance(&pr, ap);
2598             d_pa = point_line_distance(&pr, pa);
2600             // find out which line is the closest, save its closest point in c
2601             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2602                 point_line_closest(&pr, an, &c);
2603             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2604                 point_line_closest(&pr, ap, &c);
2605             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2606                 point_line_closest(&pr, na, &c);
2607             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2608                 point_line_closest(&pr, pa, &c);
2609             }
2611             // move the node to the closest point
2612             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2613                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2614                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2616         } else {  // constraining to hor/vert
2618             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2619                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2620             } else { // snap to vert
2621                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2622             }
2623         }
2624     } else { // move freely
2625         sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2626                                         (*p)[NR::X] - n->pos[NR::X],
2627                                         (*p)[NR::Y] - n->pos[NR::Y],
2628                                         (state & GDK_SHIFT_MASK) == 0);
2629     }
2631     n->subpath->nodepath->desktop->scroll_to_point(p);
2633     return TRUE;
2636 /**
2637  * Node handle clicked callback.
2638  */
2639 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
2641    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2643     if (state & GDK_CONTROL_MASK) { // "delete" handle
2644         if (n->p.knot == knot) {
2645             n->p.pos = n->pos;
2646         } else if (n->n.knot == knot) {
2647             n->n.pos = n->pos;
2648         }
2649         sp_node_update_handles(n);
2650         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2651         sp_nodepath_update_repr(nodepath);
2652         sp_nodepath_update_statusbar(nodepath);
2654     } else { // just select or add to selection, depending in Shift
2655         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2656     }
2659 /**
2660  * Node handle grabbed callback.
2661  */
2662 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
2664    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2666     if (!n->selected) {
2667         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2668     }
2670     // remember the origin point of the handle
2671     if (n->p.knot == knot) {
2672         n->p.origin = n->p.pos - n->pos;
2673     } else if (n->n.knot == knot) {
2674         n->n.origin = n->n.pos - n->pos;
2675     } else {
2676         g_assert_not_reached();
2677     }
2681 /**
2682  * Node handle ungrabbed callback.
2683  */
2684 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
2686    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2688     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
2689     if (n->p.knot == knot) {
2690         n->p.origin.a = 0;
2691         sp_knot_set_position(knot, &n->p.pos, state);
2692     } else if (n->n.knot == knot) {
2693         n->n.origin.a = 0;
2694         sp_knot_set_position(knot, &n->n.pos, state);
2695     } else {
2696         g_assert_not_reached();
2697     }
2699     sp_nodepath_update_repr(n->subpath->nodepath);
2702 /**
2703  * Node handle "request" signal callback.
2704  */
2705 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2707     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2709     Inkscape::NodePath::NodeSide *me, *opposite;
2710     gint which;
2711     if (n->p.knot == knot) {
2712         me = &n->p;
2713         opposite = &n->n;
2714         which = -1;
2715     } else if (n->n.knot == knot) {
2716         me = &n->n;
2717         opposite = &n->p;
2718         which = 1;
2719     } else {
2720         me = opposite = NULL;
2721         which = 0;
2722         g_assert_not_reached();
2723     }
2725     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
2727     SnapManager const m(n->subpath->nodepath->desktop->namedview);
2729     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
2730         /* We are smooth node adjacent with line */
2731         NR::Point const delta = *p - n->pos;
2732         NR::Coord const len = NR::L2(delta);
2733         Inkscape::NodePath::Node *othernode = opposite->other;
2734         NR::Point const ndelta = n->pos - othernode->pos;
2735         NR::Coord const linelen = NR::L2(ndelta);
2736         if (len > NR_EPSILON && linelen > NR_EPSILON) {
2737             NR::Coord const scal = dot(delta, ndelta) / linelen;
2738             (*p) = n->pos + (scal / linelen) * ndelta;
2739         }
2740         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, ndelta, NULL).getPoint();
2741     } else {
2742         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
2743     }
2745     sp_node_adjust_handle(n, -which);
2747     return FALSE;
2750 /**
2751  * Node handle moved callback.
2752  */
2753 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2755    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2757    Inkscape::NodePath::NodeSide *me;
2758    Inkscape::NodePath::NodeSide *other;
2759     if (n->p.knot == knot) {
2760         me = &n->p;
2761         other = &n->n;
2762     } else if (n->n.knot == knot) {
2763         me = &n->n;
2764         other = &n->p;
2765     } else {
2766         me = NULL;
2767         other = NULL;
2768         g_assert_not_reached();
2769     }
2771     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
2772     Radial rme(me->pos - n->pos);
2773     Radial rother(other->pos - n->pos);
2774     Radial rnew(*p - n->pos);
2776     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
2777         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
2778         /* 0 interpreted as "no snapping". */
2780         // The closest PI/snaps angle, starting from zero.
2781         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
2782         if (me->origin.a == HUGE_VAL) {
2783             // ortho doesn't exist: original handle was zero length.
2784             rnew.a = a_snapped;
2785         } else {
2786             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
2787              * its opposite and perpendiculars). */
2788             double const a_ortho = me->origin.a + floor((rnew.a - me->origin.a)/(M_PI/2) + 0.5) * (M_PI/2);
2790             // Snap to the closest.
2791             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
2792                        ? a_snapped
2793                        : a_ortho );
2794         }
2795     }
2797     if (state & GDK_MOD1_MASK) {
2798         // lock handle length
2799         rnew.r = me->origin.r;
2800     }
2802     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
2803         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
2804         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
2805         rother.a += rnew.a - rme.a;
2806         other->pos = NR::Point(rother) + n->pos;
2807         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
2808         sp_knot_set_position(other->knot, &other->pos, 0);
2809     }
2811     me->pos = NR::Point(rnew) + n->pos;
2812     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
2814     // this is what sp_knot_set_position does, but without emitting the signal:
2815     // we cannot emit a "moved" signal because we're now processing it
2816     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
2818     knot->desktop->set_coordinate_status(me->pos);
2820     update_object(n->subpath->nodepath);
2822     /* status text */
2823     SPDesktop *desktop = n->subpath->nodepath->desktop;
2824     if (!desktop) return;
2825     SPEventContext *ec = desktop->event_context;
2826     if (!ec) return;
2827     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
2828     if (!mc) return;
2830     double degrees = 180 / M_PI * rnew.a;
2831     if (degrees > 180) degrees -= 360;
2832     if (degrees < -180) degrees += 360;
2833     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
2834         degrees = angle_to_compass (degrees);
2836     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
2838     mc->setF(Inkscape::NORMAL_MESSAGE,
2839          _("<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);
2841     g_string_free(length, TRUE);
2844 /**
2845  * Node handle event callback.
2846  */
2847 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2849     gboolean ret = FALSE;
2850     switch (event->type) {
2851         case GDK_KEY_PRESS:
2852             switch (get_group0_keyval (&event->key)) {
2853                 case GDK_space:
2854                     if (event->key.state & GDK_BUTTON1_MASK) {
2855                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2856                         stamp_repr(nodepath);
2857                         ret = TRUE;
2858                     }
2859                     break;
2860                 default:
2861                     break;
2862             }
2863             break;
2864         default:
2865             break;
2866     }
2868     return ret;
2871 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
2872                                  Radial &rme, Radial &rother, gboolean const both)
2874     rme.a += angle;
2875     if ( both
2876          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2877          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2878     {
2879         rother.a += angle;
2880     }
2883 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
2884                                         Radial &rme, Radial &rother, gboolean const both)
2886     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
2888     gdouble r;
2889     if ( both
2890          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2891          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2892     {
2893         r = MAX(rme.r, rother.r);
2894     } else {
2895         r = rme.r;
2896     }
2898     gdouble const weird_angle = atan2(norm_angle, r);
2899 /* Bulia says norm_angle is just the visible distance that the
2900  * object's end must travel on the screen.  Left as 'angle' for want of
2901  * a better name.*/
2903     rme.a += weird_angle;
2904     if ( both
2905          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2906          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2907     {
2908         rother.a += weird_angle;
2909     }
2912 /**
2913  * Rotate one node.
2914  */
2915 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
2917     Inkscape::NodePath::NodeSide *me, *other;
2918     bool both = false;
2920     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
2921     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
2923     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
2924         me = &(n->p);
2925         other = &(n->n);
2926     } else if (!n->p.other) {
2927         me = &(n->n);
2928         other = &(n->p);
2929     } else {
2930         if (which > 0) { // right handle
2931             if (xn > xp) {
2932                 me = &(n->n);
2933                 other = &(n->p);
2934             } else {
2935                 me = &(n->p);
2936                 other = &(n->n);
2937             }
2938         } else if (which < 0){ // left handle
2939             if (xn <= xp) {
2940                 me = &(n->n);
2941                 other = &(n->p);
2942             } else {
2943                 me = &(n->p);
2944                 other = &(n->n);
2945             }
2946         } else { // both handles
2947             me = &(n->n);
2948             other = &(n->p);
2949             both = true;
2950         }
2951     }
2953     Radial rme(me->pos - n->pos);
2954     Radial rother(other->pos - n->pos);
2956     if (screen) {
2957         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
2958     } else {
2959         node_rotate_one_internal (*n, angle, rme, rother, both);
2960     }
2962     me->pos = n->pos + NR::Point(rme);
2964     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
2965         other->pos =  n->pos + NR::Point(rother);
2966     }
2968     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
2969     // so here we just move all the knots without emitting move signals, for speed
2970     sp_node_update_handles(n, false);
2973 /**
2974  * Rotate selected nodes.
2975  */
2976 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
2978     if (!nodepath || !nodepath->selected) return;
2980     if (g_list_length(nodepath->selected) == 1) {
2981        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
2982         node_rotate_one (n, angle, which, screen);
2983     } else {
2984        // rotate as an object:
2986         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
2987         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
2988         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2989             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2990             box.expandTo (n->pos); // contain all selected nodes
2991         }
2993         gdouble rot;
2994         if (screen) {
2995             gdouble const zoom = nodepath->desktop->current_zoom();
2996             gdouble const zmove = angle / zoom;
2997             gdouble const r = NR::L2(box.max() - box.midpoint());
2998             rot = atan2(zmove, r);
2999         } else {
3000             rot = angle;
3001         }
3003         NR::Matrix t =
3004             NR::Matrix (NR::translate(-box.midpoint())) *
3005             NR::Matrix (NR::rotate(rot)) *
3006             NR::Matrix (NR::translate(box.midpoint()));
3008         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3009             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3010             n->pos *= t;
3011             n->n.pos *= t;
3012             n->p.pos *= t;
3013             sp_node_update_handles(n, false);
3014         }
3015     }
3017     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3020 /**
3021  * Scale one node.
3022  */
3023 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3025     bool both = false;
3026     Inkscape::NodePath::NodeSide *me, *other;
3028     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3029     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3031     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3032         me = &(n->p);
3033         other = &(n->n);
3034         n->code = NR_CURVETO;
3035     } else if (!n->p.other) {
3036         me = &(n->n);
3037         other = &(n->p);
3038         if (n->n.other)
3039             n->n.other->code = NR_CURVETO;
3040     } else {
3041         if (which > 0) { // right handle
3042             if (xn > xp) {
3043                 me = &(n->n);
3044                 other = &(n->p);
3045                 if (n->n.other)
3046                     n->n.other->code = NR_CURVETO;
3047             } else {
3048                 me = &(n->p);
3049                 other = &(n->n);
3050                 n->code = NR_CURVETO;
3051             }
3052         } else if (which < 0){ // left handle
3053             if (xn <= xp) {
3054                 me = &(n->n);
3055                 other = &(n->p);
3056                 if (n->n.other)
3057                     n->n.other->code = NR_CURVETO;
3058             } else {
3059                 me = &(n->p);
3060                 other = &(n->n);
3061                 n->code = NR_CURVETO;
3062             }
3063         } else { // both handles
3064             me = &(n->n);
3065             other = &(n->p);
3066             both = true;
3067             n->code = NR_CURVETO;
3068             if (n->n.other)
3069                 n->n.other->code = NR_CURVETO;
3070         }
3071     }
3073     Radial rme(me->pos - n->pos);
3074     Radial rother(other->pos - n->pos);
3076     rme.r += grow;
3077     if (rme.r < 0) rme.r = 0;
3078     if (rme.a == HUGE_VAL) {
3079         if (me->other) { // if direction is unknown, initialize it towards the next node
3080             Radial rme_next(me->other->pos - n->pos);
3081             rme.a = rme_next.a;
3082         } else { // if there's no next, initialize to 0
3083             rme.a = 0;
3084         }
3085     }
3086     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3087         rother.r += grow;
3088         if (rother.r < 0) rother.r = 0;
3089         if (rother.a == HUGE_VAL) {
3090             rother.a = rme.a + M_PI;
3091         }
3092     }
3094     me->pos = n->pos + NR::Point(rme);
3096     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3097         other->pos = n->pos + NR::Point(rother);
3098     }
3100     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3101     // so here we just move all the knots without emitting move signals, for speed
3102     sp_node_update_handles(n, false);
3105 /**
3106  * Scale selected nodes.
3107  */
3108 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3110     if (!nodepath || !nodepath->selected) return;
3112     if (g_list_length(nodepath->selected) == 1) {
3113         // scale handles of the single selected node
3114         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3115         node_scale_one (n, grow, which);
3116     } else {
3117         // scale nodes as an "object":
3119         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3120         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3121         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3122             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3123             box.expandTo (n->pos); // contain all selected nodes
3124         }
3126         double scale = (box.maxExtent() + grow)/box.maxExtent();
3128         NR::Matrix t =
3129             NR::Matrix (NR::translate(-box.midpoint())) *
3130             NR::Matrix (NR::scale(scale, scale)) *
3131             NR::Matrix (NR::translate(box.midpoint()));
3133         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3134             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3135             n->pos *= t;
3136             n->n.pos *= t;
3137             n->p.pos *= t;
3138             sp_node_update_handles(n, false);
3139         }
3140     }
3142     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3145 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3147     if (!nodepath) return;
3148     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3151 /**
3152  * Flip selected nodes horizontally/vertically.
3153  */
3154 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3156     if (!nodepath || !nodepath->selected) return;
3158     if (g_list_length(nodepath->selected) == 1) {
3159         // flip handles of the single selected node
3160         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3161         double temp = n->p.pos[axis];
3162         n->p.pos[axis] = n->n.pos[axis];
3163         n->n.pos[axis] = temp;
3164         sp_node_update_handles(n, false);
3165     } else {
3166         // scale nodes as an "object":
3168         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3169         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3170         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3171             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3172             box.expandTo (n->pos); // contain all selected nodes
3173         }
3175         NR::Matrix t =
3176             NR::Matrix (NR::translate(-box.midpoint())) *
3177             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3178             NR::Matrix (NR::translate(box.midpoint()));
3180         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3181             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3182             n->pos *= t;
3183             n->n.pos *= t;
3184             n->p.pos *= t;
3185             sp_node_update_handles(n, false);
3186         }
3187     }
3189     sp_nodepath_update_repr(nodepath);
3192 //-----------------------------------------------
3193 /**
3194  * Return new subpath under given nodepath.
3195  */
3196 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3198     g_assert(nodepath);
3199     g_assert(nodepath->desktop);
3201    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3203     s->nodepath = nodepath;
3204     s->closed = FALSE;
3205     s->nodes = NULL;
3206     s->first = NULL;
3207     s->last = NULL;
3209     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3210     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3211     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3213     return s;
3216 /**
3217  * Destroy nodes in subpath, then subpath itself.
3218  */
3219 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3221     g_assert(subpath);
3222     g_assert(subpath->nodepath);
3223     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3225     while (subpath->nodes) {
3226         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3227     }
3229     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3231     g_free(subpath);
3234 /**
3235  * Link head to tail in subpath.
3236  */
3237 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3239     g_assert(!sp->closed);
3240     g_assert(sp->last != sp->first);
3241     g_assert(sp->first->code == NR_MOVETO);
3243     sp->closed = TRUE;
3245     //Link the head to the tail
3246     sp->first->p.other = sp->last;
3247     sp->last->n.other  = sp->first;
3248     sp->last->n.pos    = sp->first->n.pos;
3249     sp->first          = sp->last;
3251     //Remove the extra end node
3252     sp_nodepath_node_destroy(sp->last->n.other);
3255 /**
3256  * Open closed (loopy) subpath at node.
3257  */
3258 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3260     g_assert(sp->closed);
3261     g_assert(n->subpath == sp);
3262     g_assert(sp->first == sp->last);
3264     /* We create new startpoint, current node will become last one */
3266    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3267                                                 &n->pos, &n->pos, &n->n.pos);
3270     sp->closed        = FALSE;
3272     //Unlink to make a head and tail
3273     sp->first         = new_path;
3274     sp->last          = n;
3275     n->n.other        = NULL;
3276     new_path->p.other = NULL;
3279 /**
3280  * Returns area in triangle given by points; may be negative.
3281  */
3282 inline double
3283 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3285     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]);
3288 /**
3289  * Return new node in subpath with given properties.
3290  * \param pos Position of node.
3291  * \param ppos Handle position in previous direction
3292  * \param npos Handle position in previous direction
3293  */
3294 Inkscape::NodePath::Node *
3295 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)
3297     g_assert(sp);
3298     g_assert(sp->nodepath);
3299     g_assert(sp->nodepath->desktop);
3301     if (nodechunk == NULL)
3302         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3304     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3306     n->subpath  = sp;
3308     if (type != Inkscape::NodePath::NODE_NONE) {
3309         // use the type from sodipodi:nodetypes
3310         n->type = type;
3311     } else {
3312         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3313             // points are (almost) collinear
3314             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3315                 // endnode, or a node with a retracted handle
3316                 n->type = Inkscape::NodePath::NODE_CUSP;
3317             } else {
3318                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3319             }
3320         } else {
3321             n->type = Inkscape::NodePath::NODE_CUSP;
3322         }
3323     }
3325     n->code     = code;
3326     n->selected = FALSE;
3327     n->pos      = *pos;
3328     n->p.pos    = *ppos;
3329     n->n.pos    = *npos;
3331     n->dragging_out = NULL;
3333     Inkscape::NodePath::Node *prev;
3334     if (next) {
3335         //g_assert(g_list_find(sp->nodes, next));
3336         prev = next->p.other;
3337     } else {
3338         prev = sp->last;
3339     }
3341     if (prev)
3342         prev->n.other = n;
3343     else
3344         sp->first = n;
3346     if (next)
3347         next->p.other = n;
3348     else
3349         sp->last = n;
3351     n->p.other = prev;
3352     n->n.other = next;
3354     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"));
3355     sp_knot_set_position(n->knot, pos, 0);
3357     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3358     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3359     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3360     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3361     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3362     sp_knot_update_ctrl(n->knot);
3364     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3365     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3366     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3367     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3368     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3369     sp_knot_show(n->knot);
3371     // We only create handle knots and lines on demand
3372     n->p.knot = NULL;
3373     n->p.line = NULL;
3374     n->n.knot = NULL;
3375     n->n.line = NULL;
3377     sp->nodes = g_list_prepend(sp->nodes, n);
3379     return n;
3382 /**
3383  * Destroy node and its knots, link neighbors in subpath.
3384  */
3385 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3387     g_assert(node);
3388     g_assert(node->subpath);
3389     g_assert(SP_IS_KNOT(node->knot));
3391    Inkscape::NodePath::SubPath *sp = node->subpath;
3393     if (node->selected) { // first, deselect
3394         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3395         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3396     }
3398     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3400     g_object_unref(G_OBJECT(node->knot));
3401     if (node->p.knot)
3402         g_object_unref(G_OBJECT(node->p.knot));
3403     if (node->n.knot)
3404         g_object_unref(G_OBJECT(node->n.knot));
3406     if (node->p.line)
3407         gtk_object_destroy(GTK_OBJECT(node->p.line));
3408     if (node->n.line)
3409         gtk_object_destroy(GTK_OBJECT(node->n.line));
3411     if (sp->nodes) { // there are others nodes on the subpath
3412         if (sp->closed) {
3413             if (sp->first == node) {
3414                 g_assert(sp->last == node);
3415                 sp->first = node->n.other;
3416                 sp->last = sp->first;
3417             }
3418             node->p.other->n.other = node->n.other;
3419             node->n.other->p.other = node->p.other;
3420         } else {
3421             if (sp->first == node) {
3422                 sp->first = node->n.other;
3423                 sp->first->code = NR_MOVETO;
3424             }
3425             if (sp->last == node) sp->last = node->p.other;
3426             if (node->p.other) node->p.other->n.other = node->n.other;
3427             if (node->n.other) node->n.other->p.other = node->p.other;
3428         }
3429     } else { // this was the last node on subpath
3430         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3431     }
3433     g_mem_chunk_free(nodechunk, node);
3436 /**
3437  * Returns one of the node's two sides.
3438  * \param which Indicates which side.
3439  * \return Pointer to previous node side if which==-1, next if which==1.
3440  */
3441 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3443     g_assert(node);
3445     switch (which) {
3446         case -1:
3447             return &node->p;
3448         case 1:
3449             return &node->n;
3450         default:
3451             break;
3452     }
3454     g_assert_not_reached();
3456     return NULL;
3459 /**
3460  * Return the other side of the node, given one of its sides.
3461  */
3462 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
3464     g_assert(node);
3466     if (me == &node->p) return &node->n;
3467     if (me == &node->n) return &node->p;
3469     g_assert_not_reached();
3471     return NULL;
3474 /**
3475  * Return NRPathcode on the given side of the node.
3476  */
3477 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3479     g_assert(node);
3481     if (me == &node->p) {
3482         if (node->p.other) return (NRPathcode)node->code;
3483         return NR_MOVETO;
3484     }
3486     if (me == &node->n) {
3487         if (node->n.other) return (NRPathcode)node->n.other->code;
3488         return NR_MOVETO;
3489     }
3491     g_assert_not_reached();
3493     return NR_END;
3496 /**
3497  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3498  */
3499 Inkscape::NodePath::Node *
3500 sp_nodepath_get_node_by_index(int index)
3502     Inkscape::NodePath::Node *e = NULL;
3504     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3505     if (!nodepath) {
3506         return e;
3507     }
3509     //find segment
3510     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3512         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3513         int n = g_list_length(sp->nodes);
3514         if (sp->closed) {
3515             n++;
3516         }
3518         //if the piece belongs to this subpath grab it
3519         //otherwise move onto the next subpath
3520         if (index < n) {
3521             e = sp->first;
3522             for (int i = 0; i < index; ++i) {
3523                 e = e->n.other;
3524             }
3525             break;
3526         } else {
3527             if (sp->closed) {
3528                 index -= (n+1);
3529             } else {
3530                 index -= n;
3531             }
3532         }
3533     }
3535     return e;
3538 /**
3539  * Returns plain text meaning of node type.
3540  */
3541 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3543     unsigned retracted = 0;
3544     bool endnode = false;
3546     for (int which = -1; which <= 1; which += 2) {
3547         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3548         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3549             retracted ++;
3550         if (!side->other)
3551             endnode = true;
3552     }
3554     if (retracted == 0) {
3555         if (endnode) {
3556                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3557                 return _("end node");
3558         } else {
3559             switch (node->type) {
3560                 case Inkscape::NodePath::NODE_CUSP:
3561                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3562                     return _("cusp");
3563                 case Inkscape::NodePath::NODE_SMOOTH:
3564                     // TRANSLATORS: "smooth" is an adjective here
3565                     return _("smooth");
3566                 case Inkscape::NodePath::NODE_SYMM:
3567                     return _("symmetric");
3568             }
3569         }
3570     } else if (retracted == 1) {
3571         if (endnode) {
3572             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3573             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3574         } else {
3575             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3576         }
3577     } else {
3578         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3579     }
3581     return NULL;
3584 /**
3585  * Handles content of statusbar as long as node tool is active.
3586  */
3587 void
3588 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3590     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>arrow</b> keys to move nodes");
3591     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3593     gint total = 0;
3594     gint selected = 0;
3595     SPDesktop *desktop = NULL;
3597     if (nodepath) {
3598         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3599             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3600             total += g_list_length(subpath->nodes);
3601         }
3602         selected = g_list_length(nodepath->selected);
3603         desktop = nodepath->desktop;
3604     } else {
3605         desktop = SP_ACTIVE_DESKTOP;
3606     }
3608     SPEventContext *ec = desktop->event_context;
3609     if (!ec) return;
3610     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3611     if (!mc) return;
3613     if (selected == 0) {
3614         Inkscape::Selection *sel = desktop->selection;
3615         if (!sel || sel->isEmpty()) {
3616             mc->setF(Inkscape::NORMAL_MESSAGE,
3617                      _("Select a single object to edit its nodes or handles."));
3618         } else {
3619             if (nodepath) {
3620             mc->setF(Inkscape::NORMAL_MESSAGE,
3621                      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.",
3622                               "<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.",
3623                               total),
3624                      total);
3625             } else {
3626                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3627                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3628                 } else {
3629                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3630                 }
3631             }
3632         }
3633     } else if (nodepath && selected == 1) {
3634         mc->setF(Inkscape::NORMAL_MESSAGE,
3635                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3636                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3637                           total),
3638                  selected, total, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3639     } else {
3640         mc->setF(Inkscape::NORMAL_MESSAGE,
3641                  ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
3642                           "<b>%i</b> of <b>%i</b> nodes selected. %s.",
3643                           total),
3644                  selected, total, when_selected);
3645     }
3649 /*
3650   Local Variables:
3651   mode:c++
3652   c-file-style:"stroustrup"
3653   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3654   indent-tabs-mode:nil
3655   fill-column:99
3656   End:
3657 */
3658 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :