Code

bug 1441767: allow device fonts (eg opentype with postscript outlines or plain postsc...
[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 || e->p.other->selected))) {
1229         force = TRUE;
1230     }
1231     sp_nodepath_node_select(e, (gboolean) toggle, force);
1232     if (e->p.other)
1233         sp_nodepath_node_select(e->p.other, TRUE, force);
1235     sp_nodepath_ensure_ctrls(nodepath);
1237     sp_nodepath_update_statusbar(nodepath);
1240 /**
1241  * Add a node nearest to point
1242  */
1243 void
1244 sp_nodepath_add_node_near_point(SPItem * item, NR::Point p)
1246     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1247     if (!nodepath) {
1248         return;
1249     }
1251     Path::cut_position position = get_nearest_position_on_Path(item, p);
1253     //find segment to split
1254     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1256     //don't know why but t seems to flip for lines
1257     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1258         position.t = 1.0 - position.t;
1259     }
1260     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1261     sp_nodepath_node_select(n, FALSE, TRUE);
1263     /* fixme: adjust ? */
1264     sp_nodepath_ensure_ctrls(nodepath);
1266     update_repr(nodepath);
1268     sp_nodepath_update_statusbar(nodepath);
1271 /*
1272  * Adjusts a segment so that t moves by a certain delta for dragging
1273  * converts lines to curves
1274  *
1275  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1276  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1277  */
1278 void
1279 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta, char * key)
1281     /* feel good is an arbitrary parameter that distributes the delta between handles
1282      * if t of the drag point is less than 1/6 distance form the endpoint only
1283      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1284      */
1285     double feel_good;
1286     if (t <= 1.0 / 6.0)
1287         feel_good = 0;
1288     else if (t <= 0.5)
1289         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1290     else if (t <= 5.0 / 6.0)
1291         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1292     else
1293         feel_good = 1;
1295     //if we're dragging a line convert it to a curve
1296     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1297         sp_nodepath_set_line_type(e, NR_CURVETO);
1298     }
1300     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1301     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1302     e->p.other->n.pos += offsetcoord0;
1303     e->p.pos += offsetcoord1;
1305     // adjust controls of adjacent segments where necessary
1306     sp_node_adjust_knot(e,1);
1307     sp_node_adjust_knot(e->p.other,-1);
1309     sp_nodepath_ensure_ctrls(e->subpath->nodepath);
1311     update_repr_keyed(e->subpath->nodepath, key);
1313     sp_nodepath_update_statusbar(e->subpath->nodepath);
1317 /**
1318  * Call sp_nodepath_break() for all selected segments.
1319  */
1320 void sp_node_selected_break()
1322     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1323     if (!nodepath) return;
1325     GList *temp = NULL;
1326     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1327        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1328        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1329         if (nn == NULL) continue; // no break, no new node
1330         temp = g_list_prepend(temp, nn);
1331     }
1333     if (temp) {
1334         sp_nodepath_deselect(nodepath);
1335     }
1336     for (GList *l = temp; l != NULL; l = l->next) {
1337         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1338     }
1340     sp_nodepath_ensure_ctrls(nodepath);
1342     update_repr(nodepath);
1345 /**
1346  * Duplicate the selected node(s).
1347  */
1348 void sp_node_selected_duplicate()
1350     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1351     if (!nodepath) {
1352         return;
1353     }
1355     GList *temp = NULL;
1356     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1357        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1358        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1359         if (nn == NULL) continue; // could not duplicate
1360         temp = g_list_prepend(temp, nn);
1361     }
1363     if (temp) {
1364         sp_nodepath_deselect(nodepath);
1365     }
1366     for (GList *l = temp; l != NULL; l = l->next) {
1367         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1368     }
1370     sp_nodepath_ensure_ctrls(nodepath);
1372     update_repr(nodepath);
1375 /**
1376  *  Join two nodes by merging them into one.
1377  */
1378 void sp_node_selected_join()
1380     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1381     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1383     if (g_list_length(nodepath->selected) != 2) {
1384         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1385         return;
1386     }
1388    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1389    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1391     g_assert(a != b);
1392     g_assert(a->p.other || a->n.other);
1393     g_assert(b->p.other || b->n.other);
1395     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1396         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1397         return;
1398     }
1400     /* a and b are endpoints */
1402     NR::Point c = (a->pos + b->pos) / 2;
1404     if (a->subpath == b->subpath) {
1405        Inkscape::NodePath::SubPath *sp = a->subpath;
1406         sp_nodepath_subpath_close(sp);
1408         sp_nodepath_ensure_ctrls(sp->nodepath);
1410         update_repr(nodepath);
1412         return;
1413     }
1415     /* a and b are separate subpaths */
1416    Inkscape::NodePath::SubPath *sa = a->subpath;
1417    Inkscape::NodePath::SubPath *sb = b->subpath;
1418     NR::Point p;
1419    Inkscape::NodePath::Node *n;
1420     NRPathcode code;
1421     if (a == sa->first) {
1422         p = sa->first->n.pos;
1423         code = (NRPathcode)sa->first->n.other->code;
1424        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1425         n = sa->last;
1426         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1427         n = n->p.other;
1428         while (n) {
1429             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1430             n = n->p.other;
1431             if (n == sa->first) n = NULL;
1432         }
1433         sp_nodepath_subpath_destroy(sa);
1434         sa = t;
1435     } else if (a == sa->last) {
1436         p = sa->last->p.pos;
1437         code = (NRPathcode)sa->last->code;
1438         sp_nodepath_node_destroy(sa->last);
1439     } else {
1440         code = NR_END;
1441         g_assert_not_reached();
1442     }
1444     if (b == sb->first) {
1445         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1446         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1447             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1448         }
1449     } else if (b == sb->last) {
1450         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1451         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1452             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1453         }
1454     } else {
1455         g_assert_not_reached();
1456     }
1457     /* and now destroy sb */
1459     sp_nodepath_subpath_destroy(sb);
1461     sp_nodepath_ensure_ctrls(sa->nodepath);
1463     update_repr(nodepath);
1465     sp_nodepath_update_statusbar(nodepath);
1468 /**
1469  *  Join two nodes by adding a segment between them.
1470  */
1471 void sp_node_selected_join_segment()
1473     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1474     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1476     if (g_list_length(nodepath->selected) != 2) {
1477         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1478         return;
1479     }
1481    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1482    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1484     g_assert(a != b);
1485     g_assert(a->p.other || a->n.other);
1486     g_assert(b->p.other || b->n.other);
1488     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1489         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1490         return;
1491     }
1493     if (a->subpath == b->subpath) {
1494        Inkscape::NodePath::SubPath *sp = a->subpath;
1496         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1497         sp->closed = TRUE;
1499         sp->first->p.other = sp->last;
1500         sp->last->n.other  = sp->first;
1502         sp_node_control_mirror_p_to_n(sp->last);
1503         sp_node_control_mirror_n_to_p(sp->first);
1505         sp->first->code = sp->last->code;
1506         sp->first       = sp->last;
1508         sp_nodepath_ensure_ctrls(sp->nodepath);
1510         update_repr(nodepath);
1512         return;
1513     }
1515     /* a and b are separate subpaths */
1516    Inkscape::NodePath::SubPath *sa = a->subpath;
1517    Inkscape::NodePath::SubPath *sb = b->subpath;
1519    Inkscape::NodePath::Node *n;
1520     NR::Point p;
1521     NRPathcode code;
1522     if (a == sa->first) {
1523         code = (NRPathcode) sa->first->n.other->code;
1524        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1525         n = sa->last;
1526         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1527         for (n = n->p.other; n != NULL; n = n->p.other) {
1528             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1529         }
1530         sp_nodepath_subpath_destroy(sa);
1531         sa = t;
1532     } else if (a == sa->last) {
1533         code = (NRPathcode)sa->last->code;
1534     } else {
1535         code = NR_END;
1536         g_assert_not_reached();
1537     }
1539     if (b == sb->first) {
1540         n = sb->first;
1541         sp_node_control_mirror_p_to_n(sa->last);
1542         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1543         sp_node_control_mirror_n_to_p(sa->last);
1544         for (n = n->n.other; n != NULL; n = n->n.other) {
1545             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1546         }
1547     } else if (b == sb->last) {
1548         n = sb->last;
1549         sp_node_control_mirror_p_to_n(sa->last);
1550         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1551         sp_node_control_mirror_n_to_p(sa->last);
1552         for (n = n->p.other; n != NULL; n = n->p.other) {
1553             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1554         }
1555     } else {
1556         g_assert_not_reached();
1557     }
1558     /* and now destroy sb */
1560     sp_nodepath_subpath_destroy(sb);
1562     sp_nodepath_ensure_ctrls(sa->nodepath);
1564     update_repr(nodepath);
1567 /**
1568  * Delete one or more selected nodes.
1569  */
1570 void sp_node_selected_delete()
1572     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1573     if (!nodepath) return;
1574     if (!nodepath->selected) return;
1576     /** \todo fixme: do it the right way */
1577     while (nodepath->selected) {
1578        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1579         sp_nodepath_node_destroy(node);
1580     }
1583     //clean up the nodepath (such as for trivial subpaths)
1584     sp_nodepath_cleanup(nodepath);
1586     sp_nodepath_ensure_ctrls(nodepath);
1588     // if the entire nodepath is removed, delete the selected object.
1589     if (nodepath->subpaths == NULL ||
1590         sp_nodepath_get_node_count(nodepath) < 2) {
1591         SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
1592         sp_nodepath_destroy(nodepath);
1593         sp_selection_delete();
1594         sp_document_done (document);
1595         return;
1596     }
1598     update_repr(nodepath);
1600     sp_nodepath_update_statusbar(nodepath);
1603 /**
1604  * Delete one or more segments between two selected nodes.
1605  * This is the code for 'split'.
1606  */
1607 void
1608 sp_node_selected_delete_segment(void)
1610    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
1611    Inkscape::NodePath::Node *curr, *next;     //Iterators
1613     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1614     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1616     if (g_list_length(nodepath->selected) != 2) {
1617         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1618                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1619         return;
1620     }
1622     //Selected nodes, not inclusive
1623    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1624    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1626     if ( ( a==b)                       ||  //same node
1627          (a->subpath  != b->subpath )  ||  //not the same path
1628          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
1629          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
1630     {
1631         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1632                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1633         return;
1634     }
1636     //###########################################
1637     //# BEGIN EDITS
1638     //###########################################
1639     //##################################
1640     //# CLOSED PATH
1641     //##################################
1642     if (a->subpath->closed) {
1645         gboolean reversed = FALSE;
1647         //Since we can go in a circle, we need to find the shorter distance.
1648         //  a->b or b->a
1649         start = end = NULL;
1650         int distance    = 0;
1651         int minDistance = 0;
1652         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1653             if (curr==b) {
1654                 //printf("a to b:%d\n", distance);
1655                 start = a;//go from a to b
1656                 end   = b;
1657                 minDistance = distance;
1658                 //printf("A to B :\n");
1659                 break;
1660             }
1661             distance++;
1662         }
1664         //try again, the other direction
1665         distance = 0;
1666         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1667             if (curr==a) {
1668                 //printf("b to a:%d\n", distance);
1669                 if (distance < minDistance) {
1670                     start    = b;  //we go from b to a
1671                     end      = a;
1672                     reversed = TRUE;
1673                     //printf("B to A\n");
1674                 }
1675                 break;
1676             }
1677             distance++;
1678         }
1681         //Copy everything from 'end' to 'start' to a new subpath
1682        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1683         for (curr=end ; curr ; curr=curr->n.other) {
1684             NRPathcode code = (NRPathcode) curr->code;
1685             if (curr == end)
1686                 code = NR_MOVETO;
1687             sp_nodepath_node_new(t, NULL,
1688                                  (Inkscape::NodePath::NodeType)curr->type, code,
1689                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1690             if (curr == start)
1691                 break;
1692         }
1693         sp_nodepath_subpath_destroy(a->subpath);
1696     }
1700     //##################################
1701     //# OPEN PATH
1702     //##################################
1703     else {
1705         //We need to get the direction of the list between A and B
1706         //Can we walk from a to b?
1707         start = end = NULL;
1708         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1709             if (curr==b) {
1710                 start = a;  //did it!  we go from a to b
1711                 end   = b;
1712                 //printf("A to B\n");
1713                 break;
1714             }
1715         }
1716         if (!start) {//didn't work?  let's try the other direction
1717             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1718                 if (curr==a) {
1719                     start = b;  //did it!  we go from b to a
1720                     end   = a;
1721                     //printf("B to A\n");
1722                     break;
1723                 }
1724             }
1725         }
1726         if (!start) {
1727             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1728                                                      _("Cannot find path between nodes."));
1729             return;
1730         }
1734         //Copy everything after 'end' to a new subpath
1735        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1736         for (curr=end ; curr ; curr=curr->n.other) {
1737             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
1738                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1739         }
1741         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
1742         for (curr = start->n.other ; curr  ; curr=next) {
1743             next = curr->n.other;
1744             sp_nodepath_node_destroy(curr);
1745         }
1747     }
1748     //###########################################
1749     //# END EDITS
1750     //###########################################
1752     //clean up the nodepath (such as for trivial subpaths)
1753     sp_nodepath_cleanup(nodepath);
1755     sp_nodepath_ensure_ctrls(nodepath);
1757     update_repr(nodepath);
1759     // if the entire nodepath is removed, delete the selected object.
1760     if (nodepath->subpaths == NULL ||
1761         sp_nodepath_get_node_count(nodepath) < 2) {
1762         sp_nodepath_destroy(nodepath);
1763         sp_selection_delete();
1764         return;
1765     }
1767     sp_nodepath_update_statusbar(nodepath);
1770 /**
1771  * Call sp_nodepath_set_line() for all selected segments.
1772  */
1773 void
1774 sp_node_selected_set_line_type(NRPathcode code)
1776     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1777     if (nodepath == NULL) return;
1779     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1780        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1781         g_assert(n->selected);
1782         if (n->p.other && n->p.other->selected) {
1783             sp_nodepath_set_line_type(n, code);
1784         }
1785     }
1787     update_repr(nodepath);
1790 /**
1791  * Call sp_nodepath_convert_node_type() for all selected nodes.
1792  */
1793 void
1794 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
1796     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1797     if (nodepath == NULL) return;
1799     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1800         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
1801     }
1803     update_repr(nodepath);
1806 /**
1807  * Change select status of node, update its own and neighbour handles.
1808  */
1809 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
1811     node->selected = selected;
1813     if (selected) {
1814         g_object_set(G_OBJECT(node->knot),
1815                      "fill", NODE_FILL_SEL,
1816                      "fill_mouseover", NODE_FILL_SEL_HI,
1817                      "stroke", NODE_STROKE_SEL,
1818                      "stroke_mouseover", NODE_STROKE_SEL_HI,
1819                      "size", (node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9,
1820                      NULL);
1821     } else {
1822         g_object_set(G_OBJECT(node->knot),
1823                      "fill", NODE_FILL,
1824                      "fill_mouseover", NODE_FILL_HI,
1825                      "stroke", NODE_STROKE,
1826                      "stroke_mouseover", NODE_STROKE_HI,
1827                      "size", (node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7,
1828                      NULL);
1829     }
1831     sp_node_ensure_ctrls(node);
1832     if (node->n.other) sp_node_ensure_ctrls(node->n.other);
1833     if (node->p.other) sp_node_ensure_ctrls(node->p.other);
1836 /**
1837 \brief Select a node
1838 \param node     The node to select
1839 \param incremental   If true, add to selection, otherwise deselect others
1840 \param override   If true, always select this node, otherwise toggle selected status
1841 */
1842 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
1844     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
1846     if (incremental) {
1847         if (override) {
1848             if (!g_list_find(nodepath->selected, node)) {
1849                 nodepath->selected = g_list_append(nodepath->selected, node);
1850             }
1851             sp_node_set_selected(node, TRUE);
1852         } else { // toggle
1853             if (node->selected) {
1854                 g_assert(g_list_find(nodepath->selected, node));
1855                 nodepath->selected = g_list_remove(nodepath->selected, node);
1856             } else {
1857                 g_assert(!g_list_find(nodepath->selected, node));
1858                 nodepath->selected = g_list_append(nodepath->selected, node);
1859             }
1860             sp_node_set_selected(node, !node->selected);
1861         }
1862     } else {
1863         sp_nodepath_deselect(nodepath);
1864         nodepath->selected = g_list_append(nodepath->selected, node);
1865         sp_node_set_selected(node, TRUE);
1866     }
1868     sp_nodepath_update_statusbar(nodepath);
1872 /**
1873 \brief Deselect all nodes in the nodepath
1874 */
1875 void
1876 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
1878     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1880     while (nodepath->selected) {
1881         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
1882         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
1883     }
1884     sp_nodepath_update_statusbar(nodepath);
1887 /**
1888 \brief Select or invert selection of all nodes in the nodepath
1889 */
1890 void
1891 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
1893     if (!nodepath) return;
1895     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1896        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1897         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1898            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1899            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
1900         }
1901     }
1904 /**
1905  * If nothing selected, does the same as sp_nodepath_select_all();
1906  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
1907  * (i.e., similar to "select all in layer", with the "selected" subpaths
1908  * being treated as "layers" in the path).
1909  */
1910 void
1911 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
1913     if (!nodepath) return;
1915     if (g_list_length (nodepath->selected) == 0) {
1916         sp_nodepath_select_all (nodepath, invert);
1917         return;
1918     }
1920     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
1921     GSList *subpaths = NULL;
1923     for (GList *l = copy; l != NULL; l = l->next) {
1924         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1925         Inkscape::NodePath::SubPath *subpath = n->subpath;
1926         if (!g_slist_find (subpaths, subpath))
1927             subpaths = g_slist_prepend (subpaths, subpath);
1928     }
1930     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
1931         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
1932         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1933             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1934             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
1935         }
1936     }
1938     g_slist_free (subpaths);
1939     g_list_free (copy);
1942 /**
1943  * \brief Select the node after the last selected; if none is selected,
1944  * select the first within path.
1945  */
1946 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
1948     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1950    Inkscape::NodePath::Node *last = NULL;
1951     if (nodepath->selected) {
1952         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1953            Inkscape::NodePath::SubPath *subpath, *subpath_next;
1954             subpath = (Inkscape::NodePath::SubPath *) spl->data;
1955             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1956                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1957                 if (node->selected) {
1958                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
1959                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
1960                             if (spl->next) { // there's a next subpath
1961                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
1962                                 last = subpath_next->first;
1963                             } else if (spl->prev) { // there's a previous subpath
1964                                 last = NULL; // to be set later to the first node of first subpath
1965                             } else {
1966                                 last = node->n.other;
1967                             }
1968                         } else {
1969                             last = node->n.other;
1970                         }
1971                     } else {
1972                         if (node->n.other) {
1973                             last = node->n.other;
1974                         } else {
1975                             if (spl->next) { // there's a next subpath
1976                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
1977                                 last = subpath_next->first;
1978                             } else if (spl->prev) { // there's a previous subpath
1979                                 last = NULL; // to be set later to the first node of first subpath
1980                             } else {
1981                                 last = (Inkscape::NodePath::Node *) subpath->first;
1982                             }
1983                         }
1984                     }
1985                 }
1986             }
1987         }
1988         sp_nodepath_deselect(nodepath);
1989     }
1991     if (last) { // there's at least one more node after selected
1992         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
1993     } else { // no more nodes, select the first one in first subpath
1994        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
1995         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
1996     }
1999 /**
2000  * \brief Select the node before the first selected; if none is selected,
2001  * select the last within path
2002  */
2003 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2005     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2007    Inkscape::NodePath::Node *last = NULL;
2008     if (nodepath->selected) {
2009         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2010            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2011             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2012                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2013                 if (node->selected) {
2014                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2015                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2016                             if (spl->prev) { // there's a prev subpath
2017                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2018                                 last = subpath_prev->last;
2019                             } else if (spl->next) { // there's a next subpath
2020                                 last = NULL; // to be set later to the last node of last subpath
2021                             } else {
2022                                 last = node->p.other;
2023                             }
2024                         } else {
2025                             last = node->p.other;
2026                         }
2027                     } else {
2028                         if (node->p.other) {
2029                             last = node->p.other;
2030                         } else {
2031                             if (spl->prev) { // there's a prev subpath
2032                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2033                                 last = subpath_prev->last;
2034                             } else if (spl->next) { // there's a next subpath
2035                                 last = NULL; // to be set later to the last node of last subpath
2036                             } else {
2037                                 last = (Inkscape::NodePath::Node *) subpath->last;
2038                             }
2039                         }
2040                     }
2041                 }
2042             }
2043         }
2044         sp_nodepath_deselect(nodepath);
2045     }
2047     if (last) { // there's at least one more node before selected
2048         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2049     } else { // no more nodes, select the last one in last subpath
2050         GList *spl = g_list_last(nodepath->subpaths);
2051        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2052         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2053     }
2056 /**
2057  * \brief Select all nodes that are within the rectangle.
2058  */
2059 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2061     if (!incremental) {
2062         sp_nodepath_deselect(nodepath);
2063     }
2065     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2066        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2067         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2068            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2070             if (b.contains(node->pos)) {
2071                 sp_nodepath_node_select(node, TRUE, TRUE);
2072             }
2073         }
2074     }
2077 /**
2078 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2079 */
2080 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2082     if (!nodepath->selected) {
2083         return NULL;
2084     }
2086     GList *r = NULL;
2087     guint i = 0;
2088     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2089        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2090         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2091            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2092             i++;
2093             if (node->selected) {
2094                 r = g_list_append(r, GINT_TO_POINTER(i));
2095             }
2096         }
2097     }
2098     return r;
2101 /**
2102 \brief  Restores selection by selecting nodes whose positions are in the list
2103 */
2104 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2106     sp_nodepath_deselect(nodepath);
2108     guint i = 0;
2109     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2110        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2111         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2112            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2113             i++;
2114             if (g_list_find(r, GINT_TO_POINTER(i))) {
2115                 sp_nodepath_node_select(node, TRUE, TRUE);
2116             }
2117         }
2118     }
2122 /**
2123 \brief Adjusts control point according to node type and line code.
2124 */
2125 static void sp_node_adjust_knot(Inkscape::NodePath::Node *node, gint which_adjust)
2127     double len, otherlen, linelen;
2129     g_assert(node);
2131    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2132    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2134     /** \todo fixme: */
2135     if (me->other == NULL) return;
2136     if (other->other == NULL) return;
2138     /* I have line */
2140     NRPathcode mecode, ocode;
2141     if (which_adjust == 1) {
2142         mecode = (NRPathcode)me->other->code;
2143         ocode = (NRPathcode)node->code;
2144     } else {
2145         mecode = (NRPathcode)node->code;
2146         ocode = (NRPathcode)other->other->code;
2147     }
2149     if (mecode == NR_LINETO) return;
2151     /* I am curve */
2153     if (other->other == NULL) return;
2155     /* Other has line */
2157     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2159     NR::Point delta;
2160     if (ocode == NR_LINETO) {
2161         /* other is lineto, we are either smooth or symm */
2162        Inkscape::NodePath::Node *othernode = other->other;
2163         len = NR::L2(me->pos - node->pos);
2164         delta = node->pos - othernode->pos;
2165         linelen = NR::L2(delta);
2166         if (linelen < 1e-18) return;
2168         me->pos = node->pos + (len / linelen)*delta;
2169         sp_knot_set_position(me->knot, &me->pos, 0);
2171         sp_node_ensure_ctrls(node);
2172         return;
2173     }
2175     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2177         me->pos = 2 * node->pos - other->pos;
2178         sp_knot_set_position(me->knot, &me->pos, 0);
2180         sp_node_ensure_ctrls(node);
2181         return;
2182     }
2184     /* We are smooth */
2186     len = NR::L2(me->pos - node->pos);
2187     delta = other->pos - node->pos;
2188     otherlen = NR::L2(delta);
2189     if (otherlen < 1e-18) return;
2191     me->pos = node->pos - (len / otherlen) * delta;
2192     sp_knot_set_position(me->knot, &me->pos, 0);
2194     sp_node_ensure_ctrls(node);
2197 /**
2198  \brief Adjusts control point according to node type and line code
2199  */
2200 static void sp_node_adjust_knots(Inkscape::NodePath::Node *node)
2202     g_assert(node);
2204     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2206     /* we are either smooth or symm */
2208     if (node->p.other == NULL) return;
2210     if (node->n.other == NULL) return;
2212     if (node->code == NR_LINETO) {
2213         if (node->n.other->code == NR_LINETO) return;
2214         sp_node_adjust_knot(node, 1);
2215         sp_node_ensure_ctrls(node);
2216         return;
2217     }
2219     if (node->n.other->code == NR_LINETO) {
2220         if (node->code == NR_LINETO) return;
2221         sp_node_adjust_knot(node, -1);
2222         sp_node_ensure_ctrls(node);
2223         return;
2224     }
2226     /* both are curves */
2228     NR::Point const delta( node->n.pos - node->p.pos );
2230     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2231         node->p.pos = node->pos - delta / 2;
2232         node->n.pos = node->pos + delta / 2;
2233         sp_node_ensure_ctrls(node);
2234         return;
2235     }
2237     /* We are smooth */
2239     double plen = NR::L2(node->p.pos - node->pos);
2240     if (plen < 1e-18) return;
2241     double nlen = NR::L2(node->n.pos - node->pos);
2242     if (nlen < 1e-18) return;
2243     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2244     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2245     sp_node_ensure_ctrls(node);
2248 /**
2249  * Knot events handler callback.
2250  */
2251 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2253     gboolean ret = FALSE;
2254     switch (event->type) {
2255         case GDK_ENTER_NOTIFY:
2256             active_node = n;
2257             break;
2258         case GDK_LEAVE_NOTIFY:
2259             active_node = NULL;
2260             break;
2261         case GDK_KEY_PRESS:
2262             switch (get_group0_keyval (&event->key)) {
2263                 case GDK_space:
2264                     if (event->key.state & GDK_BUTTON1_MASK) {
2265                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2266                         stamp_repr(nodepath);
2267                         ret = TRUE;
2268                     }
2269                     break;
2270                 default:
2271                     break;
2272             }
2273             break;
2274         default:
2275             break;
2276     }
2278     return ret;
2281 /**
2282  * Handle keypress on node; directly called.
2283  */
2284 gboolean node_key(GdkEvent *event)
2286     Inkscape::NodePath::Path *np;
2288     // there is no way to verify nodes so set active_node to nil when deleting!!
2289     if (active_node == NULL) return FALSE;
2291     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2292         gint ret = FALSE;
2293         switch (get_group0_keyval (&event->key)) {
2294             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2295             case GDK_BackSpace:
2296                 np = active_node->subpath->nodepath;
2297                 sp_nodepath_node_destroy(active_node);
2298                 update_repr(np);
2299                 active_node = NULL;
2300                 ret = TRUE;
2301                 break;
2302             case GDK_c:
2303                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2304                 ret = TRUE;
2305                 break;
2306             case GDK_s:
2307                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2308                 ret = TRUE;
2309                 break;
2310             case GDK_y:
2311                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2312                 ret = TRUE;
2313                 break;
2314             case GDK_b:
2315                 sp_nodepath_node_break(active_node);
2316                 ret = TRUE;
2317                 break;
2318         }
2319         return ret;
2320     }
2321     return FALSE;
2324 /**
2325  * Mouseclick on node callback.
2326  */
2327 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2329    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2331     if (state & GDK_CONTROL_MASK) {
2332         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2334         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2335             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2336                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2337             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2338                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2339             } else {
2340                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2341             }
2342             update_repr(nodepath);
2343             sp_nodepath_update_statusbar(nodepath);
2345         } else { //ctrl+alt+click: delete node
2346             sp_nodepath_node_destroy(n);
2347             //clean up the nodepath (such as for trivial subpaths)
2348             sp_nodepath_cleanup(nodepath);
2350             // if the entire nodepath is removed, delete the selected object.
2351             if (nodepath->subpaths == NULL ||
2352                 sp_nodepath_get_node_count(nodepath) < 2) {
2353                 SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
2354                 sp_nodepath_destroy(nodepath);
2355                 sp_selection_delete();
2356                 sp_document_done (document);
2358             } else {
2359                 sp_nodepath_ensure_ctrls(nodepath);
2360                 update_repr(nodepath);
2361                 sp_nodepath_update_statusbar(nodepath);
2362             }
2363         }
2365     } else {
2366         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2367     }
2370 /**
2371  * Mouse grabbed node callback.
2372  */
2373 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2375    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2377     n->origin = knot->pos;
2379     if (!n->selected) {
2380         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2381     }
2384 /**
2385  * Mouse ungrabbed node callback.
2386  */
2387 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2389    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2391    n->dragging_out = NULL;
2393    update_repr(n->subpath->nodepath);
2396 /**
2397  * The point on a line, given by its angle, closest to the given point.
2398  * \param p  A point.
2399  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2400  * \param closest  Pointer to the point struct where the result is stored.
2401  * \todo FIXME: use dot product perhaps?
2402  */
2403 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2405     if (a == HUGE_VAL) { // vertical
2406         *closest = NR::Point(0, (*p)[NR::Y]);
2407     } else {
2408         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2409         (*closest)[NR::Y] = a * (*closest)[NR::X];
2410     }
2413 /**
2414  * Distance from the point to a line given by its angle.
2415  * \param p  A point.
2416  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2417  */
2418 static double point_line_distance(NR::Point *p, double a)
2420     NR::Point c;
2421     point_line_closest(p, a, &c);
2422     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]));
2425 /**
2426  * Callback for node "request" signal.
2427  * \todo fixme: This goes to "moved" event? (lauris)
2428  */
2429 static gboolean
2430 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2432     double yn, xn, yp, xp;
2433     double an, ap, na, pa;
2434     double d_an, d_ap, d_na, d_pa;
2435     gboolean collinear = FALSE;
2436     NR::Point c;
2437     NR::Point pr;
2439    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2441    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2442    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2444        NR::Point mouse = (*p);
2446        if (!n->dragging_out) {
2447            // This is the first drag-out event; find out which handle to drag out
2448            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2449            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2451            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2452                return FALSE;
2454            Inkscape::NodePath::NodeSide *opposite;
2455            if (appr_p > appr_n) { // closer to p
2456                n->dragging_out = &n->p;
2457                opposite = &n->n;
2458                n->code = NR_CURVETO;
2459            } else if (appr_p < appr_n) { // closer to n
2460                n->dragging_out = &n->n;
2461                opposite = &n->p;
2462                n->n.other->code = NR_CURVETO;
2463            } else { // p and n nodes are the same
2464                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2465                    n->dragging_out = &n->p;
2466                    opposite = &n->n;
2467                    n->code = NR_CURVETO;
2468                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2469                    n->dragging_out = &n->n;
2470                    opposite = &n->p;
2471                    n->n.other->code = NR_CURVETO;
2472                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2473                    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);
2474                    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);
2475                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2476                        n->dragging_out = &n->n;
2477                        opposite = &n->p;
2478                        n->n.other->code = NR_CURVETO;
2479                    } else { // closer to other's n handle
2480                        n->dragging_out = &n->p;
2481                        opposite = &n->n;
2482                        n->code = NR_CURVETO;
2483                    }
2484                }
2485            }
2487            // if there's another handle, make sure the one we drag out starts parallel to it
2488            if (opposite->pos != n->pos) {
2489                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2490            }
2491        }
2493        // pass this on to the handle-moved callback
2494        node_ctrl_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2495        sp_node_ensure_ctrls(n);
2496        return TRUE;
2497    }
2499     if (state & GDK_CONTROL_MASK) { // constrained motion
2501         // calculate relative distances of handles
2502         // n handle:
2503         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2504         xn = n->n.pos[NR::X] - n->pos[NR::X];
2505         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2506         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2507             if (n->n.other) { // if there is the next point
2508                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2509                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2510                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2511             }
2512         }
2513         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2514         if (yn < 0) { xn = -xn; yn = -yn; }
2516         // p handle:
2517         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2518         xp = n->p.pos[NR::X] - n->pos[NR::X];
2519         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2520         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2521             if (n->p.other) {
2522                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2523                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2524                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2525             }
2526         }
2527         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2528         if (yp < 0) { xp = -xp; yp = -yp; }
2530         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2531             // sliding on handles, only if at least one of the handles is non-vertical
2532             // (otherwise it's the same as ctrl+drag anyway)
2534             // calculate angles of the control handles
2535             if (xn == 0) {
2536                 if (yn == 0) { // no handle, consider it the continuation of the other one
2537                     an = 0;
2538                     collinear = TRUE;
2539                 }
2540                 else an = 0; // vertical; set the angle to horizontal
2541             } else an = yn/xn;
2543             if (xp == 0) {
2544                 if (yp == 0) { // no handle, consider it the continuation of the other one
2545                     ap = an;
2546                 }
2547                 else ap = 0; // vertical; set the angle to horizontal
2548             } else  ap = yp/xp;
2550             if (collinear) an = ap;
2552             // angles of the perpendiculars; HUGE_VAL means vertical
2553             if (an == 0) na = HUGE_VAL; else na = -1/an;
2554             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2556             //g_print("an %g    ap %g\n", an, ap);
2558             // mouse point relative to the node's original pos
2559             pr = (*p) - n->origin;
2561             // distances to the four lines (two handles and two perpendiculars)
2562             d_an = point_line_distance(&pr, an);
2563             d_na = point_line_distance(&pr, na);
2564             d_ap = point_line_distance(&pr, ap);
2565             d_pa = point_line_distance(&pr, pa);
2567             // find out which line is the closest, save its closest point in c
2568             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2569                 point_line_closest(&pr, an, &c);
2570             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2571                 point_line_closest(&pr, ap, &c);
2572             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2573                 point_line_closest(&pr, na, &c);
2574             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2575                 point_line_closest(&pr, pa, &c);
2576             }
2578             // move the node to the closest point
2579             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2580                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2581                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2583         } else {  // constraining to hor/vert
2585             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2586                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2587             } else { // snap to vert
2588                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2589             }
2590         }
2591     } else { // move freely
2592         sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2593                                         (*p)[NR::X] - n->pos[NR::X],
2594                                         (*p)[NR::Y] - n->pos[NR::Y],
2595                                         (state & GDK_SHIFT_MASK) == 0);
2596     }
2598     n->subpath->nodepath->desktop->scroll_to_point(p);
2600     return TRUE;
2603 /**
2604  * Node handle clicked callback.
2605  */
2606 static void node_ctrl_clicked(SPKnot *knot, guint state, gpointer data)
2608    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2610     if (state & GDK_CONTROL_MASK) { // "delete" handle
2611         if (n->p.knot == knot) {
2612             n->p.pos = n->pos;
2613         } else if (n->n.knot == knot) {
2614             n->n.pos = n->pos;
2615         }
2616         sp_node_ensure_ctrls(n);
2617         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2618         update_repr(nodepath);
2619         sp_nodepath_update_statusbar(nodepath);
2621     } else { // just select or add to selection, depending in Shift
2622         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2623     }
2626 /**
2627  * Node handle grabbed callback.
2628  */
2629 static void node_ctrl_grabbed(SPKnot *knot, guint state, gpointer data)
2631    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2633     if (!n->selected) {
2634         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2635     }
2637     // remember the origin of the control
2638     if (n->p.knot == knot) {
2639         n->p.origin = n->p.pos - n->pos;
2640     } else if (n->n.knot == knot) {
2641         n->n.origin = n->n.pos - n->pos;
2642     } else {
2643         g_assert_not_reached();
2644     }
2648 /**
2649  * Node handle ungrabbed callback.
2650  */
2651 static void node_ctrl_ungrabbed(SPKnot *knot, guint state, gpointer data)
2653    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2655     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
2656     if (n->p.knot == knot) {
2657         n->p.origin.a = 0;
2658         sp_knot_set_position(knot, &n->p.pos, state);
2659     } else if (n->n.knot == knot) {
2660         n->n.origin.a = 0;
2661         sp_knot_set_position(knot, &n->n.pos, state);
2662     } else {
2663         g_assert_not_reached();
2664     }
2666     update_repr(n->subpath->nodepath);
2669 /**
2670  * Node handle "request" signal callback.
2671  */
2672 static gboolean node_ctrl_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2674     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2676     Inkscape::NodePath::NodeSide *me, *opposite;
2677     gint which;
2678     if (n->p.knot == knot) {
2679         me = &n->p;
2680         opposite = &n->n;
2681         which = -1;
2682     } else if (n->n.knot == knot) {
2683         me = &n->n;
2684         opposite = &n->p;
2685         which = 1;
2686     } else {
2687         me = opposite = NULL;
2688         which = 0;
2689         g_assert_not_reached();
2690     }
2692     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
2694     SnapManager const m(n->subpath->nodepath->desktop->namedview);
2696     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
2697         /* We are smooth node adjacent with line */
2698         NR::Point const delta = *p - n->pos;
2699         NR::Coord const len = NR::L2(delta);
2700         Inkscape::NodePath::Node *othernode = opposite->other;
2701         NR::Point const ndelta = n->pos - othernode->pos;
2702         NR::Coord const linelen = NR::L2(ndelta);
2703         if (len > NR_EPSILON && linelen > NR_EPSILON) {
2704             NR::Coord const scal = dot(delta, ndelta) / linelen;
2705             (*p) = n->pos + (scal / linelen) * ndelta;
2706         }
2707         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, ndelta, NULL).getPoint();
2708     } else {
2709         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
2710     }
2712     sp_node_adjust_knot(n, -which);
2714     return FALSE;
2717 /**
2718  * Node handle moved callback.
2719  */
2720 static void node_ctrl_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2722    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2724    Inkscape::NodePath::NodeSide *me;
2725    Inkscape::NodePath::NodeSide *other;
2726     if (n->p.knot == knot) {
2727         me = &n->p;
2728         other = &n->n;
2729     } else if (n->n.knot == knot) {
2730         me = &n->n;
2731         other = &n->p;
2732     } else {
2733         me = NULL;
2734         other = NULL;
2735         g_assert_not_reached();
2736     }
2738     // calculate radial coordinates of the grabbed control, other control, and the mouse point
2739     Radial rme(me->pos - n->pos);
2740     Radial rother(other->pos - n->pos);
2741     Radial rnew(*p - n->pos);
2743     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
2744         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
2745         /* 0 interpreted as "no snapping". */
2747         // The closest PI/snaps angle, starting from zero.
2748         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
2749         if (me->origin.a == HUGE_VAL) {
2750             // ortho doesn't exist: original control was zero length.
2751             rnew.a = a_snapped;
2752         } else {
2753             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
2754              * its opposite and perpendiculars). */
2755             double const a_ortho = me->origin.a + floor((rnew.a - me->origin.a)/(M_PI/2) + 0.5) * (M_PI/2);
2757             // Snap to the closest.
2758             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
2759                        ? a_snapped
2760                        : a_ortho );
2761         }
2762     }
2764     if (state & GDK_MOD1_MASK) {
2765         // lock handle length
2766         rnew.r = me->origin.r;
2767     }
2769     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
2770         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
2771         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
2772         rother.a += rnew.a - rme.a;
2773         other->pos = NR::Point(rother) + n->pos;
2774         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
2775         sp_knot_set_position(other->knot, &other->pos, 0);
2776     }
2778     me->pos = NR::Point(rnew) + n->pos;
2779     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
2781     // this is what sp_knot_set_position does, but without emitting the signal:
2782     // we cannot emit a "moved" signal because we're now processing it
2783     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
2785     knot->desktop->set_coordinate_status(me->pos);
2787     update_object(n->subpath->nodepath);
2789     /* status text */
2790     SPDesktop *desktop = n->subpath->nodepath->desktop;
2791     if (!desktop) return;
2792     SPEventContext *ec = desktop->event_context;
2793     if (!ec) return;
2794     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
2795     if (!mc) return;
2797     double degrees = 180 / M_PI * rnew.a;
2798     if (degrees > 180) degrees -= 360;
2799     if (degrees < -180) degrees += 360;
2800     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
2801         degrees = angle_to_compass (degrees);
2803     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
2805     mc->setF(Inkscape::NORMAL_MESSAGE,
2806          _("<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);
2808     g_string_free(length, TRUE);
2811 /**
2812  * Node handle event callback.
2813  */
2814 static gboolean node_ctrl_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2816     gboolean ret = FALSE;
2817     switch (event->type) {
2818         case GDK_KEY_PRESS:
2819             switch (get_group0_keyval (&event->key)) {
2820                 case GDK_space:
2821                     if (event->key.state & GDK_BUTTON1_MASK) {
2822                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2823                         stamp_repr(nodepath);
2824                         ret = TRUE;
2825                     }
2826                     break;
2827                 default:
2828                     break;
2829             }
2830             break;
2831         default:
2832             break;
2833     }
2835     return ret;
2838 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
2839                                  Radial &rme, Radial &rother, gboolean const both)
2841     rme.a += angle;
2842     if ( both
2843          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2844          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2845     {
2846         rother.a += angle;
2847     }
2850 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
2851                                         Radial &rme, Radial &rother, gboolean const both)
2853     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
2855     gdouble r;
2856     if ( both
2857          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2858          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2859     {
2860         r = MAX(rme.r, rother.r);
2861     } else {
2862         r = rme.r;
2863     }
2865     gdouble const weird_angle = atan2(norm_angle, r);
2866 /* Bulia says norm_angle is just the visible distance that the
2867  * object's end must travel on the screen.  Left as 'angle' for want of
2868  * a better name.*/
2870     rme.a += weird_angle;
2871     if ( both
2872          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2873          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2874     {
2875         rother.a += weird_angle;
2876     }
2879 /**
2880  * Rotate one node.
2881  */
2882 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
2884     Inkscape::NodePath::NodeSide *me, *other;
2885     bool both = false;
2887     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
2888     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
2890     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
2891         me = &(n->p);
2892         other = &(n->n);
2893     } else if (!n->p.other) {
2894         me = &(n->n);
2895         other = &(n->p);
2896     } else {
2897         if (which > 0) { // right handle
2898             if (xn > xp) {
2899                 me = &(n->n);
2900                 other = &(n->p);
2901             } else {
2902                 me = &(n->p);
2903                 other = &(n->n);
2904             }
2905         } else if (which < 0){ // left handle
2906             if (xn <= xp) {
2907                 me = &(n->n);
2908                 other = &(n->p);
2909             } else {
2910                 me = &(n->p);
2911                 other = &(n->n);
2912             }
2913         } else { // both handles
2914             me = &(n->n);
2915             other = &(n->p);
2916             both = true;
2917         }
2918     }
2920     Radial rme(me->pos - n->pos);
2921     Radial rother(other->pos - n->pos);
2923     if (screen) {
2924         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
2925     } else {
2926         node_rotate_one_internal (*n, angle, rme, rother, both);
2927     }
2929     me->pos = n->pos + NR::Point(rme);
2931     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
2932         other->pos =  n->pos + NR::Point(rother);
2933     }
2935     sp_node_ensure_ctrls(n);
2938 /**
2939  * Rotate selected nodes.
2940  */
2941 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
2943     if (!nodepath || !nodepath->selected) return;
2945     if (g_list_length(nodepath->selected) == 1) {
2946        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
2947         node_rotate_one (n, angle, which, screen);
2948     } else {
2949        // rotate as an object:
2951         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
2952         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
2953         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2954             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2955             box.expandTo (n->pos); // contain all selected nodes
2956         }
2958         gdouble rot;
2959         if (screen) {
2960             gdouble const zoom = nodepath->desktop->current_zoom();
2961             gdouble const zmove = angle / zoom;
2962             gdouble const r = NR::L2(box.max() - box.midpoint());
2963             rot = atan2(zmove, r);
2964         } else {
2965             rot = angle;
2966         }
2968         NR::Matrix t =
2969             NR::Matrix (NR::translate(-box.midpoint())) *
2970             NR::Matrix (NR::rotate(rot)) *
2971             NR::Matrix (NR::translate(box.midpoint()));
2973         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2974             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2975             n->pos *= t;
2976             n->n.pos *= t;
2977             n->p.pos *= t;
2978             sp_node_ensure_ctrls(n);
2979         }
2980     }
2982     update_object(nodepath);
2983     /// \todo fixme: use _keyed
2984     update_repr(nodepath);
2987 /**
2988  * Scale one node.
2989  */
2990 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
2992     bool both = false;
2993     Inkscape::NodePath::NodeSide *me, *other;
2995     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
2996     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
2998     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
2999         me = &(n->p);
3000         other = &(n->n);
3001         n->code = NR_CURVETO;
3002     } else if (!n->p.other) {
3003         me = &(n->n);
3004         other = &(n->p);
3005         if (n->n.other)
3006             n->n.other->code = NR_CURVETO;
3007     } else {
3008         if (which > 0) { // right handle
3009             if (xn > xp) {
3010                 me = &(n->n);
3011                 other = &(n->p);
3012                 if (n->n.other)
3013                     n->n.other->code = NR_CURVETO;
3014             } else {
3015                 me = &(n->p);
3016                 other = &(n->n);
3017                 n->code = NR_CURVETO;
3018             }
3019         } else if (which < 0){ // left handle
3020             if (xn <= xp) {
3021                 me = &(n->n);
3022                 other = &(n->p);
3023                 if (n->n.other)
3024                     n->n.other->code = NR_CURVETO;
3025             } else {
3026                 me = &(n->p);
3027                 other = &(n->n);
3028                 n->code = NR_CURVETO;
3029             }
3030         } else { // both handles
3031             me = &(n->n);
3032             other = &(n->p);
3033             both = true;
3034             n->code = NR_CURVETO;
3035             if (n->n.other)
3036                 n->n.other->code = NR_CURVETO;
3037         }
3038     }
3040     Radial rme(me->pos - n->pos);
3041     Radial rother(other->pos - n->pos);
3043     rme.r += grow;
3044     if (rme.r < 0) rme.r = 0;
3045     if (rme.a == HUGE_VAL) {
3046         if (me->other) { // if direction is unknown, initialize it towards the next node
3047             Radial rme_next(me->other->pos - n->pos);
3048             rme.a = rme_next.a;
3049         } else { // if there's no next, initialize to 0
3050             rme.a = 0;
3051         }
3052     }
3053     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3054         rother.r += grow;
3055         if (rother.r < 0) rother.r = 0;
3056         if (rother.a == HUGE_VAL) {
3057             rother.a = rme.a + M_PI;
3058         }
3059     }
3061     me->pos = n->pos + NR::Point(rme);
3063     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3064         other->pos = n->pos + NR::Point(rother);
3065     }
3067     sp_node_ensure_ctrls(n);
3070 /**
3071  * Scale selected nodes.
3072  */
3073 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3075     if (!nodepath || !nodepath->selected) return;
3077     if (g_list_length(nodepath->selected) == 1) {
3078         // scale handles of the single selected node
3079         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3080         node_scale_one (n, grow, which);
3081     } else {
3082         // scale nodes as an "object":
3084         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3085         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3086         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3087             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3088             box.expandTo (n->pos); // contain all selected nodes
3089         }
3091         double scale = (box.maxExtent() + grow)/box.maxExtent();
3093         NR::Matrix t =
3094             NR::Matrix (NR::translate(-box.midpoint())) *
3095             NR::Matrix (NR::scale(scale, scale)) *
3096             NR::Matrix (NR::translate(box.midpoint()));
3098         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3099             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3100             n->pos *= t;
3101             n->n.pos *= t;
3102             n->p.pos *= t;
3103             sp_node_ensure_ctrls(n);
3104         }
3105     }
3107     update_object(nodepath);
3108     /// \todo fixme: use _keyed
3109     update_repr(nodepath);
3112 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3114     if (!nodepath) return;
3115     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3118 /**
3119  * Flip selected nodes horizontally/vertically.
3120  */
3121 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3123     if (!nodepath || !nodepath->selected) return;
3125     if (g_list_length(nodepath->selected) == 1) {
3126         // flip handles of the single selected node
3127         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3128         double temp = n->p.pos[axis];
3129         n->p.pos[axis] = n->n.pos[axis];
3130         n->n.pos[axis] = temp;
3131         sp_node_ensure_ctrls(n);
3132     } else {
3133         // scale nodes as an "object":
3135         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3136         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3137         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3138             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3139             box.expandTo (n->pos); // contain all selected nodes
3140         }
3142         NR::Matrix t =
3143             NR::Matrix (NR::translate(-box.midpoint())) *
3144             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3145             NR::Matrix (NR::translate(box.midpoint()));
3147         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3148             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3149             n->pos *= t;
3150             n->n.pos *= t;
3151             n->p.pos *= t;
3152             sp_node_ensure_ctrls(n);
3153         }
3154     }
3156     update_object(nodepath);
3157     /// \todo fixme: use _keyed
3158     update_repr(nodepath);
3161 //-----------------------------------------------
3162 /**
3163  * Return new subpath under given nodepath.
3164  */
3165 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3167     g_assert(nodepath);
3168     g_assert(nodepath->desktop);
3170    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3172     s->nodepath = nodepath;
3173     s->closed = FALSE;
3174     s->nodes = NULL;
3175     s->first = NULL;
3176     s->last = NULL;
3178     // do not use prepend here because:
3179     // if you have a path like "subpath_1 subpath_2 ... subpath_k" in the svg, you end up with
3180     // subpath_k -> ... ->subpath_1 in the nodepath structure. thus the i-th node of the svg is not
3181     // the i-th node in the nodepath (only if there are multiple subpaths)
3182     // note that the problem only arise when called from subpath_from_bpath(), since for all the other
3183     // cases, the repr is updated after the call to sp_nodepath_subpath_new()
3184     nodepath->subpaths = g_list_append /*g_list_prepend*/ (nodepath->subpaths, s);
3186     return s;
3189 /**
3190  * Destroy nodes in subpath, then subpath itself.
3191  */
3192 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3194     g_assert(subpath);
3195     g_assert(subpath->nodepath);
3196     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3198     while (subpath->nodes) {
3199         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3200     }
3202     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3204     g_free(subpath);
3207 /**
3208  * Link head to tail in subpath.
3209  */
3210 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3212     g_assert(!sp->closed);
3213     g_assert(sp->last != sp->first);
3214     g_assert(sp->first->code == NR_MOVETO);
3216     sp->closed = TRUE;
3218     //Link the head to the tail
3219     sp->first->p.other = sp->last;
3220     sp->last->n.other  = sp->first;
3221     sp->last->n.pos    = sp->first->n.pos;
3222     sp->first          = sp->last;
3224     //Remove the extra end node
3225     sp_nodepath_node_destroy(sp->last->n.other);
3228 /**
3229  * Open closed (loopy) subpath at node.
3230  */
3231 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3233     g_assert(sp->closed);
3234     g_assert(n->subpath == sp);
3235     g_assert(sp->first == sp->last);
3237     /* We create new startpoint, current node will become last one */
3239    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3240                                                 &n->pos, &n->pos, &n->n.pos);
3243     sp->closed        = FALSE;
3245     //Unlink to make a head and tail
3246     sp->first         = new_path;
3247     sp->last          = n;
3248     n->n.other        = NULL;
3249     new_path->p.other = NULL;
3252 /**
3253  * Returns area in triangle given by points; may be negative.
3254  */
3255 inline double
3256 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3258     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]);
3261 /**
3262  * Return new node in subpath with given properties.
3263  * \param pos Position of node.
3264  * \param ppos Handle position in previous direction
3265  * \param npos Handle position in previous direction
3266  */
3267 Inkscape::NodePath::Node *
3268 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)
3270     g_assert(sp);
3271     g_assert(sp->nodepath);
3272     g_assert(sp->nodepath->desktop);
3274     if (nodechunk == NULL)
3275         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3277     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3279     n->subpath  = sp;
3281     if (type != Inkscape::NodePath::NODE_NONE) {
3282         // use the type from sodipodi:nodetypes
3283         n->type = type;
3284     } else {
3285         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3286             // points are (almost) collinear
3287             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3288                 // endnode, or a node with a retracted handle
3289                 n->type = Inkscape::NodePath::NODE_CUSP;
3290             } else {
3291                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3292             }
3293         } else {
3294             n->type = Inkscape::NodePath::NODE_CUSP;
3295         }
3296     }
3298     n->code     = code;
3299     n->selected = FALSE;
3300     n->pos      = *pos;
3301     n->p.pos    = *ppos;
3302     n->n.pos    = *npos;
3304     n->dragging_out = NULL;
3306     Inkscape::NodePath::Node *prev;
3307     if (next) {
3308         g_assert(g_list_find(sp->nodes, next));
3309         prev = next->p.other;
3310     } else {
3311         prev = sp->last;
3312     }
3314     if (prev)
3315         prev->n.other = n;
3316     else
3317         sp->first = n;
3319     if (next)
3320         next->p.other = n;
3321     else
3322         sp->last = n;
3324     n->p.other = prev;
3325     n->n.other = next;
3327     n->knot = sp_knot_new(sp->nodepath->desktop);
3328     sp_knot_set_position(n->knot, pos, 0);
3329     g_object_set(G_OBJECT(n->knot),
3330                  "anchor", GTK_ANCHOR_CENTER,
3331                  "fill", NODE_FILL,
3332                  "fill_mouseover", NODE_FILL_HI,
3333                  "stroke", NODE_STROKE,
3334                  "stroke_mouseover", NODE_STROKE_HI,
3335                  "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"),
3336                  NULL);
3337     if (n->type == Inkscape::NodePath::NODE_CUSP)
3338         g_object_set(G_OBJECT(n->knot), "shape", SP_KNOT_SHAPE_DIAMOND, "size", 9, NULL);
3339     else
3340         g_object_set(G_OBJECT(n->knot), "shape", SP_KNOT_SHAPE_SQUARE, "size", 7, NULL);
3342     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3343     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3344     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3345     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3346     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3347     sp_knot_show(n->knot);
3349     n->p.knot = sp_knot_new(sp->nodepath->desktop);
3350     sp_knot_set_position(n->p.knot, ppos, 0);
3351     g_object_set(G_OBJECT(n->p.knot),
3352                  "shape", SP_KNOT_SHAPE_CIRCLE,
3353                  "size", 7,
3354                  "anchor", GTK_ANCHOR_CENTER,
3355                  "fill", KNOT_FILL,
3356                  "fill_mouseover", KNOT_FILL_HI,
3357                  "stroke", KNOT_STROKE,
3358                  "stroke_mouseover", KNOT_STROKE_HI,
3359                  "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"),
3360                  NULL);
3361     g_signal_connect(G_OBJECT(n->p.knot), "clicked", G_CALLBACK(node_ctrl_clicked), n);
3362     g_signal_connect(G_OBJECT(n->p.knot), "grabbed", G_CALLBACK(node_ctrl_grabbed), n);
3363     g_signal_connect(G_OBJECT(n->p.knot), "ungrabbed", G_CALLBACK(node_ctrl_ungrabbed), n);
3364     g_signal_connect(G_OBJECT(n->p.knot), "request", G_CALLBACK(node_ctrl_request), n);
3365     g_signal_connect(G_OBJECT(n->p.knot), "moved", G_CALLBACK(node_ctrl_moved), n);
3366     g_signal_connect(G_OBJECT(n->p.knot), "event", G_CALLBACK(node_ctrl_event), n);
3368     sp_knot_hide(n->p.knot);
3369     n->p.line = sp_canvas_item_new(SP_DT_CONTROLS(n->subpath->nodepath->desktop),
3370                                    SP_TYPE_CTRLLINE, NULL);
3371     sp_canvas_item_hide(n->p.line);
3373     n->n.knot = sp_knot_new(sp->nodepath->desktop);
3374     sp_knot_set_position(n->n.knot, npos, 0);
3375     g_object_set(G_OBJECT(n->n.knot),
3376                  "shape", SP_KNOT_SHAPE_CIRCLE,
3377                  "size", 7,
3378                  "anchor", GTK_ANCHOR_CENTER,
3379                  "fill", KNOT_FILL,
3380                  "fill_mouseover", KNOT_FILL_HI,
3381                  "stroke", KNOT_STROKE,
3382                  "stroke_mouseover", KNOT_STROKE_HI,
3383                  "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"),
3384                  NULL);
3385     g_signal_connect(G_OBJECT(n->n.knot), "clicked", G_CALLBACK(node_ctrl_clicked), n);
3386     g_signal_connect(G_OBJECT(n->n.knot), "grabbed", G_CALLBACK(node_ctrl_grabbed), n);
3387     g_signal_connect(G_OBJECT(n->n.knot), "ungrabbed", G_CALLBACK(node_ctrl_ungrabbed), n);
3388     g_signal_connect(G_OBJECT(n->n.knot), "request", G_CALLBACK(node_ctrl_request), n);
3389     g_signal_connect(G_OBJECT(n->n.knot), "moved", G_CALLBACK(node_ctrl_moved), n);
3390     g_signal_connect(G_OBJECT(n->n.knot), "event", G_CALLBACK(node_ctrl_event), n);
3391     sp_knot_hide(n->n.knot);
3392     n->n.line = sp_canvas_item_new(SP_DT_CONTROLS(n->subpath->nodepath->desktop),
3393                                    SP_TYPE_CTRLLINE, NULL);
3394     sp_canvas_item_hide(n->n.line);
3396     sp->nodes = g_list_prepend(sp->nodes, n);
3398     return n;
3401 /**
3402  * Destroy node and its knots, link neighbors in subpath.
3403  */
3404 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3406     g_assert(node);
3407     g_assert(node->subpath);
3408     g_assert(SP_IS_KNOT(node->knot));
3409     g_assert(SP_IS_KNOT(node->p.knot));
3410     g_assert(SP_IS_KNOT(node->n.knot));
3411     g_assert(g_list_find(node->subpath->nodes, node));
3413    Inkscape::NodePath::SubPath *sp = node->subpath;
3415     if (node->selected) { // first, deselect
3416         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3417         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3418     }
3420     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3422     g_object_unref(G_OBJECT(node->knot));
3423     g_object_unref(G_OBJECT(node->p.knot));
3424     g_object_unref(G_OBJECT(node->n.knot));
3426     gtk_object_destroy(GTK_OBJECT(node->p.line));
3427     gtk_object_destroy(GTK_OBJECT(node->n.line));
3429     if (sp->nodes) { // there are others nodes on the subpath
3430         if (sp->closed) {
3431             if (sp->first == node) {
3432                 g_assert(sp->last == node);
3433                 sp->first = node->n.other;
3434                 sp->last = sp->first;
3435             }
3436             node->p.other->n.other = node->n.other;
3437             node->n.other->p.other = node->p.other;
3438         } else {
3439             if (sp->first == node) {
3440                 sp->first = node->n.other;
3441                 sp->first->code = NR_MOVETO;
3442             }
3443             if (sp->last == node) sp->last = node->p.other;
3444             if (node->p.other) node->p.other->n.other = node->n.other;
3445             if (node->n.other) node->n.other->p.other = node->p.other;
3446         }
3447     } else { // this was the last node on subpath
3448         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3449     }
3451     g_mem_chunk_free(nodechunk, node);
3454 /**
3455  * Returns one of the node's two knots (node sides).
3456  * \param which Indicates which side.
3457  * \return Pointer to previous node side if which==-1, next if which==1.
3458  */
3459 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3461     g_assert(node);
3463     switch (which) {
3464         case -1:
3465             return &node->p;
3466         case 1:
3467             return &node->n;
3468         default:
3469             break;
3470     }
3472     g_assert_not_reached();
3474     return NULL;
3477 /**
3478  * Return knot on other side of node.
3479  */
3480 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3482     g_assert(node);
3484     if (me == &node->p) return &node->n;
3485     if (me == &node->n) return &node->p;
3487     g_assert_not_reached();
3489     return NULL;
3492 /**
3493  * Return NRPathcode on this knot's side of the node.
3494  */
3495 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3497     g_assert(node);
3499     if (me == &node->p) {
3500         if (node->p.other) return (NRPathcode)node->code;
3501         return NR_MOVETO;
3502     }
3504     if (me == &node->n) {
3505         if (node->n.other) return (NRPathcode)node->n.other->code;
3506         return NR_MOVETO;
3507     }
3509     g_assert_not_reached();
3511     return NR_END;
3514 /**
3515  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3516  */
3517 Inkscape::NodePath::Node *
3518 sp_nodepath_get_node_by_index(int index)
3520     Inkscape::NodePath::Node *e = NULL;
3522     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3523     if (!nodepath) {
3524         return e;
3525     }
3527     //find segment
3528     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3530         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3531         int n = g_list_length(sp->nodes);
3532         if (sp->closed) {
3533             n++;
3534         }
3536         //if the piece belongs to this subpath grab it
3537         //otherwise move onto the next subpath
3538         if (index < n) {
3539             e = sp->first;
3540             for (int i = 0; i < index; ++i) {
3541                 e = e->n.other;
3542             }
3543             break;
3544         } else {
3545             if (sp->closed) {
3546                 index -= (n+1);
3547             } else {
3548                 index -= n;
3549             }
3550         }
3551     }
3553     return e;
3556 /**
3557  * Returns plain text meaning of node type.
3558  */
3559 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3561     unsigned retracted = 0;
3562     bool endnode = false;
3564     for (int which = -1; which <= 1; which += 2) {
3565         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3566         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3567             retracted ++;
3568         if (!side->other)
3569             endnode = true;
3570     }
3572     if (retracted == 0) {
3573         if (endnode) {
3574                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3575                 return _("end node");
3576         } else {
3577             switch (node->type) {
3578                 case Inkscape::NodePath::NODE_CUSP:
3579                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3580                     return _("cusp");
3581                 case Inkscape::NodePath::NODE_SMOOTH:
3582                     // TRANSLATORS: "smooth" is an adjective here
3583                     return _("smooth");
3584                 case Inkscape::NodePath::NODE_SYMM:
3585                     return _("symmetric");
3586             }
3587         }
3588     } else if (retracted == 1) {
3589         if (endnode) {
3590             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3591             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3592         } else {
3593             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3594         }
3595     } else {
3596         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3597     }
3599     return NULL;
3602 /**
3603  * Handles content of statusbar as long as node tool is active.
3604  */
3605 void
3606 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3608     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>arrow</b> keys to move nodes");
3609     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3611     gint total = 0;
3612     gint selected = 0;
3613     SPDesktop *desktop = NULL;
3615     if (nodepath) {
3616         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3617             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3618             total += g_list_length(subpath->nodes);
3619         }
3620         selected = g_list_length(nodepath->selected);
3621         desktop = nodepath->desktop;
3622     } else {
3623         desktop = SP_ACTIVE_DESKTOP;
3624     }
3626     SPEventContext *ec = desktop->event_context;
3627     if (!ec) return;
3628     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3629     if (!mc) return;
3631     if (selected == 0) {
3632         Inkscape::Selection *sel = desktop->selection;
3633         if (!sel || sel->isEmpty()) {
3634             mc->setF(Inkscape::NORMAL_MESSAGE,
3635                      _("Select a single object to edit its nodes or handles."));
3636         } else {
3637             if (nodepath) {
3638             mc->setF(Inkscape::NORMAL_MESSAGE,
3639                      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.",
3640                               "<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.",
3641                               total),
3642                      total);
3643             } else {
3644                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3645                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3646                 } else {
3647                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3648                 }
3649             }
3650         }
3651     } else if (nodepath && selected == 1) {
3652         mc->setF(Inkscape::NORMAL_MESSAGE,
3653                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3654                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3655                           total),
3656                  selected, total, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3657     } else {
3658         mc->setF(Inkscape::NORMAL_MESSAGE,
3659                  ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
3660                           "<b>%i</b> of <b>%i</b> nodes selected. %s.",
3661                           total),
3662                  selected, total, when_selected);
3663     }
3667 /*
3668   Local Variables:
3669   mode:c++
3670   c-file-style:"stroustrup"
3671   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3672   indent-tabs-mode:nil
3673   fill-column:99
3674   End:
3675 */
3676 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :