Code

moving trunk for module inkscape
[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_ensure_ctrls(Inkscape::NodePath::Node *node);
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 /* Control knot placement, if node or other knot is moved */
99 static void sp_node_adjust_knot(Inkscape::NodePath::Node *node, gint which_adjust);
100 static void sp_node_adjust_knots(Inkscape::NodePath::Node *node);
102 /* Knot event handlers */
104 static void node_clicked(SPKnot *knot, guint state, gpointer data);
105 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
106 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
107 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
108 static void node_ctrl_clicked(SPKnot *knot, guint state, gpointer data);
109 static void node_ctrl_grabbed(SPKnot *knot, guint state, gpointer data);
110 static void node_ctrl_ungrabbed(SPKnot *knot, guint state, gpointer data);
111 static gboolean node_ctrl_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
112 static void node_ctrl_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
114 /* Constructors and destructors */
116 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
117 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
118 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
119 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
120 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
121                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
122 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
124 /* Helpers */
126 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
127 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
128 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
130 // active_node indicates mouseover node
131 static Inkscape::NodePath::Node *active_node = NULL;
133 /**
134  * \brief Creates new nodepath from item 
135  */
136 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item)
138     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
140     /** \todo
141      * FIXME: remove this. We don't want to edit paths inside flowtext.
142      * Instead we will build our flowtext with cloned paths, so that the
143      * real paths are outside the flowtext and thus editable as usual.
144      */
145     if (SP_IS_FLOWTEXT(item)) {
146         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
147             if SP_IS_FLOWREGION(child) {
148                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
149                 if (grandchild && SP_IS_PATH(grandchild)) {
150                     item = SP_ITEM(grandchild);
151                     break;
152                 }
153             }
154         }
155     }
157     if (!SP_IS_PATH(item))
158         return NULL;
159     SPPath *path = SP_PATH(item);
160     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
161     if (curve == NULL)
162         return NULL;
164     NArtBpath *bpath = sp_curve_first_bpath(curve);
165     gint length = curve->end;
166     if (length == 0)
167         return NULL; // prevent crash for one-node paths
169     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
170     gchar *typestr = parse_nodetypes(nodetypes, length);
172     //Create new nodepath
173     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
174     if (!np)
175         return NULL;
177     // Set defaults
178     np->desktop     = desktop;
179     np->path        = path;
180     np->subpaths    = NULL;
181     np->selected    = NULL;
182     np->nodeContext = NULL; //Let the context that makes this set it
184     // we need to update item's transform from the repr here,
185     // because they may be out of sync when we respond 
186     // to a change in repr by regenerating nodepath     --bb
187     sp_object_read_attr(SP_OBJECT(item), "transform");
189     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
190     np->d2i  = np->i2d.inverse();
191     np->repr = repr;
193     /* Now the bitchy part (lauris) */
195     NArtBpath *b = bpath;
197     while (b->code != NR_END) {
198         b = subpath_from_bpath(np, b, typestr + (b - bpath));
199     }
201     g_free(typestr);
202     sp_curve_unref(curve);
204     return np;
207 /**
208  * Destroys nodepath's subpaths, then itself, also tell context about it.
209  */
210 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
212     if (!np)  //soft fail, like delete
213         return;
215     while (np->subpaths) {
216         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
217     }
219     //Inform the context that made me, if any, that I am gone.
220     if (np->nodeContext)
221         np->nodeContext->nodepath = NULL;
223     g_assert(!np->selected);
225     np->desktop = NULL;
227     g_free(np);
231 /**
232  *  Return the node count of a given NodeSubPath.
233  */
234 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
236     if (!subpath)
237         return 0;
238     gint nodeCount = g_list_length(subpath->nodes);
239     return nodeCount;
242 /**
243  *  Return the node count of a given NodePath.
244  */
245 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
247     if (!np)
248         return 0;
249     gint nodeCount = 0;
250     for (GList *item = np->subpaths ; item ; item=item->next) {
251        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
252         nodeCount += g_list_length(subpath->nodes);
253     }
254     return nodeCount;
258 /**
259  * Clean up a nodepath after editing.
260  * 
261  * Currently we are deleting trivial subpaths.
262  */
263 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
265     GList *badSubPaths = NULL;
267     //Check all subpaths to be >=2 nodes
268     for (GList *l = nodepath->subpaths; l ; l=l->next) {
269        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
270         if (sp_nodepath_subpath_get_node_count(sp)<2)
271             badSubPaths = g_list_append(badSubPaths, sp);
272     }
274     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
275     //also removes the subpath from nodepath->subpaths
276     for (GList *l = badSubPaths; l ; l=l->next) {
277        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
278         sp_nodepath_subpath_destroy(sp);
279     }
281     g_list_free(badSubPaths);
286 /**
287  * \brief Returns true if the argument nodepath and the d attribute in 
288  * its repr do not match. 
289  *
290  * This may happen if repr was changed in, e.g., XML editor or by undo. 
291  * 
292  * \todo
293  * UGLY HACK, think how we can eliminate it.
294  */
295 gboolean nodepath_repr_d_changed(Inkscape::NodePath::Path *np, char const *newd)
297     g_assert(np);
299     SPCurve *curve = create_curve(np);
301     gchar *svgpath = sp_svg_write_path(curve->bpath);
303     char const *attr_d = ( newd
304                            ? newd
305                            : SP_OBJECT(np->path)->repr->attribute("d") );
307     gboolean ret;
308     if (attr_d && svgpath)
309         ret = strcmp(attr_d, svgpath);
310     else 
311         ret = TRUE;
313     g_free(svgpath);
314     sp_curve_unref(curve);
316     return ret;
319 /**
320  * \brief Returns true if the argument nodepath and the sodipodi:nodetypes 
321  * attribute in its repr do not match. 
322  *
323  * This may happen if repr was changed in, e.g., the XML editor or by undo.
324  */
325 gboolean nodepath_repr_typestr_changed(Inkscape::NodePath::Path *np, char const *newtypestr)
327     g_assert(np);
328     gchar *typestr = create_typestr(np);
329     char const *attr_typestr = ( newtypestr
330                                  ? newtypestr
331                                  : SP_OBJECT(np->path)->repr->attribute("sodipodi:nodetypes") );
332     gboolean const ret = (attr_typestr && strcmp(attr_typestr, typestr));
334     g_free(typestr);
336     return ret;
339 /**
340  * Create new nodepath from b, make it subpath of np.
341  * \param t The node type.
342  * \todo Fixme: t should be a proper type, rather than gchar
343  */
344 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
346     NR::Point ppos, pos, npos;
348     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
350     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
351     bool const closed = (b->code == NR_MOVETO);
353     pos = NR::Point(b->x3, b->y3) * np->i2d;
354     if (b[1].code == NR_CURVETO) {
355         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
356     } else {
357         npos = pos;
358     }
359     Inkscape::NodePath::Node *n;
360     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
361     g_assert(sp->first == n);
362     g_assert(sp->last  == n);
364     b++;
365     t++;
366     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
367         pos = NR::Point(b->x3, b->y3) * np->i2d;
368         if (b->code == NR_CURVETO) {
369             ppos = NR::Point(b->x2, b->y2) * np->i2d;
370         } else {
371             ppos = pos;
372         }
373         if (b[1].code == NR_CURVETO) {
374             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
375         } else {
376             npos = pos;
377         }
378         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
379         b++;
380         t++;
381     }
383     if (closed) sp_nodepath_subpath_close(sp);
385     return b;
388 /**
389  * Convert from sodipodi:nodetypes to new style type string.
390  */
391 static gchar *parse_nodetypes(gchar const *types, gint length)
393     g_assert(length > 0);
395     gchar *typestr = g_new(gchar, length + 1);
397     gint pos = 0;
399     if (types) {
400         for (gint i = 0; types[i] && ( i < length ); i++) {
401             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
402             if (types[i] != '\0') {
403                 switch (types[i]) {
404                     case 's':
405                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
406                         break;
407                     case 'z':
408                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
409                         break;
410                     case 'c':
411                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
412                         break;
413                     default:
414                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
415                         break;
416                 }
417             }
418         }
419     }
421     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
423     return typestr;
426 /**
427  * Make curve out of path and associate it with it.
428  */
429 static void update_object(Inkscape::NodePath::Path *np)
431     g_assert(np);
433     SPCurve *curve = create_curve(np);
435     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
437     sp_curve_unref(curve);
440 /**
441  * Update XML path node with data from path object.
442  */
443 static void update_repr_internal(Inkscape::NodePath::Path *np)
445     g_assert(np);
447     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
449     SPCurve *curve = create_curve(np);
450     gchar *typestr = create_typestr(np);
451     gchar *svgpath = sp_svg_write_path(curve->bpath);
453     repr->setAttribute("d", svgpath);
454     repr->setAttribute("sodipodi:nodetypes", typestr);
456     g_free(svgpath);
457     g_free(typestr);
458     sp_curve_unref(curve);
461 /**
462  * Update XML path node with data from path object, commit changes forever.
463  */
464 static void update_repr(Inkscape::NodePath::Path *np)
466     update_repr_internal(np);
467     sp_document_done(SP_DT_DOCUMENT(np->desktop));
470 /**
471  * Update XML path node with data from path object, commit changes with undo.
472  */
473 static void update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key)
475     update_repr_internal(np);
476     sp_document_maybe_done(SP_DT_DOCUMENT(np->desktop), key);
479 /**
480  * Make duplicate of path, replace corresponding XML node in tree, commit.
481  */
482 static void stamp_repr(Inkscape::NodePath::Path *np)
484     g_assert(np);
486     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
487     Inkscape::XML::Node *new_repr = old_repr->duplicate();
489     // remember the position of the item
490     gint pos = old_repr->position();
491     // remember parent
492     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
494     SPCurve *curve = create_curve(np);
495     gchar *typestr = create_typestr(np);
497     gchar *svgpath = sp_svg_write_path(curve->bpath);
499     new_repr->setAttribute("d", svgpath);
500     new_repr->setAttribute("sodipodi:nodetypes", typestr);
502     // add the new repr to the parent
503     parent->appendChild(new_repr);
504     // move to the saved position
505     new_repr->setPosition(pos > 0 ? pos : 0);
507     sp_document_done(SP_DT_DOCUMENT(np->desktop));
509     Inkscape::GC::release(new_repr);
510     g_free(svgpath);
511     g_free(typestr);
512     sp_curve_unref(curve);
515 /**
516  * Create curve from path.
517  */
518 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
520     SPCurve *curve = sp_curve_new();
522     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
523        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
524         sp_curve_moveto(curve,
525                         sp->first->pos * np->d2i);
526        Inkscape::NodePath::Node *n = sp->first->n.other;
527         while (n) {
528             NR::Point const end_pt = n->pos * np->d2i;
529             switch (n->code) {
530                 case NR_LINETO:
531                     sp_curve_lineto(curve, end_pt);
532                     break;
533                 case NR_CURVETO:
534                     sp_curve_curveto(curve,
535                                      n->p.other->n.pos * np->d2i,
536                                      n->p.pos * np->d2i,
537                                      end_pt);
538                     break;
539                 default:
540                     g_assert_not_reached();
541                     break;
542             }
543             if (n != sp->last) {
544                 n = n->n.other;
545             } else {
546                 n = NULL;
547             }
548         }
549         if (sp->closed) {
550             sp_curve_closepath(curve);
551         }
552     }
554     return curve;
557 /**
558  * Convert path type string to sodipodi:nodetypes style.
559  */
560 static gchar *create_typestr(Inkscape::NodePath::Path *np)
562     gchar *typestr = g_new(gchar, 32);
563     gint len = 32;
564     gint pos = 0;
566     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
567        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
569         if (pos >= len) {
570             typestr = g_renew(gchar, typestr, len + 32);
571             len += 32;
572         }
574         typestr[pos++] = 'c';
576        Inkscape::NodePath::Node *n;
577         n = sp->first->n.other;
578         while (n) {
579             gchar code;
581             switch (n->type) {
582                 case Inkscape::NodePath::NODE_CUSP:
583                     code = 'c';
584                     break;
585                 case Inkscape::NodePath::NODE_SMOOTH:
586                     code = 's';
587                     break;
588                 case Inkscape::NodePath::NODE_SYMM:
589                     code = 'z';
590                     break;
591                 default:
592                     g_assert_not_reached();
593                     code = '\0';
594                     break;
595             }
597             if (pos >= len) {
598                 typestr = g_renew(gchar, typestr, len + 32);
599                 len += 32;
600             }
602             typestr[pos++] = code;
604             if (n != sp->last) {
605                 n = n->n.other;
606             } else {
607                 n = NULL;
608             }
609         }
610     }
612     if (pos >= len) {
613         typestr = g_renew(gchar, typestr, len + 1);
614         len += 1;
615     }
617     typestr[pos++] = '\0';
619     return typestr;
622 /**
623  * Returns current path in context.
624  */
625 static Inkscape::NodePath::Path *sp_nodepath_current()
627     if (!SP_ACTIVE_DESKTOP) {
628         return NULL;
629     }
631     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
633     if (!SP_IS_NODE_CONTEXT(event_context)) {
634         return NULL;
635     }
637     return SP_NODE_CONTEXT(event_context)->nodepath;
642 /**
643  \brief Fills node and control positions for three nodes, splitting line
644   marked by end at distance t.
645  */
646 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
648     g_assert(new_path != NULL);
649     g_assert(end      != NULL);
651     g_assert(end->p.other == new_path);
652    Inkscape::NodePath::Node *start = new_path->p.other;
653     g_assert(start);
655     if (end->code == NR_LINETO) {
656         new_path->type =Inkscape::NodePath::NODE_CUSP;
657         new_path->code = NR_LINETO;
658         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
659     } else {
660         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
661         new_path->code = NR_CURVETO;
662         gdouble s      = 1 - t;
663         for (int dim = 0; dim < 2; dim++) {
664             NR::Coord const f000 = start->pos[dim];
665             NR::Coord const f001 = start->n.pos[dim];
666             NR::Coord const f011 = end->p.pos[dim];
667             NR::Coord const f111 = end->pos[dim];
668             NR::Coord const f00t = s * f000 + t * f001;
669             NR::Coord const f01t = s * f001 + t * f011;
670             NR::Coord const f11t = s * f011 + t * f111;
671             NR::Coord const f0tt = s * f00t + t * f01t;
672             NR::Coord const f1tt = s * f01t + t * f11t;
673             NR::Coord const fttt = s * f0tt + t * f1tt;
674             start->n.pos[dim]    = f00t;
675             new_path->p.pos[dim] = f0tt;
676             new_path->pos[dim]   = fttt;
677             new_path->n.pos[dim] = f1tt;
678             end->p.pos[dim]      = f11t;
679         }
680     }
683 /**
684  * Adds new node on direct line between two nodes, activates handles of all 
685  * three nodes.
686  */
687 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
689     g_assert(end);
690     g_assert(end->subpath);
691     g_assert(g_list_find(end->subpath->nodes, end));
693    Inkscape::NodePath::Node *start = end->p.other;
694     g_assert( start->n.other == end );
695    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
696                                                end,
697                                               Inkscape::NodePath::NODE_SMOOTH,
698                                                (NRPathcode)end->code,
699                                                &start->pos, &start->pos, &start->n.pos);
700     sp_nodepath_line_midpoint(newnode, end, t);
702     sp_node_ensure_ctrls(start);
703     sp_node_ensure_ctrls(newnode);
704     sp_node_ensure_ctrls(end);
706     return newnode;
709 /**
710 \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
711 */
712 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
714     g_assert(node);
715     g_assert(node->subpath);
716     g_assert(g_list_find(node->subpath->nodes, node));
718    Inkscape::NodePath::SubPath *sp = node->subpath;
719     Inkscape::NodePath::Path *np    = sp->nodepath;
721     if (sp->closed) {
722         sp_nodepath_subpath_open(sp, node);
723         return sp->first;
724     } else {
725         // no break for end nodes
726         if (node == sp->first) return NULL;
727         if (node == sp->last ) return NULL;
729         // create a new subpath
730        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
732         // duplicate the break node as start of the new subpath
733        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
735         while (node->n.other) { // copy the remaining nodes into the new subpath
736            Inkscape::NodePath::Node *n  = node->n.other;
737            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);
738             if (n->selected) {
739                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
740             }
741             sp_nodepath_node_destroy(n); // remove the point on the original subpath
742         }
744         return newnode;
745     }
748 /**
749  * Duplicate node and connect to neighbours.
750  */
751 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
753     g_assert(node);
754     g_assert(node->subpath);
755     g_assert(g_list_find(node->subpath->nodes, node));
757    Inkscape::NodePath::SubPath *sp = node->subpath;
759     NRPathcode code = (NRPathcode) node->code;
760     if (code == NR_MOVETO) { // if node is the endnode,
761         node->code = NR_LINETO; // new one is inserted before it, so change that to line
762     }
764     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
766     if (!node->n.other || !node->p.other) // if node is an endnode, select it
767         return node;
768     else
769         return newnode; // otherwise select the newly created node
772 static void sp_node_control_mirror_n_to_p(Inkscape::NodePath::Node *node)
774     node->p.pos = (node->pos + (node->pos - node->n.pos));
777 static void sp_node_control_mirror_p_to_n(Inkscape::NodePath::Node *node)
779     node->n.pos = (node->pos + (node->pos - node->p.pos));
782 /**
783  * Change line type at node, with side effects on neighbours.
784  */
785 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
787     g_assert(end);
788     g_assert(end->subpath);
789     g_assert(end->p.other);
791     if (end->code == static_cast< guint > ( code ) )
792         return;
794    Inkscape::NodePath::Node *start = end->p.other;
796     end->code = code;
798     if (code == NR_LINETO) {
799         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
800         if (end->n.other) {
801             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
802         }
803         sp_node_adjust_knot(start, -1);
804         sp_node_adjust_knot(end, 1);
805     } else {
806         NR::Point delta = end->pos - start->pos;
807         start->n.pos = start->pos + delta / 3;
808         end->p.pos = end->pos - delta / 3;
809         sp_node_adjust_knot(start, 1);
810         sp_node_adjust_knot(end, -1);
811     }
813     sp_node_ensure_ctrls(start);
814     sp_node_ensure_ctrls(end);
817 /**
818  * Change node type, and its handles accordingly.
819  */
820 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeType type)
822     g_assert(node);
823     g_assert(node->subpath);
825     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
826         return node;
828     if ((node->p.other != NULL) && (node->n.other != NULL)) {
829         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
830             type =Inkscape::NodePath::NODE_CUSP;
831         }
832     }
834     node->type = type;
836     if (node->type == Inkscape::NodePath::NODE_CUSP) {
837         g_object_set(G_OBJECT(node->knot), "shape", SP_KNOT_SHAPE_DIAMOND, "size", 9, NULL);
838     } else {
839         g_object_set(G_OBJECT(node->knot), "shape", SP_KNOT_SHAPE_SQUARE, "size", 7, NULL);
840     }
842     sp_node_adjust_knots(node);
844     sp_nodepath_update_statusbar(node->subpath->nodepath);
846     return node;
849 /**
850  * Same as sp_nodepath_set_node_type(), but also converts, if necessary, 
851  * adjacent segments from lines to curves.
852 */
853 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
855     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
856         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
857             // convert adjacent segment BEFORE to curve
858             node->code = NR_CURVETO;
859             NR::Point delta;
860             if (node->n.other != NULL)
861                 delta = node->n.other->pos - node->p.other->pos;
862             else 
863                 delta = node->pos - node->p.other->pos;
864             node->p.pos = node->pos - delta / 4;
865             sp_node_ensure_ctrls(node);
866         }
868         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
869             // convert adjacent segment AFTER to curve
870             node->n.other->code = NR_CURVETO;
871             NR::Point delta;
872             if (node->p.other != NULL)
873                 delta = node->p.other->pos - node->n.other->pos;
874             else 
875                 delta = node->pos - node->n.other->pos;
876             node->n.pos = node->pos - delta / 4;
877             sp_node_ensure_ctrls(node);
878         }
879     }
881     sp_nodepath_set_node_type (node, type);
884 /**
885  * Move node to point, and adjust its and neighbouring handles.
886  */
887 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
889     NR::Point delta = p - node->pos;
890     node->pos = p;
892     node->p.pos += delta;
893     node->n.pos += delta;
895     if (node->p.other) {
896         if (node->code == NR_LINETO) {
897             sp_node_adjust_knot(node, 1);
898             sp_node_adjust_knot(node->p.other, -1);
899         }
900     }
901     if (node->n.other) {
902         if (node->n.other->code == NR_LINETO) {
903             sp_node_adjust_knot(node, -1);
904             sp_node_adjust_knot(node->n.other, 1);
905         }
906     }
908     sp_node_ensure_ctrls(node);
911 /**
912  * Call sp_node_moveto() for node selection and handle possible snapping.
913  */
914 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
915                                             bool const snap = true)
917     NR::Coord best[2] = { NR_HUGE, NR_HUGE };
918     NR::Point delta(dx, dy);
919     NR::Point best_pt = delta;
921     if (snap) {
922         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
923            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
924             NR::Point p = n->pos + delta;
925             for (int dim = 0; dim < 2; dim++) {
926                 NR::Coord dist = namedview_dim_snap(nodepath->desktop->namedview,
927                                                     Inkscape::Snapper::SNAP_POINT, p,
928                                                     NR::Dim2(dim), nodepath->path);
929                 if (dist < best[dim]) {
930                     best[dim] = dist;
931                     best_pt[dim] = p[dim] - n->pos[dim];
932                 }
933             }
934         }
935     }
937     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
938        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
939         sp_node_moveto(n, n->pos + best_pt);
940     }
942     update_object(nodepath);
945 /**
946  * Move node selection to point, adjust its and neighbouring handles,
947  * handle possible snapping, and commit the change with possible undo.
948  */
949 void
950 sp_node_selected_move(gdouble dx, gdouble dy)
952     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
953     if (!nodepath) return;
955     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
957     if (dx == 0) {
958         update_repr_keyed(nodepath, "node:move:vertical");
959     } else if (dy == 0) {
960         update_repr_keyed(nodepath, "node:move:horizontal");
961     } else {
962         update_repr(nodepath);
963     }
966 /**
967  * Move node selection off screen and commit the change.
968  */
969 void
970 sp_node_selected_move_screen(gdouble dx, gdouble dy)
972     // borrowed from sp_selection_move_screen in selection-chemistry.c
973     // we find out the current zoom factor and divide deltas by it
974     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
976     gdouble zoom = desktop->current_zoom();
977     gdouble zdx = dx / zoom;
978     gdouble zdy = dy / zoom;
980     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
981     if (!nodepath) return;
983     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
985     if (dx == 0) {
986         update_repr_keyed(nodepath, "node:move:vertical");
987     } else if (dy == 0) {
988         update_repr_keyed(nodepath, "node:move:horizontal");
989     } else {
990         update_repr(nodepath);
991     }
994 /**
995  * Ensure knot on side of node is visible/invisible.
996  */
997 static void sp_node_ensure_knot(Inkscape::NodePath::Node *node, gint which, gboolean show_knot)
999     g_assert(node != NULL);
1001    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1002     NRPathcode code = sp_node_path_code_from_side(node, side);
1004     show_knot = show_knot && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1006     if (show_knot) {
1007         if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1008             sp_knot_show(side->knot);
1009         }
1011         sp_knot_set_position(side->knot, &side->pos, 0);
1012         sp_canvas_item_show(side->line);
1014     } else {
1015         if (SP_KNOT_IS_VISIBLE(side->knot)) {
1016             sp_knot_hide(side->knot);
1017         }
1018         sp_canvas_item_hide(side->line);
1019     }
1022 /**
1023  * Ensure handles on node and neighbours of node are visible if selected.
1024  */
1025 static void sp_node_ensure_ctrls(Inkscape::NodePath::Node *node)
1027     g_assert(node != NULL);
1029     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1030         sp_knot_show(node->knot);
1031     }
1033     sp_knot_set_position(node->knot, &node->pos, 0);
1035     gboolean show_knots = node->selected;
1036     if (node->p.other != NULL) {
1037         if (node->p.other->selected) show_knots = TRUE;
1038     }
1039     if (node->n.other != NULL) {
1040         if (node->n.other->selected) show_knots = TRUE;
1041     }
1043     sp_node_ensure_knot(node, -1, show_knots);
1044     sp_node_ensure_knot(node, 1, show_knots);
1047 /**
1048  * Call sp_node_ensure_ctrls() for all nodes on subpath.
1049  */
1050 static void sp_nodepath_subpath_ensure_ctrls(Inkscape::NodePath::SubPath *subpath)
1052     g_assert(subpath != NULL);
1054     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1055         sp_node_ensure_ctrls((Inkscape::NodePath::Node *) l->data);
1056     }
1059 /**
1060  * Call sp_nodepath_subpath_ensure_ctrls() for all subpaths of nodepath.
1061  */
1062 static void sp_nodepath_ensure_ctrls(Inkscape::NodePath::Path *nodepath)
1064     g_assert(nodepath != NULL);
1066     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1067         sp_nodepath_subpath_ensure_ctrls((Inkscape::NodePath::SubPath *) l->data);
1068     }
1071 /**
1072  * Adds all selected nodes in nodepath to list.
1073  */
1074 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1076     StlConv<Node *>::list(l, selected);
1077 /// \todo this adds a copying, rework when the selection becomes a stl list
1080 /**
1081  * Align selected nodes on the specified axis.
1082  */
1083 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1085     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1086         return;
1087     }
1089     if ( !nodepath->selected->next ) { // only one node selected
1090         return;
1091     }
1092    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1093     NR::Point dest(pNode->pos);
1094     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1095         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1096         if (pNode) {
1097             dest[axis] = pNode->pos[axis];
1098             sp_node_moveto(pNode, dest);
1099         }
1100     }
1101     if (axis == NR::X) {
1102         update_repr_keyed(nodepath, "node:move:vertical");
1103     } else {
1104         update_repr_keyed(nodepath, "node:move:horizontal");
1105     }
1108 /// Helper struct.
1109 struct NodeSort
1111    Inkscape::NodePath::Node *_node;
1112     NR::Coord _coord;
1113     /// \todo use vectorof pointers instead of calling copy ctor
1114     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1115         _node(node), _coord(node->pos[axis])
1116     {}
1118 };
1120 static bool operator<(NodeSort const &a, NodeSort const &b)
1122     return (a._coord < b._coord);
1125 /**
1126  * Distribute selected nodes on the specified axis.
1127  */
1128 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1130     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1131         return;
1132     }
1134     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1135         return;
1136     }
1138    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1139     std::vector<NodeSort> sorted;
1140     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1141         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1142         if (pNode) {
1143             NodeSort n(pNode, axis);
1144             sorted.push_back(n);
1145             //dest[axis] = pNode->pos[axis];
1146             //sp_node_moveto(pNode, dest);
1147         }
1148     }
1149     std::sort(sorted.begin(), sorted.end());
1150     unsigned int len = sorted.size();
1151     //overall bboxes span
1152     float dist = (sorted.back()._coord -
1153                   sorted.front()._coord);
1154     //new distance between each bbox
1155     float step = (dist) / (len - 1);
1156     float pos = sorted.front()._coord;
1157     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1158           it < sorted.end();
1159           it ++ )
1160     {
1161         NR::Point dest((*it)._node->pos);
1162         dest[axis] = pos;
1163         sp_node_moveto((*it)._node, dest);
1164         pos += step;
1165     }
1167     if (axis == NR::X) {
1168         update_repr_keyed(nodepath, "node:move:horizontal");
1169     } else {
1170         update_repr_keyed(nodepath, "node:move:vertical");
1171     }
1175 /**
1176  * Call sp_nodepath_line_add_node() for all selected segments.
1177  */
1178 void
1179 sp_node_selected_add_node(void)
1181     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1182     if (!nodepath) {
1183         return;
1184     }
1186     GList *nl = NULL;
1188     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1189        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1190         g_assert(t->selected);
1191         if (t->p.other && t->p.other->selected) {
1192             nl = g_list_prepend(nl, t);
1193         }
1194     }
1196     while (nl) {
1197        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1198        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1199         sp_nodepath_node_select(n, TRUE, FALSE);
1200         nl = g_list_remove(nl, t);
1201     }
1203     /** \todo fixme: adjust ? */
1204     sp_nodepath_ensure_ctrls(nodepath);
1206     update_repr(nodepath);
1208     sp_nodepath_update_statusbar(nodepath);
1211 /**
1212  * Select segment nearest to point
1213  */
1214 void
1215 sp_nodepath_select_segment_near_point(SPItem * item, NR::Point p, bool toggle)
1217     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1218     if (!nodepath) {
1219         return;
1220     }
1222     Path::cut_position position = get_nearest_position_on_Path(item, p);
1224     //find segment to segment
1225     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1227     gboolean force = FALSE;
1228     if (!(e->selected && e->p.other->selected)) {
1229         force = TRUE;
1230     } 
1231     sp_nodepath_node_select(e, (gboolean) toggle, force);
1232     sp_nodepath_node_select(e->p.other, TRUE, force);
1234     sp_nodepath_ensure_ctrls(nodepath);
1236     sp_nodepath_update_statusbar(nodepath);
1239 /**
1240  * Add a node nearest to point
1241  */
1242 void
1243 sp_nodepath_add_node_near_point(SPItem * item, NR::Point p)
1245     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1246     if (!nodepath) {
1247         return;
1248     }
1250     Path::cut_position position = get_nearest_position_on_Path(item, p);
1252     //find segment to split
1253     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1254    
1255     //don't know why but t seems to flip for lines
1256     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1257         position.t = 1.0 - position.t;
1258     }
1259     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1260     sp_nodepath_node_select(n, FALSE, TRUE);
1262     /* fixme: adjust ? */
1263     sp_nodepath_ensure_ctrls(nodepath);
1265     update_repr(nodepath);
1267     sp_nodepath_update_statusbar(nodepath);
1270 /*
1271  * Adjusts a segment so that t moves by a certain delta for dragging
1272  * converts lines to curves
1273  *
1274  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1275  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1276  */
1277 void
1278 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta, char * key) 
1280     /* feel good is an arbitrary parameter that distributes the delta between handles
1281      * if t of the drag point is less than 1/6 distance form the endpoint only
1282      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1283      */
1284     double feel_good;
1285     if (t <= 1.0 / 6.0)
1286         feel_good = 0;
1287     else if (t <= 0.5)
1288         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1289     else if (t <= 5.0 / 6.0)
1290         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1291     else
1292         feel_good = 1;
1293     
1294     //if we're dragging a line convert it to a curve
1295     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1296         sp_nodepath_set_line_type(e, NR_CURVETO);
1297     }
1299     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1300     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1301     e->p.other->n.pos += offsetcoord0;
1302     e->p.pos += offsetcoord1;
1304     // adjust controls of adjacent segments where necessary
1305     sp_node_adjust_knot(e,1);
1306     sp_node_adjust_knot(e->p.other,-1);
1308     sp_nodepath_ensure_ctrls(e->subpath->nodepath);
1310     update_repr_keyed(e->subpath->nodepath, key);
1312     sp_nodepath_update_statusbar(e->subpath->nodepath);
1316 /**
1317  * Call sp_nodepath_break() for all selected segments.
1318  */
1319 void sp_node_selected_break()
1321     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1322     if (!nodepath) return;
1324     GList *temp = NULL;
1325     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1326        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1327        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1328         if (nn == NULL) continue; // no break, no new node
1329         temp = g_list_prepend(temp, nn);
1330     }
1332     if (temp) {
1333         sp_nodepath_deselect(nodepath);
1334     }
1335     for (GList *l = temp; l != NULL; l = l->next) {
1336         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1337     }
1339     sp_nodepath_ensure_ctrls(nodepath);
1341     update_repr(nodepath);
1344 /**
1345  * Duplicate the selected node(s).
1346  */
1347 void sp_node_selected_duplicate()
1349     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1350     if (!nodepath) {
1351         return;
1352     }
1354     GList *temp = NULL;
1355     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1356        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1357        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1358         if (nn == NULL) continue; // could not duplicate
1359         temp = g_list_prepend(temp, nn);
1360     }
1362     if (temp) {
1363         sp_nodepath_deselect(nodepath);
1364     }
1365     for (GList *l = temp; l != NULL; l = l->next) {
1366         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1367     }
1369     sp_nodepath_ensure_ctrls(nodepath);
1371     update_repr(nodepath);
1374 /**
1375  *  Join two nodes by merging them into one.
1376  */
1377 void sp_node_selected_join()
1379     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1380     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1382     if (g_list_length(nodepath->selected) != 2) {
1383         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1384         return;
1385     }
1387    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1388    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1390     g_assert(a != b);
1391     g_assert(a->p.other || a->n.other);
1392     g_assert(b->p.other || b->n.other);
1394     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1395         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1396         return;
1397     }
1399     /* a and b are endpoints */
1401     NR::Point c = (a->pos + b->pos) / 2;
1403     if (a->subpath == b->subpath) {
1404        Inkscape::NodePath::SubPath *sp = a->subpath;
1405         sp_nodepath_subpath_close(sp);
1407         sp_nodepath_ensure_ctrls(sp->nodepath);
1409         update_repr(nodepath);
1411         return;
1412     }
1414     /* a and b are separate subpaths */
1415    Inkscape::NodePath::SubPath *sa = a->subpath;
1416    Inkscape::NodePath::SubPath *sb = b->subpath;
1417     NR::Point p;
1418    Inkscape::NodePath::Node *n;
1419     NRPathcode code;
1420     if (a == sa->first) {
1421         p = sa->first->n.pos;
1422         code = (NRPathcode)sa->first->n.other->code;
1423        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1424         n = sa->last;
1425         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1426         n = n->p.other;
1427         while (n) {
1428             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1429             n = n->p.other;
1430             if (n == sa->first) n = NULL;
1431         }
1432         sp_nodepath_subpath_destroy(sa);
1433         sa = t;
1434     } else if (a == sa->last) {
1435         p = sa->last->p.pos;
1436         code = (NRPathcode)sa->last->code;
1437         sp_nodepath_node_destroy(sa->last);
1438     } else {
1439         code = NR_END;
1440         g_assert_not_reached();
1441     }
1443     if (b == sb->first) {
1444         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1445         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1446             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1447         }
1448     } else if (b == sb->last) {
1449         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1450         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1451             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1452         }
1453     } else {
1454         g_assert_not_reached();
1455     }
1456     /* and now destroy sb */
1458     sp_nodepath_subpath_destroy(sb);
1460     sp_nodepath_ensure_ctrls(sa->nodepath);
1462     update_repr(nodepath);
1464     sp_nodepath_update_statusbar(nodepath);
1467 /**
1468  *  Join two nodes by adding a segment between them.
1469  */
1470 void sp_node_selected_join_segment()
1472     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1473     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1475     if (g_list_length(nodepath->selected) != 2) {
1476         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1477         return;
1478     }
1480    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1481    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1483     g_assert(a != b);
1484     g_assert(a->p.other || a->n.other);
1485     g_assert(b->p.other || b->n.other);
1487     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1488         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1489         return;
1490     }
1492     if (a->subpath == b->subpath) {
1493        Inkscape::NodePath::SubPath *sp = a->subpath;
1495         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1496         sp->closed = TRUE;
1498         sp->first->p.other = sp->last;
1499         sp->last->n.other  = sp->first;
1501         sp_node_control_mirror_p_to_n(sp->last);
1502         sp_node_control_mirror_n_to_p(sp->first);
1504         sp->first->code = sp->last->code;
1505         sp->first       = sp->last;
1507         sp_nodepath_ensure_ctrls(sp->nodepath);
1509         update_repr(nodepath);
1511         return;
1512     }
1514     /* a and b are separate subpaths */
1515    Inkscape::NodePath::SubPath *sa = a->subpath;
1516    Inkscape::NodePath::SubPath *sb = b->subpath;
1518    Inkscape::NodePath::Node *n;
1519     NR::Point p;
1520     NRPathcode code;
1521     if (a == sa->first) {
1522         code = (NRPathcode) sa->first->n.other->code;
1523        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1524         n = sa->last;
1525         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1526         for (n = n->p.other; n != NULL; n = n->p.other) {
1527             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1528         }
1529         sp_nodepath_subpath_destroy(sa);
1530         sa = t;
1531     } else if (a == sa->last) {
1532         code = (NRPathcode)sa->last->code;
1533     } else {
1534         code = NR_END;
1535         g_assert_not_reached();
1536     }
1538     if (b == sb->first) {
1539         n = sb->first;
1540         sp_node_control_mirror_p_to_n(sa->last);
1541         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1542         sp_node_control_mirror_n_to_p(sa->last);
1543         for (n = n->n.other; n != NULL; n = n->n.other) {
1544             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1545         }
1546     } else if (b == sb->last) {
1547         n = sb->last;
1548         sp_node_control_mirror_p_to_n(sa->last);
1549         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1550         sp_node_control_mirror_n_to_p(sa->last);
1551         for (n = n->p.other; n != NULL; n = n->p.other) {
1552             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1553         }
1554     } else {
1555         g_assert_not_reached();
1556     }
1557     /* and now destroy sb */
1559     sp_nodepath_subpath_destroy(sb);
1561     sp_nodepath_ensure_ctrls(sa->nodepath);
1563     update_repr(nodepath);
1566 /**
1567  * Delete one or more selected nodes.
1568  */
1569 void sp_node_selected_delete()
1571     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1572     if (!nodepath) return;
1573     if (!nodepath->selected) return;
1575     /** \todo fixme: do it the right way */
1576     while (nodepath->selected) {
1577        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1578         sp_nodepath_node_destroy(node);
1579     }
1582     //clean up the nodepath (such as for trivial subpaths)
1583     sp_nodepath_cleanup(nodepath);
1585     sp_nodepath_ensure_ctrls(nodepath);
1587     // if the entire nodepath is removed, delete the selected object.
1588     if (nodepath->subpaths == NULL ||
1589         sp_nodepath_get_node_count(nodepath) < 2) {
1590         SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
1591         sp_nodepath_destroy(nodepath);
1592         sp_selection_delete();
1593         sp_document_done (document);
1594         return;
1595     }
1597     update_repr(nodepath);
1599     sp_nodepath_update_statusbar(nodepath);
1602 /**
1603  * Delete one or more segments between two selected nodes.
1604  * This is the code for 'split'.
1605  */
1606 void
1607 sp_node_selected_delete_segment(void)
1609    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
1610    Inkscape::NodePath::Node *curr, *next;     //Iterators
1612     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1613     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1615     if (g_list_length(nodepath->selected) != 2) {
1616         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1617                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1618         return;
1619     }
1621     //Selected nodes, not inclusive
1622    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1623    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1625     if ( ( a==b)                       ||  //same node
1626          (a->subpath  != b->subpath )  ||  //not the same path
1627          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
1628          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
1629     {
1630         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1631                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1632         return;
1633     }
1635     //###########################################
1636     //# BEGIN EDITS
1637     //###########################################
1638     //##################################
1639     //# CLOSED PATH
1640     //##################################
1641     if (a->subpath->closed) {
1644         gboolean reversed = FALSE;
1646         //Since we can go in a circle, we need to find the shorter distance.
1647         //  a->b or b->a
1648         start = end = NULL;
1649         int distance    = 0;
1650         int minDistance = 0;
1651         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1652             if (curr==b) {
1653                 //printf("a to b:%d\n", distance);
1654                 start = a;//go from a to b
1655                 end   = b;
1656                 minDistance = distance;
1657                 //printf("A to B :\n");
1658                 break;
1659             }
1660             distance++;
1661         }
1663         //try again, the other direction
1664         distance = 0;
1665         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1666             if (curr==a) {
1667                 //printf("b to a:%d\n", distance);
1668                 if (distance < minDistance) {
1669                     start    = b;  //we go from b to a
1670                     end      = a;
1671                     reversed = TRUE;
1672                     //printf("B to A\n");
1673                 }
1674                 break;
1675             }
1676             distance++;
1677         }
1680         //Copy everything from 'end' to 'start' to a new subpath
1681        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1682         for (curr=end ; curr ; curr=curr->n.other) {
1683             NRPathcode code = (NRPathcode) curr->code;
1684             if (curr == end)
1685                 code = NR_MOVETO;
1686             sp_nodepath_node_new(t, NULL,
1687                                  (Inkscape::NodePath::NodeType)curr->type, code,
1688                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1689             if (curr == start)
1690                 break;
1691         }
1692         sp_nodepath_subpath_destroy(a->subpath);
1695     }
1699     //##################################
1700     //# OPEN PATH
1701     //##################################
1702     else {
1704         //We need to get the direction of the list between A and B
1705         //Can we walk from a to b?
1706         start = end = NULL;
1707         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1708             if (curr==b) {
1709                 start = a;  //did it!  we go from a to b
1710                 end   = b;
1711                 //printf("A to B\n");
1712                 break;
1713             }
1714         }
1715         if (!start) {//didn't work?  let's try the other direction
1716             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1717                 if (curr==a) {
1718                     start = b;  //did it!  we go from b to a
1719                     end   = a;
1720                     //printf("B to A\n");
1721                     break;
1722                 }
1723             }
1724         }
1725         if (!start) {
1726             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1727                                                      _("Cannot find path between nodes."));
1728             return;
1729         }
1733         //Copy everything after 'end' to a new subpath
1734        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1735         for (curr=end ; curr ; curr=curr->n.other) {
1736             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
1737                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1738         }
1740         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
1741         for (curr = start->n.other ; curr  ; curr=next) {
1742             next = curr->n.other;
1743             sp_nodepath_node_destroy(curr);
1744         }
1746     }
1747     //###########################################
1748     //# END EDITS
1749     //###########################################
1751     //clean up the nodepath (such as for trivial subpaths)
1752     sp_nodepath_cleanup(nodepath);
1754     sp_nodepath_ensure_ctrls(nodepath);
1756     update_repr(nodepath);
1758     // if the entire nodepath is removed, delete the selected object.
1759     if (nodepath->subpaths == NULL ||
1760         sp_nodepath_get_node_count(nodepath) < 2) {
1761         sp_nodepath_destroy(nodepath);
1762         sp_selection_delete();
1763         return;
1764     }
1766     sp_nodepath_update_statusbar(nodepath);
1769 /**
1770  * Call sp_nodepath_set_line() for all selected segments.
1771  */
1772 void
1773 sp_node_selected_set_line_type(NRPathcode code)
1775     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1776     if (nodepath == NULL) return;
1778     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1779        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1780         g_assert(n->selected);
1781         if (n->p.other && n->p.other->selected) {
1782             sp_nodepath_set_line_type(n, code);
1783         }
1784     }
1786     update_repr(nodepath);
1789 /**
1790  * Call sp_nodepath_convert_node_type() for all selected nodes.
1791  */
1792 void
1793 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
1795     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1796     if (nodepath == NULL) return;
1798     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1799         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
1800     }
1802     update_repr(nodepath);
1805 /**
1806  * Change select status of node, update its own and neighbour handles.
1807  */
1808 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
1810     node->selected = selected;
1812     if (selected) {
1813         g_object_set(G_OBJECT(node->knot),
1814                      "fill", NODE_FILL_SEL,
1815                      "fill_mouseover", NODE_FILL_SEL_HI,
1816                      "stroke", NODE_STROKE_SEL,
1817                      "stroke_mouseover", NODE_STROKE_SEL_HI,
1818                      "size", (node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9,
1819                      NULL);
1820     } else {
1821         g_object_set(G_OBJECT(node->knot),
1822                      "fill", NODE_FILL,
1823                      "fill_mouseover", NODE_FILL_HI,
1824                      "stroke", NODE_STROKE,
1825                      "stroke_mouseover", NODE_STROKE_HI,
1826                      "size", (node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7,
1827                      NULL);
1828     }
1830     sp_node_ensure_ctrls(node);
1831     if (node->n.other) sp_node_ensure_ctrls(node->n.other);
1832     if (node->p.other) sp_node_ensure_ctrls(node->p.other);
1835 /**
1836 \brief Select a node
1837 \param node     The node to select
1838 \param incremental   If true, add to selection, otherwise deselect others
1839 \param override   If true, always select this node, otherwise toggle selected status
1840 */
1841 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
1843     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
1845     if (incremental) {
1846         if (override) {
1847             if (!g_list_find(nodepath->selected, node)) {
1848                 nodepath->selected = g_list_append(nodepath->selected, node);
1849             }
1850             sp_node_set_selected(node, TRUE);
1851         } else { // toggle
1852             if (node->selected) {
1853                 g_assert(g_list_find(nodepath->selected, node));
1854                 nodepath->selected = g_list_remove(nodepath->selected, node);
1855             } else {
1856                 g_assert(!g_list_find(nodepath->selected, node));
1857                 nodepath->selected = g_list_append(nodepath->selected, node);
1858             }
1859             sp_node_set_selected(node, !node->selected);
1860         }
1861     } else {
1862         sp_nodepath_deselect(nodepath);
1863         nodepath->selected = g_list_append(nodepath->selected, node);
1864         sp_node_set_selected(node, TRUE);
1865     }
1867     sp_nodepath_update_statusbar(nodepath);
1871 /**
1872 \brief Deselect all nodes in the nodepath
1873 */
1874 void
1875 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
1877     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1879     while (nodepath->selected) {
1880         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
1881         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
1882     }
1883     sp_nodepath_update_statusbar(nodepath);
1886 /**
1887 \brief Select or invert selection of all nodes in the nodepath
1888 */
1889 void
1890 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
1892     if (!nodepath) return;
1894     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1895        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1896         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1897            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1898            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
1899         }
1900     }
1903 /** 
1904  * If nothing selected, does the same as sp_nodepath_select_all(); 
1905  * otherwise selects/inverts all nodes in all subpaths that have selected nodes 
1906  * (i.e., similar to "select all in layer", with the "selected" subpaths 
1907  * being treated as "layers" in the path).
1908  */
1909 void
1910 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
1912     if (!nodepath) return;
1914     if (g_list_length (nodepath->selected) == 0) {
1915         sp_nodepath_select_all (nodepath, invert);
1916         return;
1917     }
1919     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
1920     GSList *subpaths = NULL;
1922     for (GList *l = copy; l != NULL; l = l->next) {
1923         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1924         Inkscape::NodePath::SubPath *subpath = n->subpath;
1925         if (!g_slist_find (subpaths, subpath))
1926             subpaths = g_slist_prepend (subpaths, subpath);
1927     }
1929     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
1930         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
1931         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1932             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1933             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
1934         }
1935     }
1937     g_slist_free (subpaths);
1938     g_list_free (copy);
1941 /**
1942  * \brief Select the node after the last selected; if none is selected, 
1943  * select the first within path.
1944  */
1945 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
1947     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1949    Inkscape::NodePath::Node *last = NULL;
1950     if (nodepath->selected) {
1951         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1952            Inkscape::NodePath::SubPath *subpath, *subpath_next;
1953             subpath = (Inkscape::NodePath::SubPath *) spl->data;
1954             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1955                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1956                 if (node->selected) {
1957                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
1958                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
1959                             if (spl->next) { // there's a next subpath
1960                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
1961                                 last = subpath_next->first;
1962                             } else if (spl->prev) { // there's a previous subpath
1963                                 last = NULL; // to be set later to the first node of first subpath
1964                             } else {
1965                                 last = node->n.other;
1966                             }
1967                         } else {
1968                             last = node->n.other;
1969                         }
1970                     } else {
1971                         if (node->n.other) {
1972                             last = node->n.other;
1973                         } else {
1974                             if (spl->next) { // there's a next subpath
1975                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
1976                                 last = subpath_next->first;
1977                             } else if (spl->prev) { // there's a previous subpath
1978                                 last = NULL; // to be set later to the first node of first subpath
1979                             } else {
1980                                 last = (Inkscape::NodePath::Node *) subpath->first;
1981                             }
1982                         }
1983                     }
1984                 }
1985             }
1986         }
1987         sp_nodepath_deselect(nodepath);
1988     }
1990     if (last) { // there's at least one more node after selected
1991         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
1992     } else { // no more nodes, select the first one in first subpath
1993        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
1994         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
1995     }
1998 /**
1999  * \brief Select the node before the first selected; if none is selected, 
2000  * select the last within path
2001  */
2002 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2004     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2006    Inkscape::NodePath::Node *last = NULL;
2007     if (nodepath->selected) {
2008         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2009            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2010             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2011                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2012                 if (node->selected) {
2013                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2014                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2015                             if (spl->prev) { // there's a prev subpath
2016                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2017                                 last = subpath_prev->last;
2018                             } else if (spl->next) { // there's a next subpath
2019                                 last = NULL; // to be set later to the last node of last subpath
2020                             } else {
2021                                 last = node->p.other;
2022                             }
2023                         } else {
2024                             last = node->p.other;
2025                         }
2026                     } else {
2027                         if (node->p.other) {
2028                             last = node->p.other;
2029                         } else {
2030                             if (spl->prev) { // there's a prev subpath
2031                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2032                                 last = subpath_prev->last;
2033                             } else if (spl->next) { // there's a next subpath
2034                                 last = NULL; // to be set later to the last node of last subpath
2035                             } else {
2036                                 last = (Inkscape::NodePath::Node *) subpath->last;
2037                             }
2038                         }
2039                     }
2040                 }
2041             }
2042         }
2043         sp_nodepath_deselect(nodepath);
2044     }
2046     if (last) { // there's at least one more node before selected
2047         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2048     } else { // no more nodes, select the last one in last subpath
2049         GList *spl = g_list_last(nodepath->subpaths);
2050        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2051         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2052     }
2055 /**
2056  * \brief Select all nodes that are within the rectangle.
2057  */
2058 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2060     if (!incremental) {
2061         sp_nodepath_deselect(nodepath);
2062     }
2064     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2065        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2066         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2067            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2069             if (b.contains(node->pos)) {
2070                 sp_nodepath_node_select(node, TRUE, TRUE);
2071             }
2072         }
2073     }
2076 /**
2077 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2078 */
2079 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2081     if (!nodepath->selected) {
2082         return NULL;
2083     }
2085     GList *r = NULL;
2086     guint i = 0;
2087     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2088        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2089         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2090            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2091             i++;
2092             if (node->selected) {
2093                 r = g_list_append(r, GINT_TO_POINTER(i));
2094             }
2095         }
2096     }
2097     return r;
2100 /**
2101 \brief  Restores selection by selecting nodes whose positions are in the list
2102 */
2103 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2105     sp_nodepath_deselect(nodepath);
2107     guint i = 0;
2108     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2109        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2110         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2111            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2112             i++;
2113             if (g_list_find(r, GINT_TO_POINTER(i))) {
2114                 sp_nodepath_node_select(node, TRUE, TRUE);
2115             }
2116         }
2117     }
2121 /**
2122 \brief Adjusts control point according to node type and line code.
2123 */
2124 static void sp_node_adjust_knot(Inkscape::NodePath::Node *node, gint which_adjust)
2126     double len, otherlen, linelen;
2128     g_assert(node);
2130    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2131    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2133     /** \todo fixme: */
2134     if (me->other == NULL) return;
2135     if (other->other == NULL) return;
2137     /* I have line */
2139     NRPathcode mecode, ocode;
2140     if (which_adjust == 1) {
2141         mecode = (NRPathcode)me->other->code;
2142         ocode = (NRPathcode)node->code;
2143     } else {
2144         mecode = (NRPathcode)node->code;
2145         ocode = (NRPathcode)other->other->code;
2146     }
2148     if (mecode == NR_LINETO) return;
2150     /* I am curve */
2152     if (other->other == NULL) return;
2154     /* Other has line */
2156     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2158     NR::Point delta;
2159     if (ocode == NR_LINETO) {
2160         /* other is lineto, we are either smooth or symm */
2161        Inkscape::NodePath::Node *othernode = other->other;
2162         len = NR::L2(me->pos - node->pos);
2163         delta = node->pos - othernode->pos;
2164         linelen = NR::L2(delta);
2165         if (linelen < 1e-18) return;
2167         me->pos = node->pos + (len / linelen)*delta;
2168         sp_knot_set_position(me->knot, &me->pos, 0);
2170         sp_node_ensure_ctrls(node);
2171         return;
2172     }
2174     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2176         me->pos = 2 * node->pos - other->pos;
2177         sp_knot_set_position(me->knot, &me->pos, 0);
2179         sp_node_ensure_ctrls(node);
2180         return;
2181     }
2183     /* We are smooth */
2185     len = NR::L2(me->pos - node->pos);
2186     delta = other->pos - node->pos;
2187     otherlen = NR::L2(delta);
2188     if (otherlen < 1e-18) return;
2190     me->pos = node->pos - (len / otherlen) * delta;
2191     sp_knot_set_position(me->knot, &me->pos, 0);
2193     sp_node_ensure_ctrls(node);
2196 /**
2197  \brief Adjusts control point according to node type and line code
2198  */
2199 static void sp_node_adjust_knots(Inkscape::NodePath::Node *node)
2201     g_assert(node);
2203     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2205     /* we are either smooth or symm */
2207     if (node->p.other == NULL) return;
2209     if (node->n.other == NULL) return;
2211     if (node->code == NR_LINETO) {
2212         if (node->n.other->code == NR_LINETO) return;
2213         sp_node_adjust_knot(node, 1);
2214         sp_node_ensure_ctrls(node);
2215         return;
2216     }
2218     if (node->n.other->code == NR_LINETO) {
2219         if (node->code == NR_LINETO) return;
2220         sp_node_adjust_knot(node, -1);
2221         sp_node_ensure_ctrls(node);
2222         return;
2223     }
2225     /* both are curves */
2227     NR::Point const delta( node->n.pos - node->p.pos );
2229     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2230         node->p.pos = node->pos - delta / 2;
2231         node->n.pos = node->pos + delta / 2;
2232         sp_node_ensure_ctrls(node);
2233         return;
2234     }
2236     /* We are smooth */
2238     double plen = NR::L2(node->p.pos - node->pos);
2239     if (plen < 1e-18) return;
2240     double nlen = NR::L2(node->n.pos - node->pos);
2241     if (nlen < 1e-18) return;
2242     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2243     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2244     sp_node_ensure_ctrls(node);
2247 /**
2248  * Knot events handler callback.
2249  */
2250 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2252     gboolean ret = FALSE;
2253     switch (event->type) {
2254         case GDK_ENTER_NOTIFY:
2255             active_node = n;
2256             break;
2257         case GDK_LEAVE_NOTIFY:
2258             active_node = NULL;
2259             break;
2260         case GDK_KEY_PRESS:
2261             switch (get_group0_keyval (&event->key)) {
2262                 case GDK_space:
2263                     if (event->key.state & GDK_BUTTON1_MASK) {
2264                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2265                         stamp_repr(nodepath);
2266                         ret = TRUE;
2267                     }
2268                     break;
2269                 default:
2270                     break;
2271             }
2272             break;
2273         default:
2274             break;
2275     }
2277     return ret;
2280 /**
2281  * Handle keypress on node; directly called.
2282  */
2283 gboolean node_key(GdkEvent *event)
2285     Inkscape::NodePath::Path *np;
2287     // there is no way to verify nodes so set active_node to nil when deleting!!
2288     if (active_node == NULL) return FALSE;
2290     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2291         gint ret = FALSE;
2292         switch (get_group0_keyval (&event->key)) {
2293             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2294             case GDK_BackSpace:
2295                 np = active_node->subpath->nodepath;
2296                 sp_nodepath_node_destroy(active_node);
2297                 update_repr(np);
2298                 active_node = NULL;
2299                 ret = TRUE;
2300                 break;
2301             case GDK_c:
2302                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2303                 ret = TRUE;
2304                 break;
2305             case GDK_s:
2306                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2307                 ret = TRUE;
2308                 break;
2309             case GDK_y:
2310                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2311                 ret = TRUE;
2312                 break;
2313             case GDK_b:
2314                 sp_nodepath_node_break(active_node);
2315                 ret = TRUE;
2316                 break;
2317         }
2318         return ret;
2319     }
2320     return FALSE;
2323 /**
2324  * Mouseclick on node callback.
2325  */
2326 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2328    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2330     if (state & GDK_CONTROL_MASK) {
2331         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2333         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2334             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2335                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2336             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2337                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2338             } else {
2339                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2340             }
2341             update_repr(nodepath);
2342             sp_nodepath_update_statusbar(nodepath);
2344         } else { //ctrl+alt+click: delete node
2345             sp_nodepath_node_destroy(n);
2346             //clean up the nodepath (such as for trivial subpaths)
2347             sp_nodepath_cleanup(nodepath);
2349             // if the entire nodepath is removed, delete the selected object.
2350             if (nodepath->subpaths == NULL ||
2351                 sp_nodepath_get_node_count(nodepath) < 2) {
2352                 SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
2353                 sp_nodepath_destroy(nodepath);
2354                 sp_selection_delete();
2355                 sp_document_done (document);
2357             } else {
2358                 sp_nodepath_ensure_ctrls(nodepath);
2359                 update_repr(nodepath);
2360                 sp_nodepath_update_statusbar(nodepath);
2361             }
2362         }
2364     } else {
2365         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2366     }
2369 /**
2370  * Mouse grabbed node callback.
2371  */
2372 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2374    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2376     n->origin = knot->pos;
2378     if (!n->selected) {
2379         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2380     }
2383 /**
2384  * Mouse ungrabbed node callback.
2385  */
2386 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2388    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2390    n->dragging_out = NULL;
2392    update_repr(n->subpath->nodepath);
2395 /**
2396  * The point on a line, given by its angle, closest to the given point.
2397  * \param p  A point.
2398  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2399  * \param closest  Pointer to the point struct where the result is stored.
2400  * \todo FIXME: use dot product perhaps?
2401  */
2402 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2404     if (a == HUGE_VAL) { // vertical
2405         *closest = NR::Point(0, (*p)[NR::Y]);
2406     } else {
2407         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2408         (*closest)[NR::Y] = a * (*closest)[NR::X];
2409     }
2412 /**
2413  * Distance from the point to a line given by its angle.
2414  * \param p  A point.
2415  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2416  */
2417 static double point_line_distance(NR::Point *p, double a)
2419     NR::Point c;
2420     point_line_closest(p, a, &c);
2421     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]));
2424 /**
2425  * Callback for node "request" signal.
2426  * \todo fixme: This goes to "moved" event? (lauris)
2427  */
2428 static gboolean
2429 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2431     double yn, xn, yp, xp;
2432     double an, ap, na, pa;
2433     double d_an, d_ap, d_na, d_pa;
2434     gboolean collinear = FALSE;
2435     NR::Point c;
2436     NR::Point pr;
2438    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2440    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2441    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) { 
2443        NR::Point mouse = (*p);
2445        if (!n->dragging_out) {
2446            // This is the first drag-out event; find out which handle to drag out
2447            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2448            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2450            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2451                return FALSE;
2453            Inkscape::NodePath::NodeSide *opposite;
2454            if (appr_p > appr_n) { // closer to p
2455                n->dragging_out = &n->p;
2456                opposite = &n->n;
2457                n->code = NR_CURVETO;
2458            } else if (appr_p < appr_n) { // closer to n
2459                n->dragging_out = &n->n;
2460                opposite = &n->p;
2461                n->n.other->code = NR_CURVETO;
2462            } else { // p and n nodes are the same
2463                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2464                    n->dragging_out = &n->p;
2465                    opposite = &n->n;
2466                    n->code = NR_CURVETO;
2467                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2468                    n->dragging_out = &n->n;
2469                    opposite = &n->p;
2470                    n->n.other->code = NR_CURVETO;
2471                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2472                    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);
2473                    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);
2474                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2475                        n->dragging_out = &n->n;
2476                        opposite = &n->p;
2477                        n->n.other->code = NR_CURVETO;
2478                    } else { // closer to other's n handle
2479                        n->dragging_out = &n->p;
2480                        opposite = &n->n;
2481                        n->code = NR_CURVETO;
2482                    }
2483                }
2484            }
2486            // if there's another handle, make sure the one we drag out starts parallel to it
2487            if (opposite->pos != n->pos) {
2488                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2489            }
2490        }
2492        // pass this on to the handle-moved callback
2493        node_ctrl_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2494        sp_node_ensure_ctrls(n);
2495        return TRUE;
2496    }
2498     if (state & GDK_CONTROL_MASK) { // constrained motion
2500         // calculate relative distances of handles
2501         // n handle:
2502         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2503         xn = n->n.pos[NR::X] - n->pos[NR::X];
2504         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2505         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2506             if (n->n.other) { // if there is the next point
2507                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2508                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2509                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2510             }
2511         }
2512         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2513         if (yn < 0) { xn = -xn; yn = -yn; }
2515         // p handle:
2516         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2517         xp = n->p.pos[NR::X] - n->pos[NR::X];
2518         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2519         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2520             if (n->p.other) {
2521                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2522                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2523                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2524             }
2525         }
2526         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2527         if (yp < 0) { xp = -xp; yp = -yp; }
2529         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2530             // sliding on handles, only if at least one of the handles is non-vertical
2531             // (otherwise it's the same as ctrl+drag anyway)
2533             // calculate angles of the control handles
2534             if (xn == 0) {
2535                 if (yn == 0) { // no handle, consider it the continuation of the other one
2536                     an = 0;
2537                     collinear = TRUE;
2538                 }
2539                 else an = 0; // vertical; set the angle to horizontal
2540             } else an = yn/xn;
2542             if (xp == 0) {
2543                 if (yp == 0) { // no handle, consider it the continuation of the other one
2544                     ap = an;
2545                 }
2546                 else ap = 0; // vertical; set the angle to horizontal
2547             } else  ap = yp/xp;
2549             if (collinear) an = ap;
2551             // angles of the perpendiculars; HUGE_VAL means vertical
2552             if (an == 0) na = HUGE_VAL; else na = -1/an;
2553             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2555             //g_print("an %g    ap %g\n", an, ap);
2557             // mouse point relative to the node's original pos
2558             pr = (*p) - n->origin;
2560             // distances to the four lines (two handles and two perpendiculars)
2561             d_an = point_line_distance(&pr, an);
2562             d_na = point_line_distance(&pr, na);
2563             d_ap = point_line_distance(&pr, ap);
2564             d_pa = point_line_distance(&pr, pa);
2566             // find out which line is the closest, save its closest point in c
2567             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2568                 point_line_closest(&pr, an, &c);
2569             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2570                 point_line_closest(&pr, ap, &c);
2571             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2572                 point_line_closest(&pr, na, &c);
2573             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2574                 point_line_closest(&pr, pa, &c);
2575             }
2577             // move the node to the closest point
2578             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2579                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2580                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2582         } else {  // constraining to hor/vert
2584             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2585                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2586             } else { // snap to vert
2587                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2588             }
2589         }
2590     } else { // move freely
2591         sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2592                                         (*p)[NR::X] - n->pos[NR::X],
2593                                         (*p)[NR::Y] - n->pos[NR::Y],
2594                                         (state & GDK_SHIFT_MASK) == 0);
2595     }
2597     n->subpath->nodepath->desktop->scroll_to_point(p);
2599     return TRUE;
2602 /**
2603  * Node handle clicked callback.
2604  */
2605 static void node_ctrl_clicked(SPKnot *knot, guint state, gpointer data)
2607    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2609     if (state & GDK_CONTROL_MASK) { // "delete" handle
2610         if (n->p.knot == knot) {
2611             n->p.pos = n->pos;
2612         } else if (n->n.knot == knot) {
2613             n->n.pos = n->pos;
2614         }
2615         sp_node_ensure_ctrls(n);
2616         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2617         update_repr(nodepath);
2618         sp_nodepath_update_statusbar(nodepath);
2620     } else { // just select or add to selection, depending in Shift
2621         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2622     }
2625 /**
2626  * Node handle grabbed callback.
2627  */
2628 static void node_ctrl_grabbed(SPKnot *knot, guint state, gpointer data)
2630    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2632     if (!n->selected) {
2633         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2634     }
2636     // remember the origin of the control
2637     if (n->p.knot == knot) {
2638         n->p.origin = n->p.pos - n->pos;
2639     } else if (n->n.knot == knot) {
2640         n->n.origin = n->n.pos - n->pos;
2641     } else {
2642         g_assert_not_reached();
2643     }
2647 /**
2648  * Node handle ungrabbed callback.
2649  */
2650 static void node_ctrl_ungrabbed(SPKnot *knot, guint state, gpointer data)
2652    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2654     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
2655     if (n->p.knot == knot) {
2656         n->p.origin.a = 0;
2657         sp_knot_set_position(knot, &n->p.pos, state);
2658     } else if (n->n.knot == knot) {
2659         n->n.origin.a = 0;
2660         sp_knot_set_position(knot, &n->n.pos, state);
2661     } else {
2662         g_assert_not_reached();
2663     }
2665     update_repr(n->subpath->nodepath);
2668 /**
2669  * Node handle "request" signal callback.
2670  */
2671 static gboolean node_ctrl_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2673     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2675     Inkscape::NodePath::NodeSide *me, *opposite;
2676     gint which;
2677     if (n->p.knot == knot) {
2678         me = &n->p;
2679         opposite = &n->n;
2680         which = -1;
2681     } else if (n->n.knot == knot) {
2682         me = &n->n;
2683         opposite = &n->p;
2684         which = 1;
2685     } else {
2686         me = opposite = NULL;
2687         which = 0;
2688         g_assert_not_reached();
2689     }
2691     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
2693     SnapManager const m(n->subpath->nodepath->desktop->namedview);
2695     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
2696         /* We are smooth node adjacent with line */
2697         NR::Point const delta = *p - n->pos;
2698         NR::Coord const len = NR::L2(delta);
2699         Inkscape::NodePath::Node *othernode = opposite->other;
2700         NR::Point const ndelta = n->pos - othernode->pos;
2701         NR::Coord const linelen = NR::L2(ndelta);
2702         if (len > NR_EPSILON && linelen > NR_EPSILON) {
2703             NR::Coord const scal = dot(delta, ndelta) / linelen;
2704             (*p) = n->pos + (scal / linelen) * ndelta;
2705         }
2706         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, ndelta, NULL).getPoint();
2707     } else {
2708         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
2709     }
2711     sp_node_adjust_knot(n, -which);
2713     return FALSE;
2716 /**
2717  * Node handle moved callback.
2718  */
2719 static void node_ctrl_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2721    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2723    Inkscape::NodePath::NodeSide *me;
2724    Inkscape::NodePath::NodeSide *other;
2725     if (n->p.knot == knot) {
2726         me = &n->p;
2727         other = &n->n;
2728     } else if (n->n.knot == knot) {
2729         me = &n->n;
2730         other = &n->p;
2731     } else {
2732         me = NULL;
2733         other = NULL;
2734         g_assert_not_reached();
2735     }
2737     // calculate radial coordinates of the grabbed control, other control, and the mouse point
2738     Radial rme(me->pos - n->pos);
2739     Radial rother(other->pos - n->pos);
2740     Radial rnew(*p - n->pos);
2742     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
2743         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
2744         /* 0 interpreted as "no snapping". */
2746         // The closest PI/snaps angle, starting from zero.
2747         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
2748         if (me->origin.a == HUGE_VAL) {
2749             // ortho doesn't exist: original control was zero length.
2750             rnew.a = a_snapped;
2751         } else {
2752             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
2753              * its opposite and perpendiculars). */
2754             double const a_ortho = me->origin.a + floor((rnew.a - me->origin.a)/(M_PI/2) + 0.5) * (M_PI/2);
2756             // Snap to the closest.
2757             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
2758                        ? a_snapped
2759                        : a_ortho );
2760         }
2761     }
2763     if (state & GDK_MOD1_MASK) {
2764         // lock handle length
2765         rnew.r = me->origin.r;
2766     }
2768     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
2769         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
2770         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
2771         rother.a += rnew.a - rme.a;
2772         other->pos = NR::Point(rother) + n->pos;
2773         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
2774         sp_knot_set_position(other->knot, &other->pos, 0);
2775     }
2777     me->pos = NR::Point(rnew) + n->pos;
2778     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
2780     // this is what sp_knot_set_position does, but without emitting the signal:
2781     // we cannot emit a "moved" signal because we're now processing it
2782     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
2784     knot->desktop->set_coordinate_status(me->pos);
2786     update_object(n->subpath->nodepath);
2788     /* status text */
2789     SPDesktop *desktop = n->subpath->nodepath->desktop;
2790     if (!desktop) return;
2791     SPEventContext *ec = desktop->event_context;
2792     if (!ec) return;
2793     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
2794     if (!mc) return;
2796     double degrees = 180 / M_PI * rnew.a;
2797     if (degrees > 180) degrees -= 360;
2798     if (degrees < -180) degrees += 360;
2799     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
2800         degrees = angle_to_compass (degrees);
2802     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
2804     mc->setF(Inkscape::NORMAL_MESSAGE,
2805          _("<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);
2807     g_string_free(length, TRUE);
2810 /**
2811  * Node handle event callback.
2812  */
2813 static gboolean node_ctrl_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2815     gboolean ret = FALSE;
2816     switch (event->type) {
2817         case GDK_KEY_PRESS:
2818             switch (get_group0_keyval (&event->key)) {
2819                 case GDK_space:
2820                     if (event->key.state & GDK_BUTTON1_MASK) {
2821                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2822                         stamp_repr(nodepath);
2823                         ret = TRUE;
2824                     }
2825                     break;
2826                 default:
2827                     break;
2828             }
2829             break;
2830         default:
2831             break;
2832     }
2834     return ret;
2837 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
2838                                  Radial &rme, Radial &rother, gboolean const both)
2840     rme.a += angle;
2841     if ( both
2842          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2843          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2844     {
2845         rother.a += angle;
2846     }
2849 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
2850                                         Radial &rme, Radial &rother, gboolean const both)
2852     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
2854     gdouble r;
2855     if ( both
2856          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2857          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2858     {
2859         r = MAX(rme.r, rother.r);
2860     } else {
2861         r = rme.r;
2862     }
2864     gdouble const weird_angle = atan2(norm_angle, r);
2865 /* Bulia says norm_angle is just the visible distance that the
2866  * object's end must travel on the screen.  Left as 'angle' for want of
2867  * a better name.*/
2869     rme.a += weird_angle;
2870     if ( both
2871          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2872          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2873     {
2874         rother.a += weird_angle;
2875     }
2878 /**
2879  * Rotate one node.
2880  */
2881 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
2883     Inkscape::NodePath::NodeSide *me, *other;
2884     bool both = false;
2886     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
2887     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
2889     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
2890         me = &(n->p);
2891         other = &(n->n);
2892     } else if (!n->p.other) {
2893         me = &(n->n);
2894         other = &(n->p);
2895     } else {
2896         if (which > 0) { // right handle
2897             if (xn > xp) {
2898                 me = &(n->n);
2899                 other = &(n->p);
2900             } else {
2901                 me = &(n->p);
2902                 other = &(n->n);
2903             }
2904         } else if (which < 0){ // left handle
2905             if (xn <= xp) {
2906                 me = &(n->n);
2907                 other = &(n->p);
2908             } else {
2909                 me = &(n->p);
2910                 other = &(n->n);
2911             }
2912         } else { // both handles
2913             me = &(n->n);
2914             other = &(n->p);
2915             both = true;
2916         }
2917     }
2919     Radial rme(me->pos - n->pos);
2920     Radial rother(other->pos - n->pos);
2922     if (screen) {
2923         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
2924     } else {
2925         node_rotate_one_internal (*n, angle, rme, rother, both);
2926     }
2928     me->pos = n->pos + NR::Point(rme);
2930     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
2931         other->pos =  n->pos + NR::Point(rother);
2932     }
2934     sp_node_ensure_ctrls(n);
2937 /**
2938  * Rotate selected nodes.
2939  */
2940 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
2942     if (!nodepath || !nodepath->selected) return;
2944     if (g_list_length(nodepath->selected) == 1) {
2945        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
2946         node_rotate_one (n, angle, which, screen);
2947     } else {
2948        // rotate as an object:
2950         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
2951         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
2952         for (GList *l = nodepath->selected; l != NULL; l = l->next) { 
2953             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2954             box.expandTo (n->pos); // contain all selected nodes
2955         }
2957         gdouble rot;
2958         if (screen) {
2959             gdouble const zoom = nodepath->desktop->current_zoom();
2960             gdouble const zmove = angle / zoom;
2961             gdouble const r = NR::L2(box.max() - box.midpoint());
2962             rot = atan2(zmove, r);
2963         } else {
2964             rot = angle;
2965         }
2967         NR::Matrix t = 
2968             NR::Matrix (NR::translate(-box.midpoint())) * 
2969             NR::Matrix (NR::rotate(rot)) * 
2970             NR::Matrix (NR::translate(box.midpoint()));
2972         for (GList *l = nodepath->selected; l != NULL; l = l->next) { 
2973             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2974             n->pos *= t;
2975             n->n.pos *= t;
2976             n->p.pos *= t;
2977             sp_node_ensure_ctrls(n);
2978         }
2979     }
2981     update_object(nodepath);
2982     /// \todo fixme: use _keyed
2983     update_repr(nodepath);
2986 /**
2987  * Scale one node.
2988  */
2989 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
2991     bool both = false;
2992     Inkscape::NodePath::NodeSide *me, *other;
2994     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
2995     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
2997     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
2998         me = &(n->p);
2999         other = &(n->n);
3000         n->code = NR_CURVETO;
3001     } else if (!n->p.other) {
3002         me = &(n->n);
3003         other = &(n->p);
3004         if (n->n.other)    
3005             n->n.other->code = NR_CURVETO;
3006     } else {
3007         if (which > 0) { // right handle
3008             if (xn > xp) {
3009                 me = &(n->n);
3010                 other = &(n->p);
3011                 if (n->n.other)    
3012                     n->n.other->code = NR_CURVETO;
3013             } else {
3014                 me = &(n->p);
3015                 other = &(n->n);
3016                 n->code = NR_CURVETO;
3017             }
3018         } else if (which < 0){ // left handle
3019             if (xn <= xp) {
3020                 me = &(n->n);
3021                 other = &(n->p);
3022                 if (n->n.other)    
3023                     n->n.other->code = NR_CURVETO;
3024             } else {
3025                 me = &(n->p);
3026                 other = &(n->n);
3027                 n->code = NR_CURVETO;
3028             }
3029         } else { // both handles
3030             me = &(n->n);
3031             other = &(n->p);
3032             both = true;
3033             n->code = NR_CURVETO;
3034             if (n->n.other)    
3035                 n->n.other->code = NR_CURVETO;
3036         }
3037     }
3039     Radial rme(me->pos - n->pos);
3040     Radial rother(other->pos - n->pos);
3042     rme.r += grow;
3043     if (rme.r < 0) rme.r = 0;
3044     if (rme.a == HUGE_VAL) {
3045         if (me->other) { // if direction is unknown, initialize it towards the next node
3046             Radial rme_next(me->other->pos - n->pos);
3047             rme.a = rme_next.a;
3048         } else { // if there's no next, initialize to 0
3049             rme.a = 0;
3050         }
3051     }
3052     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3053         rother.r += grow;
3054         if (rother.r < 0) rother.r = 0;
3055         if (rother.a == HUGE_VAL) {
3056             rother.a = rme.a + M_PI;
3057         }
3058     }
3060     me->pos = n->pos + NR::Point(rme);
3062     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3063         other->pos = n->pos + NR::Point(rother);
3064     }
3066     sp_node_ensure_ctrls(n);
3069 /**
3070  * Scale selected nodes.
3071  */
3072 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3074     if (!nodepath || !nodepath->selected) return;
3076     if (g_list_length(nodepath->selected) == 1) {
3077         // scale handles of the single selected node
3078         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3079         node_scale_one (n, grow, which);
3080     } else {
3081         // scale nodes as an "object":
3083         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3084         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3085         for (GList *l = nodepath->selected; l != NULL; l = l->next) { 
3086             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3087             box.expandTo (n->pos); // contain all selected nodes
3088         }
3090         double scale = (box.maxExtent() + grow)/box.maxExtent();
3092         NR::Matrix t = 
3093             NR::Matrix (NR::translate(-box.midpoint())) * 
3094             NR::Matrix (NR::scale(scale, scale)) * 
3095             NR::Matrix (NR::translate(box.midpoint()));
3097         for (GList *l = nodepath->selected; l != NULL; l = l->next) { 
3098             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3099             n->pos *= t;
3100             n->n.pos *= t;
3101             n->p.pos *= t;
3102             sp_node_ensure_ctrls(n);
3103         }
3104     }
3106     update_object(nodepath);
3107     /// \todo fixme: use _keyed
3108     update_repr(nodepath);
3111 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3113     if (!nodepath) return;
3114     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3117 /**
3118  * Flip selected nodes horizontally/vertically.
3119  */
3120 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3122     if (!nodepath || !nodepath->selected) return;
3124     if (g_list_length(nodepath->selected) == 1) {
3125         // flip handles of the single selected node
3126         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3127         double temp = n->p.pos[axis];
3128         n->p.pos[axis] = n->n.pos[axis];
3129         n->n.pos[axis] = temp;
3130         sp_node_ensure_ctrls(n);
3131     } else {
3132         // scale nodes as an "object":
3134         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3135         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3136         for (GList *l = nodepath->selected; l != NULL; l = l->next) { 
3137             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3138             box.expandTo (n->pos); // contain all selected nodes
3139         }
3141         NR::Matrix t = 
3142             NR::Matrix (NR::translate(-box.midpoint())) * 
3143             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) * 
3144             NR::Matrix (NR::translate(box.midpoint()));
3146         for (GList *l = nodepath->selected; l != NULL; l = l->next) { 
3147             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3148             n->pos *= t;
3149             n->n.pos *= t;
3150             n->p.pos *= t;
3151             sp_node_ensure_ctrls(n);
3152         }
3153     }
3155     update_object(nodepath);
3156     /// \todo fixme: use _keyed
3157     update_repr(nodepath);
3160 //-----------------------------------------------
3161 /**
3162  * Return new subpath under given nodepath.
3163  */
3164 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3166     g_assert(nodepath);
3167     g_assert(nodepath->desktop);
3169    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3171     s->nodepath = nodepath;
3172     s->closed = FALSE;
3173     s->nodes = NULL;
3174     s->first = NULL;
3175     s->last = NULL;
3177     // do not use prepend here because:
3178     // if you have a path like "subpath_1 subpath_2 ... subpath_k" in the svg, you end up with
3179     // subpath_k -> ... ->subpath_1 in the nodepath structure. thus the i-th node of the svg is not
3180     // the i-th node in the nodepath (only if there are multiple subpaths)
3181     // note that the problem only arise when called from subpath_from_bpath(), since for all the other
3182     // cases, the repr is updated after the call to sp_nodepath_subpath_new()
3183     nodepath->subpaths = g_list_append /*g_list_prepend*/ (nodepath->subpaths, s);
3185     return s;
3188 /**
3189  * Destroy nodes in subpath, then subpath itself.
3190  */
3191 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3193     g_assert(subpath);
3194     g_assert(subpath->nodepath);
3195     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3197     while (subpath->nodes) {
3198         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3199     }
3201     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3203     g_free(subpath);
3206 /**
3207  * Link head to tail in subpath.
3208  */
3209 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3211     g_assert(!sp->closed);
3212     g_assert(sp->last != sp->first);
3213     g_assert(sp->first->code == NR_MOVETO);
3215     sp->closed = TRUE;
3217     //Link the head to the tail
3218     sp->first->p.other = sp->last;
3219     sp->last->n.other  = sp->first;
3220     sp->last->n.pos    = sp->first->n.pos;
3221     sp->first          = sp->last;
3223     //Remove the extra end node
3224     sp_nodepath_node_destroy(sp->last->n.other);
3227 /**
3228  * Open closed (loopy) subpath at node.
3229  */
3230 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3232     g_assert(sp->closed);
3233     g_assert(n->subpath == sp);
3234     g_assert(sp->first == sp->last);
3236     /* We create new startpoint, current node will become last one */
3238    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3239                                                 &n->pos, &n->pos, &n->n.pos);
3242     sp->closed        = FALSE;
3244     //Unlink to make a head and tail
3245     sp->first         = new_path;
3246     sp->last          = n;
3247     n->n.other        = NULL;
3248     new_path->p.other = NULL;
3251 /**
3252  * Returns area in triangle given by points; may be negative.
3253  */
3254 inline double
3255 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3) 
3257     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]);
3260 /**
3261  * Return new node in subpath with given properties.
3262  * \param pos Position of node.
3263  * \param ppos Handle position in previous direction
3264  * \param npos Handle position in previous direction
3265  */
3266 Inkscape::NodePath::Node *
3267 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)
3269     g_assert(sp);
3270     g_assert(sp->nodepath);
3271     g_assert(sp->nodepath->desktop);
3273     if (nodechunk == NULL)
3274         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3276     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3278     n->subpath  = sp;
3280     if (type != Inkscape::NodePath::NODE_NONE) {
3281         // use the type from sodipodi:nodetypes
3282         n->type = type;
3283     } else {
3284         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3285             // points are (almost) collinear
3286             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3287                 // endnode, or a node with a retracted handle
3288                 n->type = Inkscape::NodePath::NODE_CUSP;
3289             } else {
3290                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3291             }
3292         } else {
3293             n->type = Inkscape::NodePath::NODE_CUSP;
3294         }
3295     }
3297     n->code     = code;
3298     n->selected = FALSE;
3299     n->pos      = *pos;
3300     n->p.pos    = *ppos;
3301     n->n.pos    = *npos;
3303     n->dragging_out = NULL;
3305     Inkscape::NodePath::Node *prev;
3306     if (next) {
3307         g_assert(g_list_find(sp->nodes, next));
3308         prev = next->p.other;
3309     } else {
3310         prev = sp->last;
3311     }
3313     if (prev)
3314         prev->n.other = n;
3315     else
3316         sp->first = n;
3318     if (next)
3319         next->p.other = n;
3320     else
3321         sp->last = n;
3323     n->p.other = prev;
3324     n->n.other = next;
3326     n->knot = sp_knot_new(sp->nodepath->desktop);
3327     sp_knot_set_position(n->knot, pos, 0);
3328     g_object_set(G_OBJECT(n->knot),
3329                  "anchor", GTK_ANCHOR_CENTER,
3330                  "fill", NODE_FILL,
3331                  "fill_mouseover", NODE_FILL_HI,
3332                  "stroke", NODE_STROKE,
3333                  "stroke_mouseover", NODE_STROKE_HI,
3334                  "tip", _("<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"),
3335                  NULL);
3336     if (n->type == Inkscape::NodePath::NODE_CUSP)
3337         g_object_set(G_OBJECT(n->knot), "shape", SP_KNOT_SHAPE_DIAMOND, "size", 9, NULL);
3338     else
3339         g_object_set(G_OBJECT(n->knot), "shape", SP_KNOT_SHAPE_SQUARE, "size", 7, NULL);
3341     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3342     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3343     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3344     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3345     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3346     sp_knot_show(n->knot);
3348     n->p.knot = sp_knot_new(sp->nodepath->desktop);
3349     sp_knot_set_position(n->p.knot, ppos, 0);
3350     g_object_set(G_OBJECT(n->p.knot),
3351                  "shape", SP_KNOT_SHAPE_CIRCLE,
3352                  "size", 7,
3353                  "anchor", GTK_ANCHOR_CENTER,
3354                  "fill", KNOT_FILL,
3355                  "fill_mouseover", KNOT_FILL_HI,
3356                  "stroke", KNOT_STROKE,
3357                  "stroke_mouseover", KNOT_STROKE_HI,
3358                  "tip", _("<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"),
3359                  NULL);
3360     g_signal_connect(G_OBJECT(n->p.knot), "clicked", G_CALLBACK(node_ctrl_clicked), n);
3361     g_signal_connect(G_OBJECT(n->p.knot), "grabbed", G_CALLBACK(node_ctrl_grabbed), n);
3362     g_signal_connect(G_OBJECT(n->p.knot), "ungrabbed", G_CALLBACK(node_ctrl_ungrabbed), n);
3363     g_signal_connect(G_OBJECT(n->p.knot), "request", G_CALLBACK(node_ctrl_request), n);
3364     g_signal_connect(G_OBJECT(n->p.knot), "moved", G_CALLBACK(node_ctrl_moved), n);
3365     g_signal_connect(G_OBJECT(n->p.knot), "event", G_CALLBACK(node_ctrl_event), n);
3367     sp_knot_hide(n->p.knot);
3368     n->p.line = sp_canvas_item_new(SP_DT_CONTROLS(n->subpath->nodepath->desktop),
3369                                    SP_TYPE_CTRLLINE, NULL);
3370     sp_canvas_item_hide(n->p.line);
3372     n->n.knot = sp_knot_new(sp->nodepath->desktop);
3373     sp_knot_set_position(n->n.knot, npos, 0);
3374     g_object_set(G_OBJECT(n->n.knot),
3375                  "shape", SP_KNOT_SHAPE_CIRCLE,
3376                  "size", 7,
3377                  "anchor", GTK_ANCHOR_CENTER,
3378                  "fill", KNOT_FILL,
3379                  "fill_mouseover", KNOT_FILL_HI,
3380                  "stroke", KNOT_STROKE,
3381                  "stroke_mouseover", KNOT_STROKE_HI,
3382                  "tip", _("<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 the opposite handle in sync"),
3383                  NULL);
3384     g_signal_connect(G_OBJECT(n->n.knot), "clicked", G_CALLBACK(node_ctrl_clicked), n);
3385     g_signal_connect(G_OBJECT(n->n.knot), "grabbed", G_CALLBACK(node_ctrl_grabbed), n);
3386     g_signal_connect(G_OBJECT(n->n.knot), "ungrabbed", G_CALLBACK(node_ctrl_ungrabbed), n);
3387     g_signal_connect(G_OBJECT(n->n.knot), "request", G_CALLBACK(node_ctrl_request), n);
3388     g_signal_connect(G_OBJECT(n->n.knot), "moved", G_CALLBACK(node_ctrl_moved), n);
3389     g_signal_connect(G_OBJECT(n->n.knot), "event", G_CALLBACK(node_ctrl_event), n);
3390     sp_knot_hide(n->n.knot);
3391     n->n.line = sp_canvas_item_new(SP_DT_CONTROLS(n->subpath->nodepath->desktop),
3392                                    SP_TYPE_CTRLLINE, NULL);
3393     sp_canvas_item_hide(n->n.line);
3395     sp->nodes = g_list_prepend(sp->nodes, n);
3397     return n;
3400 /**
3401  * Destroy node and its knots, link neighbors in subpath.
3402  */
3403 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3405     g_assert(node);
3406     g_assert(node->subpath);
3407     g_assert(SP_IS_KNOT(node->knot));
3408     g_assert(SP_IS_KNOT(node->p.knot));
3409     g_assert(SP_IS_KNOT(node->n.knot));
3410     g_assert(g_list_find(node->subpath->nodes, node));
3412    Inkscape::NodePath::SubPath *sp = node->subpath;
3414     if (node->selected) { // first, deselect
3415         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3416         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3417     }
3419     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3421     g_object_unref(G_OBJECT(node->knot));
3422     g_object_unref(G_OBJECT(node->p.knot));
3423     g_object_unref(G_OBJECT(node->n.knot));
3425     gtk_object_destroy(GTK_OBJECT(node->p.line));
3426     gtk_object_destroy(GTK_OBJECT(node->n.line));
3428     if (sp->nodes) { // there are others nodes on the subpath
3429         if (sp->closed) {
3430             if (sp->first == node) {
3431                 g_assert(sp->last == node);
3432                 sp->first = node->n.other;
3433                 sp->last = sp->first;
3434             }
3435             node->p.other->n.other = node->n.other;
3436             node->n.other->p.other = node->p.other;
3437         } else {
3438             if (sp->first == node) {
3439                 sp->first = node->n.other;
3440                 sp->first->code = NR_MOVETO;
3441             }
3442             if (sp->last == node) sp->last = node->p.other;
3443             if (node->p.other) node->p.other->n.other = node->n.other;
3444             if (node->n.other) node->n.other->p.other = node->p.other;
3445         }
3446     } else { // this was the last node on subpath
3447         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3448     }
3450     g_mem_chunk_free(nodechunk, node);
3453 /**
3454  * Returns one of the node's two knots (node sides).
3455  * \param which Indicates which side.
3456  * \return Pointer to previous node side if which==-1, next if which==1.
3457  */
3458 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3460     g_assert(node);
3462     switch (which) {
3463         case -1:
3464             return &node->p;
3465         case 1:
3466             return &node->n;
3467         default:
3468             break;
3469     }
3471     g_assert_not_reached();
3473     return NULL;
3476 /**
3477  * Return knot on other side of node.
3478  */
3479 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3481     g_assert(node);
3483     if (me == &node->p) return &node->n;
3484     if (me == &node->n) return &node->p;
3486     g_assert_not_reached();
3488     return NULL;
3491 /**
3492  * Return NRPathcode on this knot's side of the node.
3493  */
3494 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3496     g_assert(node);
3498     if (me == &node->p) {
3499         if (node->p.other) return (NRPathcode)node->code;
3500         return NR_MOVETO;
3501     }
3503     if (me == &node->n) {
3504         if (node->n.other) return (NRPathcode)node->n.other->code;
3505         return NR_MOVETO;
3506     }
3508     g_assert_not_reached();
3510     return NR_END;
3513 /**
3514  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3515  */
3516 Inkscape::NodePath::Node * 
3517 sp_nodepath_get_node_by_index(int index)
3519     Inkscape::NodePath::Node *e = NULL;
3520     
3521     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3522     if (!nodepath) {
3523         return e;
3524     }
3526     //find segment
3527     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3528         
3529         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3530         int n = g_list_length(sp->nodes);
3531         if (sp->closed) {
3532             n++;
3533         } 
3534         
3535         //if the piece belongs to this subpath grab it
3536         //otherwise move onto the next subpath
3537         if (index < n) {
3538             e = sp->first;
3539             for (int i = 0; i < index; ++i) {
3540                 e = e->n.other;
3541             }
3542             break;
3543         } else {
3544             if (sp->closed) {
3545                 index -= (n+1);
3546             } else {
3547                 index -= n;
3548             }
3549         }
3550     }
3551     
3552     return e;
3555 /**
3556  * Returns plain text meaning of node type.
3557  */
3558 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3560     unsigned retracted = 0;
3561     bool endnode = false;
3563     for (int which = -1; which <= 1; which += 2) {
3564         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3565         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3566             retracted ++;
3567         if (!side->other)
3568             endnode = true;
3569     }
3571     if (retracted == 0) {
3572         if (endnode) {
3573                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3574                 return _("end node");
3575         } else {
3576             switch (node->type) {
3577                 case Inkscape::NodePath::NODE_CUSP:
3578                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3579                     return _("cusp");
3580                 case Inkscape::NodePath::NODE_SMOOTH:
3581                     // TRANSLATORS: "smooth" is an adjective here
3582                     return _("smooth");
3583                 case Inkscape::NodePath::NODE_SYMM:
3584                     return _("symmetric");
3585             }
3586         }
3587     } else if (retracted == 1) {
3588         if (endnode) {
3589             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3590             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3591         } else {
3592             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3593         }
3594     } else {
3595         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3596     }
3598     return NULL;
3601 /**
3602  * Handles content of statusbar as long as node tool is active.
3603  */
3604 void
3605 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3607     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>arrow</b> keys to move nodes");
3608     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3610     gint total = 0;
3611     gint selected = 0;
3612     SPDesktop *desktop = NULL;
3614     if (nodepath) {
3615         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3616             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3617             total += g_list_length(subpath->nodes);
3618         }
3619         selected = g_list_length(nodepath->selected);
3620         desktop = nodepath->desktop;
3621     } else {
3622         desktop = SP_ACTIVE_DESKTOP;
3623     }
3625     SPEventContext *ec = desktop->event_context;
3626     if (!ec) return;
3627     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3628     if (!mc) return;
3630     if (selected == 0) {
3631         Inkscape::Selection *sel = desktop->selection;
3632         if (!sel || sel->isEmpty()) {
3633             mc->setF(Inkscape::NORMAL_MESSAGE,
3634                      _("Select a single object to edit its nodes or handles."));
3635         } else {
3636             if (nodepath) {
3637             mc->setF(Inkscape::NORMAL_MESSAGE,
3638                      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.",
3639                               "<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.",
3640                               total),
3641                      total);
3642             } else {
3643                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3644                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3645                 } else {
3646                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3647                 }
3648             }
3649         }
3650     } else if (nodepath && selected == 1) {
3651         mc->setF(Inkscape::NORMAL_MESSAGE,
3652                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3653                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3654                           total),
3655                  selected, total, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3656     } else {
3657         mc->setF(Inkscape::NORMAL_MESSAGE,
3658                  ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
3659                           "<b>%i</b> of <b>%i</b> nodes selected. %s.",
3660                           total),
3661                  selected, total, when_selected);
3662     }
3666 /*
3667   Local Variables:
3668   mode:c++
3669   c-file-style:"stroustrup"
3670   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3671   indent-tabs-mode:nil
3672   fill-column:99
3673   End:
3674 */
3675 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :