Code

0a66ff85be99e3af2397e2deec04f2719f580a83
[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);
113 static gboolean node_ctrl_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
115 /* Constructors and destructors */
117 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
118 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
119 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
120 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
121 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
122                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
123 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
125 /* Helpers */
127 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
128 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
129 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
131 // active_node indicates mouseover node
132 static Inkscape::NodePath::Node *active_node = NULL;
134 /**
135  * \brief Creates new nodepath from item
136  */
137 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item)
139     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
141     /** \todo
142      * FIXME: remove this. We don't want to edit paths inside flowtext.
143      * Instead we will build our flowtext with cloned paths, so that the
144      * real paths are outside the flowtext and thus editable as usual.
145      */
146     if (SP_IS_FLOWTEXT(item)) {
147         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
148             if SP_IS_FLOWREGION(child) {
149                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
150                 if (grandchild && SP_IS_PATH(grandchild)) {
151                     item = SP_ITEM(grandchild);
152                     break;
153                 }
154             }
155         }
156     }
158     if (!SP_IS_PATH(item))
159         return NULL;
160     SPPath *path = SP_PATH(item);
161     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
162     if (curve == NULL)
163         return NULL;
165     NArtBpath *bpath = sp_curve_first_bpath(curve);
166     gint length = curve->end;
167     if (length == 0)
168         return NULL; // prevent crash for one-node paths
170     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
171     gchar *typestr = parse_nodetypes(nodetypes, length);
173     //Create new nodepath
174     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
175     if (!np)
176         return NULL;
178     // Set defaults
179     np->desktop     = desktop;
180     np->path        = path;
181     np->subpaths    = NULL;
182     np->selected    = NULL;
183     np->nodeContext = NULL; //Let the context that makes this set it
184     np->livarot_path = NULL;
186     // we need to update item's transform from the repr here,
187     // because they may be out of sync when we respond
188     // to a change in repr by regenerating nodepath     --bb
189     sp_object_read_attr(SP_OBJECT(item), "transform");
191     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
192     np->d2i  = np->i2d.inverse();
193     np->repr = repr;
195     /* Now the bitchy part (lauris) */
197     NArtBpath *b = bpath;
199     while (b->code != NR_END) {
200         b = subpath_from_bpath(np, b, typestr + (b - bpath));
201     }
203     g_free(typestr);
204     sp_curve_unref(curve);
206     np->livarot_path = Path_for_item(item, true, true);
207     if (np->livarot_path)
208         np->livarot_path->ConvertWithBackData(0.01);
210     return np;
213 /**
214  * Destroys nodepath's subpaths, then itself, also tell context about it.
215  */
216 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
218     if (!np)  //soft fail, like delete
219         return;
221     while (np->subpaths) {
222         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
223     }
225     //Inform the context that made me, if any, that I am gone.
226     if (np->nodeContext)
227         np->nodeContext->nodepath = NULL;
229     g_assert(!np->selected);
231     if (np->livarot_path) {
232         delete np->livarot_path;
233         np->livarot_path = NULL;
234     }
236     np->desktop = NULL;
238     g_free(np);
242 /**
243  *  Return the node count of a given NodeSubPath.
244  */
245 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
247     if (!subpath)
248         return 0;
249     gint nodeCount = g_list_length(subpath->nodes);
250     return nodeCount;
253 /**
254  *  Return the node count of a given NodePath.
255  */
256 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
258     if (!np)
259         return 0;
260     gint nodeCount = 0;
261     for (GList *item = np->subpaths ; item ; item=item->next) {
262        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
263         nodeCount += g_list_length(subpath->nodes);
264     }
265     return nodeCount;
269 /**
270  * Clean up a nodepath after editing.
271  *
272  * Currently we are deleting trivial subpaths.
273  */
274 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
276     GList *badSubPaths = NULL;
278     //Check all subpaths to be >=2 nodes
279     for (GList *l = nodepath->subpaths; l ; l=l->next) {
280        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
281         if (sp_nodepath_subpath_get_node_count(sp)<2)
282             badSubPaths = g_list_append(badSubPaths, sp);
283     }
285     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
286     //also removes the subpath from nodepath->subpaths
287     for (GList *l = badSubPaths; l ; l=l->next) {
288        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
289         sp_nodepath_subpath_destroy(sp);
290     }
292     g_list_free(badSubPaths);
297 /**
298  * \brief Returns true if the argument nodepath and the d attribute in
299  * its repr do not match.
300  *
301  * This may happen if repr was changed in, e.g., XML editor or by undo.
302  *
303  * \todo
304  * UGLY HACK, think how we can eliminate it.
305  */
306 gboolean nodepath_repr_d_changed(Inkscape::NodePath::Path *np, char const *newd)
308     g_assert(np);
310     SPCurve *curve = create_curve(np);
312     gchar *svgpath = sp_svg_write_path(curve->bpath);
314     char const *attr_d = ( newd
315                            ? newd
316                            : SP_OBJECT(np->path)->repr->attribute("d") );
318     gboolean ret;
319     if (attr_d && svgpath)
320         ret = strcmp(attr_d, svgpath);
321     else
322         ret = TRUE;
324     g_free(svgpath);
325     sp_curve_unref(curve);
327     return ret;
330 /**
331  * \brief Returns true if the argument nodepath and the sodipodi:nodetypes
332  * attribute in its repr do not match.
333  *
334  * This may happen if repr was changed in, e.g., the XML editor or by undo.
335  */
336 gboolean nodepath_repr_typestr_changed(Inkscape::NodePath::Path *np, char const *newtypestr)
338     g_assert(np);
339     gchar *typestr = create_typestr(np);
340     char const *attr_typestr = ( newtypestr
341                                  ? newtypestr
342                                  : SP_OBJECT(np->path)->repr->attribute("sodipodi:nodetypes") );
343     gboolean const ret = (attr_typestr && strcmp(attr_typestr, typestr));
345     g_free(typestr);
347     return ret;
350 /**
351  * Create new nodepath from b, make it subpath of np.
352  * \param t The node type.
353  * \todo Fixme: t should be a proper type, rather than gchar
354  */
355 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
357     NR::Point ppos, pos, npos;
359     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
361     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
362     bool const closed = (b->code == NR_MOVETO);
364     pos = NR::Point(b->x3, b->y3) * np->i2d;
365     if (b[1].code == NR_CURVETO) {
366         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
367     } else {
368         npos = pos;
369     }
370     Inkscape::NodePath::Node *n;
371     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
372     g_assert(sp->first == n);
373     g_assert(sp->last  == n);
375     b++;
376     t++;
377     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
378         pos = NR::Point(b->x3, b->y3) * np->i2d;
379         if (b->code == NR_CURVETO) {
380             ppos = NR::Point(b->x2, b->y2) * np->i2d;
381         } else {
382             ppos = pos;
383         }
384         if (b[1].code == NR_CURVETO) {
385             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
386         } else {
387             npos = pos;
388         }
389         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
390         b++;
391         t++;
392     }
394     if (closed) sp_nodepath_subpath_close(sp);
396     return b;
399 /**
400  * Convert from sodipodi:nodetypes to new style type string.
401  */
402 static gchar *parse_nodetypes(gchar const *types, gint length)
404     g_assert(length > 0);
406     gchar *typestr = g_new(gchar, length + 1);
408     gint pos = 0;
410     if (types) {
411         for (gint i = 0; types[i] && ( i < length ); i++) {
412             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
413             if (types[i] != '\0') {
414                 switch (types[i]) {
415                     case 's':
416                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
417                         break;
418                     case 'z':
419                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
420                         break;
421                     case 'c':
422                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
423                         break;
424                     default:
425                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
426                         break;
427                 }
428             }
429         }
430     }
432     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
434     return typestr;
437 /**
438  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
439  * updated but repr is not (for speed). Used during curve and node drag.
440  */
441 static void update_object(Inkscape::NodePath::Path *np)
443     g_assert(np);
445     SPCurve *curve = create_curve(np);
447     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
449     sp_curve_unref(curve);
452 /**
453  * Update XML path node with data from path object.
454  */
455 static void update_repr_internal(Inkscape::NodePath::Path *np)
457     g_assert(np);
459     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
461     SPCurve *curve = create_curve(np);
462     gchar *typestr = create_typestr(np);
463     gchar *svgpath = sp_svg_write_path(curve->bpath);
465     repr->setAttribute("d", svgpath);
466     repr->setAttribute("sodipodi:nodetypes", typestr);
468     g_free(svgpath);
469     g_free(typestr);
470     sp_curve_unref(curve);
473 /**
474  * Update XML path node with data from path object, commit changes forever.
475  */
476 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np)
478     update_repr_internal(np);
479     sp_document_done(SP_DT_DOCUMENT(np->desktop));
481     if (np->livarot_path) {
482         delete np->livarot_path;
483         np->livarot_path = NULL;
484     }
486     if (np->path && SP_IS_ITEM(np->path)) {
487         np->livarot_path = Path_for_item (np->path, true, true);
488         if (np->livarot_path)
489             np->livarot_path->ConvertWithBackData(0.01);
490     }
493 /**
494  * Update XML path node with data from path object, commit changes with undo.
495  */
496 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key)
498     update_repr_internal(np);
499     sp_document_maybe_done(SP_DT_DOCUMENT(np->desktop), key);
501     if (np->livarot_path) {
502         delete np->livarot_path;
503         np->livarot_path = NULL;
504     }
506     if (np->path && SP_IS_ITEM(np->path)) {
507         np->livarot_path = Path_for_item (np->path, true, true);
508         if (np->livarot_path)
509             np->livarot_path->ConvertWithBackData(0.01);
510     }
513 /**
514  * Make duplicate of path, replace corresponding XML node in tree, commit.
515  */
516 static void stamp_repr(Inkscape::NodePath::Path *np)
518     g_assert(np);
520     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
521     Inkscape::XML::Node *new_repr = old_repr->duplicate();
523     // remember the position of the item
524     gint pos = old_repr->position();
525     // remember parent
526     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
528     SPCurve *curve = create_curve(np);
529     gchar *typestr = create_typestr(np);
531     gchar *svgpath = sp_svg_write_path(curve->bpath);
533     new_repr->setAttribute("d", svgpath);
534     new_repr->setAttribute("sodipodi:nodetypes", typestr);
536     // add the new repr to the parent
537     parent->appendChild(new_repr);
538     // move to the saved position
539     new_repr->setPosition(pos > 0 ? pos : 0);
541     sp_document_done(SP_DT_DOCUMENT(np->desktop));
543     Inkscape::GC::release(new_repr);
544     g_free(svgpath);
545     g_free(typestr);
546     sp_curve_unref(curve);
549 /**
550  * Create curve from path.
551  */
552 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
554     SPCurve *curve = sp_curve_new();
556     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
557        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
558         sp_curve_moveto(curve,
559                         sp->first->pos * np->d2i);
560        Inkscape::NodePath::Node *n = sp->first->n.other;
561         while (n) {
562             NR::Point const end_pt = n->pos * np->d2i;
563             switch (n->code) {
564                 case NR_LINETO:
565                     sp_curve_lineto(curve, end_pt);
566                     break;
567                 case NR_CURVETO:
568                     sp_curve_curveto(curve,
569                                      n->p.other->n.pos * np->d2i,
570                                      n->p.pos * np->d2i,
571                                      end_pt);
572                     break;
573                 default:
574                     g_assert_not_reached();
575                     break;
576             }
577             if (n != sp->last) {
578                 n = n->n.other;
579             } else {
580                 n = NULL;
581             }
582         }
583         if (sp->closed) {
584             sp_curve_closepath(curve);
585         }
586     }
588     return curve;
591 /**
592  * Convert path type string to sodipodi:nodetypes style.
593  */
594 static gchar *create_typestr(Inkscape::NodePath::Path *np)
596     gchar *typestr = g_new(gchar, 32);
597     gint len = 32;
598     gint pos = 0;
600     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
601        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
603         if (pos >= len) {
604             typestr = g_renew(gchar, typestr, len + 32);
605             len += 32;
606         }
608         typestr[pos++] = 'c';
610        Inkscape::NodePath::Node *n;
611         n = sp->first->n.other;
612         while (n) {
613             gchar code;
615             switch (n->type) {
616                 case Inkscape::NodePath::NODE_CUSP:
617                     code = 'c';
618                     break;
619                 case Inkscape::NodePath::NODE_SMOOTH:
620                     code = 's';
621                     break;
622                 case Inkscape::NodePath::NODE_SYMM:
623                     code = 'z';
624                     break;
625                 default:
626                     g_assert_not_reached();
627                     code = '\0';
628                     break;
629             }
631             if (pos >= len) {
632                 typestr = g_renew(gchar, typestr, len + 32);
633                 len += 32;
634             }
636             typestr[pos++] = code;
638             if (n != sp->last) {
639                 n = n->n.other;
640             } else {
641                 n = NULL;
642             }
643         }
644     }
646     if (pos >= len) {
647         typestr = g_renew(gchar, typestr, len + 1);
648         len += 1;
649     }
651     typestr[pos++] = '\0';
653     return typestr;
656 /**
657  * Returns current path in context.
658  */
659 static Inkscape::NodePath::Path *sp_nodepath_current()
661     if (!SP_ACTIVE_DESKTOP) {
662         return NULL;
663     }
665     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
667     if (!SP_IS_NODE_CONTEXT(event_context)) {
668         return NULL;
669     }
671     return SP_NODE_CONTEXT(event_context)->nodepath;
676 /**
677  \brief Fills node and control positions for three nodes, splitting line
678   marked by end at distance t.
679  */
680 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
682     g_assert(new_path != NULL);
683     g_assert(end      != NULL);
685     g_assert(end->p.other == new_path);
686    Inkscape::NodePath::Node *start = new_path->p.other;
687     g_assert(start);
689     if (end->code == NR_LINETO) {
690         new_path->type =Inkscape::NodePath::NODE_CUSP;
691         new_path->code = NR_LINETO;
692         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
693     } else {
694         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
695         new_path->code = NR_CURVETO;
696         gdouble s      = 1 - t;
697         for (int dim = 0; dim < 2; dim++) {
698             NR::Coord const f000 = start->pos[dim];
699             NR::Coord const f001 = start->n.pos[dim];
700             NR::Coord const f011 = end->p.pos[dim];
701             NR::Coord const f111 = end->pos[dim];
702             NR::Coord const f00t = s * f000 + t * f001;
703             NR::Coord const f01t = s * f001 + t * f011;
704             NR::Coord const f11t = s * f011 + t * f111;
705             NR::Coord const f0tt = s * f00t + t * f01t;
706             NR::Coord const f1tt = s * f01t + t * f11t;
707             NR::Coord const fttt = s * f0tt + t * f1tt;
708             start->n.pos[dim]    = f00t;
709             new_path->p.pos[dim] = f0tt;
710             new_path->pos[dim]   = fttt;
711             new_path->n.pos[dim] = f1tt;
712             end->p.pos[dim]      = f11t;
713         }
714     }
717 /**
718  * Adds new node on direct line between two nodes, activates handles of all
719  * three nodes.
720  */
721 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
723     g_assert(end);
724     g_assert(end->subpath);
725     g_assert(g_list_find(end->subpath->nodes, end));
727    Inkscape::NodePath::Node *start = end->p.other;
728     g_assert( start->n.other == end );
729    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
730                                                end,
731                                               Inkscape::NodePath::NODE_SMOOTH,
732                                                (NRPathcode)end->code,
733                                                &start->pos, &start->pos, &start->n.pos);
734     sp_nodepath_line_midpoint(newnode, end, t);
736     sp_node_ensure_ctrls(start);
737     sp_node_ensure_ctrls(newnode);
738     sp_node_ensure_ctrls(end);
740     return newnode;
743 /**
744 \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
745 */
746 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
748     g_assert(node);
749     g_assert(node->subpath);
750     g_assert(g_list_find(node->subpath->nodes, node));
752    Inkscape::NodePath::SubPath *sp = node->subpath;
753     Inkscape::NodePath::Path *np    = sp->nodepath;
755     if (sp->closed) {
756         sp_nodepath_subpath_open(sp, node);
757         return sp->first;
758     } else {
759         // no break for end nodes
760         if (node == sp->first) return NULL;
761         if (node == sp->last ) return NULL;
763         // create a new subpath
764        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
766         // duplicate the break node as start of the new subpath
767        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
769         while (node->n.other) { // copy the remaining nodes into the new subpath
770            Inkscape::NodePath::Node *n  = node->n.other;
771            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);
772             if (n->selected) {
773                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
774             }
775             sp_nodepath_node_destroy(n); // remove the point on the original subpath
776         }
778         return newnode;
779     }
782 /**
783  * Duplicate node and connect to neighbours.
784  */
785 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
787     g_assert(node);
788     g_assert(node->subpath);
789     g_assert(g_list_find(node->subpath->nodes, node));
791    Inkscape::NodePath::SubPath *sp = node->subpath;
793     NRPathcode code = (NRPathcode) node->code;
794     if (code == NR_MOVETO) { // if node is the endnode,
795         node->code = NR_LINETO; // new one is inserted before it, so change that to line
796     }
798     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
800     if (!node->n.other || !node->p.other) // if node is an endnode, select it
801         return node;
802     else
803         return newnode; // otherwise select the newly created node
806 static void sp_node_control_mirror_n_to_p(Inkscape::NodePath::Node *node)
808     node->p.pos = (node->pos + (node->pos - node->n.pos));
811 static void sp_node_control_mirror_p_to_n(Inkscape::NodePath::Node *node)
813     node->n.pos = (node->pos + (node->pos - node->p.pos));
816 /**
817  * Change line type at node, with side effects on neighbours.
818  */
819 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
821     g_assert(end);
822     g_assert(end->subpath);
823     g_assert(end->p.other);
825     if (end->code == static_cast< guint > ( code ) )
826         return;
828    Inkscape::NodePath::Node *start = end->p.other;
830     end->code = code;
832     if (code == NR_LINETO) {
833         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
834         if (end->n.other) {
835             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
836         }
837         sp_node_adjust_knot(start, -1);
838         sp_node_adjust_knot(end, 1);
839     } else {
840         NR::Point delta = end->pos - start->pos;
841         start->n.pos = start->pos + delta / 3;
842         end->p.pos = end->pos - delta / 3;
843         sp_node_adjust_knot(start, 1);
844         sp_node_adjust_knot(end, -1);
845     }
847     sp_node_ensure_ctrls(start);
848     sp_node_ensure_ctrls(end);
851 /**
852  * Change node type, and its handles accordingly.
853  */
854 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeType type)
856     g_assert(node);
857     g_assert(node->subpath);
859     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
860         return node;
862     if ((node->p.other != NULL) && (node->n.other != NULL)) {
863         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
864             type =Inkscape::NodePath::NODE_CUSP;
865         }
866     }
868     node->type = type;
870     if (node->type == Inkscape::NodePath::NODE_CUSP) {
871         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
872         node->knot->setSize (node->selected? 11 : 9);
873         sp_knot_update_ctrl(node->knot);
874     } else {
875         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
876         node->knot->setSize (node->selected? 9 : 7);
877         sp_knot_update_ctrl(node->knot);
878     }
880     sp_node_adjust_knots(node);
882     sp_nodepath_update_statusbar(node->subpath->nodepath);
884     return node;
887 /**
888  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
889  * adjacent segments from lines to curves.
890 */
891 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
893     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
894         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
895             // convert adjacent segment BEFORE to curve
896             node->code = NR_CURVETO;
897             NR::Point delta;
898             if (node->n.other != NULL)
899                 delta = node->n.other->pos - node->p.other->pos;
900             else
901                 delta = node->pos - node->p.other->pos;
902             node->p.pos = node->pos - delta / 4;
903             sp_node_ensure_ctrls(node);
904         }
906         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
907             // convert adjacent segment AFTER to curve
908             node->n.other->code = NR_CURVETO;
909             NR::Point delta;
910             if (node->p.other != NULL)
911                 delta = node->p.other->pos - node->n.other->pos;
912             else
913                 delta = node->pos - node->n.other->pos;
914             node->n.pos = node->pos - delta / 4;
915             sp_node_ensure_ctrls(node);
916         }
917     }
919     sp_nodepath_set_node_type (node, type);
922 /**
923  * Move node to point, and adjust its and neighbouring handles.
924  */
925 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
927     NR::Point delta = p - node->pos;
928     node->pos = p;
930     node->p.pos += delta;
931     node->n.pos += delta;
933     if (node->p.other) {
934         if (node->code == NR_LINETO) {
935             sp_node_adjust_knot(node, 1);
936             sp_node_adjust_knot(node->p.other, -1);
937         }
938     }
939     if (node->n.other) {
940         if (node->n.other->code == NR_LINETO) {
941             sp_node_adjust_knot(node, -1);
942             sp_node_adjust_knot(node->n.other, 1);
943         }
944     }
946     sp_node_ensure_ctrls(node);
949 /**
950  * Call sp_node_moveto() for node selection and handle possible snapping.
951  */
952 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
953                                             bool const snap = true)
955     NR::Coord best[2] = { NR_HUGE, NR_HUGE };
956     NR::Point delta(dx, dy);
957     NR::Point best_pt = delta;
959     if (snap) {
960         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
961            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
962             NR::Point p = n->pos + delta;
963             for (int dim = 0; dim < 2; dim++) {
964                 NR::Coord dist = namedview_dim_snap(nodepath->desktop->namedview,
965                                                     Inkscape::Snapper::SNAP_POINT, p,
966                                                     NR::Dim2(dim), nodepath->path);
967                 if (dist < best[dim]) {
968                     best[dim] = dist;
969                     best_pt[dim] = p[dim] - n->pos[dim];
970                 }
971             }
972         }
973     }
975     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
976        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
977         sp_node_moveto(n, n->pos + best_pt);
978     }
980     update_object(nodepath);
983 /**
984  * Move node selection to point, adjust its and neighbouring handles,
985  * handle possible snapping, and commit the change with possible undo.
986  */
987 void
988 sp_node_selected_move(gdouble dx, gdouble dy)
990     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
991     if (!nodepath) return;
993     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
995     if (dx == 0) {
996         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
997     } else if (dy == 0) {
998         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
999     } else {
1000         sp_nodepath_update_repr(nodepath);
1001     }
1004 /**
1005  * Move node selection off screen and commit the change.
1006  */
1007 void
1008 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1010     // borrowed from sp_selection_move_screen in selection-chemistry.c
1011     // we find out the current zoom factor and divide deltas by it
1012     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1014     gdouble zoom = desktop->current_zoom();
1015     gdouble zdx = dx / zoom;
1016     gdouble zdy = dy / zoom;
1018     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1019     if (!nodepath) return;
1021     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1023     if (dx == 0) {
1024         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1025     } else if (dy == 0) {
1026         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1027     } else {
1028         sp_nodepath_update_repr(nodepath);
1029     }
1032 /** If they don't yet exist, creates knot and line for the given side of the node */
1033 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1035     if (!side->knot) {
1036         side->knot = sp_knot_new(desktop, _("<b>Node handle</b>: drag to shape the curve; with <b>Ctrl</b> to snap angle; with <b>Alt</b> to lock length; with <b>Shift</b> to rotate both handles"));
1038         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1039         side->knot->setSize (7);
1040         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1041         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1042         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1043         sp_knot_update_ctrl(side->knot);
1045         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_ctrl_clicked), node);
1046         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_ctrl_grabbed), node);
1047         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_ctrl_ungrabbed), node);
1048         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_ctrl_request), node);
1049         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_ctrl_moved), node);
1050         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_ctrl_event), node);
1051     }
1053     if (!side->line) {
1054         side->line = sp_canvas_item_new(SP_DT_CONTROLS(desktop),
1055                                         SP_TYPE_CTRLLINE, NULL);
1056     }
1059 /**
1060  * Ensure knot on side of node is visible/invisible.
1061  */
1062 static void sp_node_ensure_knot(Inkscape::NodePath::Node *node, gint which, gboolean show_knot)
1064     g_assert(node != NULL);
1066    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1067     NRPathcode code = sp_node_path_code_from_side(node, side);
1069     show_knot = show_knot && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1071     if (show_knot) {
1072         if (!side->knot) {
1073             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1074             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1075             side->knot->pos = side->pos;
1076             if (side->knot->item) SP_CTRL(side->knot->item)->moveto(side->pos);
1077             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1078             sp_knot_show(side->knot);
1079         } else {
1080             if (side->knot->pos != side->pos) { // only if it's really moved
1081                 sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1082             }
1083             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1084                 sp_knot_show(side->knot);
1085             }
1086         }
1087         sp_canvas_item_show(side->line);
1088     } else {
1089         if (side->knot) {
1090             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1091                 sp_knot_hide(side->knot);
1092             }
1093         }
1094         if (side->line) {
1095             sp_canvas_item_hide(side->line);
1096         }
1097     }
1100 /**
1101  * Ensure handles on node and neighbours of node are visible if selected.
1102  */
1103 static void sp_node_ensure_ctrls(Inkscape::NodePath::Node *node)
1105     g_assert(node != NULL);
1107     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1108         sp_knot_show(node->knot);
1109     }
1111     sp_knot_set_position(node->knot, &node->pos, 0);
1113     gboolean show_knots = node->selected;
1114     if (node->p.other != NULL) {
1115         if (node->p.other->selected) show_knots = TRUE;
1116     }
1117     if (node->n.other != NULL) {
1118         if (node->n.other->selected) show_knots = TRUE;
1119     }
1121     sp_node_ensure_knot(node, -1, show_knots);
1122     sp_node_ensure_knot(node, 1, show_knots);
1125 /**
1126  * Call sp_node_ensure_ctrls() for all nodes on subpath.
1127  */
1128 static void sp_nodepath_subpath_ensure_ctrls(Inkscape::NodePath::SubPath *subpath)
1130     g_assert(subpath != NULL);
1132     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1133         sp_node_ensure_ctrls((Inkscape::NodePath::Node *) l->data);
1134     }
1137 /**
1138  * Call sp_nodepath_subpath_ensure_ctrls() for all subpaths of nodepath.
1139  */
1140 static void sp_nodepath_ensure_ctrls(Inkscape::NodePath::Path *nodepath)
1142     g_assert(nodepath != NULL);
1144     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1145         sp_nodepath_subpath_ensure_ctrls((Inkscape::NodePath::SubPath *) l->data);
1146     }
1149 /**
1150  * Adds all selected nodes in nodepath to list.
1151  */
1152 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1154     StlConv<Node *>::list(l, selected);
1155 /// \todo this adds a copying, rework when the selection becomes a stl list
1158 /**
1159  * Align selected nodes on the specified axis.
1160  */
1161 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1163     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1164         return;
1165     }
1167     if ( !nodepath->selected->next ) { // only one node selected
1168         return;
1169     }
1170    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1171     NR::Point dest(pNode->pos);
1172     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1173         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1174         if (pNode) {
1175             dest[axis] = pNode->pos[axis];
1176             sp_node_moveto(pNode, dest);
1177         }
1178     }
1179     if (axis == NR::X) {
1180         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1181     } else {
1182         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1183     }
1186 /// Helper struct.
1187 struct NodeSort
1189    Inkscape::NodePath::Node *_node;
1190     NR::Coord _coord;
1191     /// \todo use vectorof pointers instead of calling copy ctor
1192     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1193         _node(node), _coord(node->pos[axis])
1194     {}
1196 };
1198 static bool operator<(NodeSort const &a, NodeSort const &b)
1200     return (a._coord < b._coord);
1203 /**
1204  * Distribute selected nodes on the specified axis.
1205  */
1206 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1208     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1209         return;
1210     }
1212     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1213         return;
1214     }
1216    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1217     std::vector<NodeSort> sorted;
1218     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1219         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1220         if (pNode) {
1221             NodeSort n(pNode, axis);
1222             sorted.push_back(n);
1223             //dest[axis] = pNode->pos[axis];
1224             //sp_node_moveto(pNode, dest);
1225         }
1226     }
1227     std::sort(sorted.begin(), sorted.end());
1228     unsigned int len = sorted.size();
1229     //overall bboxes span
1230     float dist = (sorted.back()._coord -
1231                   sorted.front()._coord);
1232     //new distance between each bbox
1233     float step = (dist) / (len - 1);
1234     float pos = sorted.front()._coord;
1235     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1236           it < sorted.end();
1237           it ++ )
1238     {
1239         NR::Point dest((*it)._node->pos);
1240         dest[axis] = pos;
1241         sp_node_moveto((*it)._node, dest);
1242         pos += step;
1243     }
1245     if (axis == NR::X) {
1246         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1247     } else {
1248         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1249     }
1253 /**
1254  * Call sp_nodepath_line_add_node() for all selected segments.
1255  */
1256 void
1257 sp_node_selected_add_node(void)
1259     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1260     if (!nodepath) {
1261         return;
1262     }
1264     GList *nl = NULL;
1266     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1267        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1268         g_assert(t->selected);
1269         if (t->p.other && t->p.other->selected) {
1270             nl = g_list_prepend(nl, t);
1271         }
1272     }
1274     while (nl) {
1275        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1276        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1277         sp_nodepath_node_select(n, TRUE, FALSE);
1278         nl = g_list_remove(nl, t);
1279     }
1281     /** \todo fixme: adjust ? */
1282     sp_nodepath_ensure_ctrls(nodepath);
1284     sp_nodepath_update_repr(nodepath);
1286     sp_nodepath_update_statusbar(nodepath);
1289 /**
1290  * Select segment nearest to point
1291  */
1292 void
1293 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1295     if (!nodepath) {
1296         return;
1297     }
1299     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1301     //find segment to segment
1302     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1304     gboolean force = FALSE;
1305     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1306         force = TRUE;
1307     }
1308     sp_nodepath_node_select(e, (gboolean) toggle, force);
1309     if (e->p.other)
1310         sp_nodepath_node_select(e->p.other, TRUE, force);
1312     sp_nodepath_ensure_ctrls(nodepath);
1314     sp_nodepath_update_statusbar(nodepath);
1317 /**
1318  * Add a node nearest to point
1319  */
1320 void
1321 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1323     if (!nodepath) {
1324         return;
1325     }
1327     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1329     //find segment to split
1330     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1332     //don't know why but t seems to flip for lines
1333     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1334         position.t = 1.0 - position.t;
1335     }
1336     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1337     sp_nodepath_node_select(n, FALSE, TRUE);
1339     /* fixme: adjust ? */
1340     sp_nodepath_ensure_ctrls(nodepath);
1342     sp_nodepath_update_repr(nodepath);
1344     sp_nodepath_update_statusbar(nodepath);
1347 /*
1348  * Adjusts a segment so that t moves by a certain delta for dragging
1349  * converts lines to curves
1350  *
1351  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1352  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1353  */
1354 void
1355 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1357     /* feel good is an arbitrary parameter that distributes the delta between handles
1358      * if t of the drag point is less than 1/6 distance form the endpoint only
1359      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1360      */
1361     double feel_good;
1362     if (t <= 1.0 / 6.0)
1363         feel_good = 0;
1364     else if (t <= 0.5)
1365         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1366     else if (t <= 5.0 / 6.0)
1367         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1368     else
1369         feel_good = 1;
1371     //if we're dragging a line convert it to a curve
1372     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1373         sp_nodepath_set_line_type(e, NR_CURVETO);
1374     }
1376     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1377     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1378     e->p.other->n.pos += offsetcoord0;
1379     e->p.pos += offsetcoord1;
1381     // adjust controls of adjacent segments where necessary
1382     sp_node_adjust_knot(e,1);
1383     sp_node_adjust_knot(e->p.other,-1);
1385     sp_nodepath_ensure_ctrls(e->subpath->nodepath);
1387     update_object(e->subpath->nodepath);
1389     sp_nodepath_update_statusbar(e->subpath->nodepath);
1393 /**
1394  * Call sp_nodepath_break() for all selected segments.
1395  */
1396 void sp_node_selected_break()
1398     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1399     if (!nodepath) return;
1401     GList *temp = NULL;
1402     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1403        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1404        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1405         if (nn == NULL) continue; // no break, no new node
1406         temp = g_list_prepend(temp, nn);
1407     }
1409     if (temp) {
1410         sp_nodepath_deselect(nodepath);
1411     }
1412     for (GList *l = temp; l != NULL; l = l->next) {
1413         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1414     }
1416     sp_nodepath_ensure_ctrls(nodepath);
1418     sp_nodepath_update_repr(nodepath);
1421 /**
1422  * Duplicate the selected node(s).
1423  */
1424 void sp_node_selected_duplicate()
1426     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1427     if (!nodepath) {
1428         return;
1429     }
1431     GList *temp = NULL;
1432     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1433        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1434        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1435         if (nn == NULL) continue; // could not duplicate
1436         temp = g_list_prepend(temp, nn);
1437     }
1439     if (temp) {
1440         sp_nodepath_deselect(nodepath);
1441     }
1442     for (GList *l = temp; l != NULL; l = l->next) {
1443         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1444     }
1446     sp_nodepath_ensure_ctrls(nodepath);
1448     sp_nodepath_update_repr(nodepath);
1451 /**
1452  *  Join two nodes by merging them into one.
1453  */
1454 void sp_node_selected_join()
1456     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1457     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1459     if (g_list_length(nodepath->selected) != 2) {
1460         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1461         return;
1462     }
1464    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1465    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1467     g_assert(a != b);
1468     g_assert(a->p.other || a->n.other);
1469     g_assert(b->p.other || b->n.other);
1471     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1472         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1473         return;
1474     }
1476     /* a and b are endpoints */
1478     NR::Point c = (a->pos + b->pos) / 2;
1480     if (a->subpath == b->subpath) {
1481        Inkscape::NodePath::SubPath *sp = a->subpath;
1482         sp_nodepath_subpath_close(sp);
1484         sp_nodepath_ensure_ctrls(sp->nodepath);
1486         sp_nodepath_update_repr(nodepath);
1488         return;
1489     }
1491     /* a and b are separate subpaths */
1492    Inkscape::NodePath::SubPath *sa = a->subpath;
1493    Inkscape::NodePath::SubPath *sb = b->subpath;
1494     NR::Point p;
1495    Inkscape::NodePath::Node *n;
1496     NRPathcode code;
1497     if (a == sa->first) {
1498         p = sa->first->n.pos;
1499         code = (NRPathcode)sa->first->n.other->code;
1500        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1501         n = sa->last;
1502         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1503         n = n->p.other;
1504         while (n) {
1505             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1506             n = n->p.other;
1507             if (n == sa->first) n = NULL;
1508         }
1509         sp_nodepath_subpath_destroy(sa);
1510         sa = t;
1511     } else if (a == sa->last) {
1512         p = sa->last->p.pos;
1513         code = (NRPathcode)sa->last->code;
1514         sp_nodepath_node_destroy(sa->last);
1515     } else {
1516         code = NR_END;
1517         g_assert_not_reached();
1518     }
1520     if (b == sb->first) {
1521         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1522         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1523             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1524         }
1525     } else if (b == sb->last) {
1526         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1527         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1528             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1529         }
1530     } else {
1531         g_assert_not_reached();
1532     }
1533     /* and now destroy sb */
1535     sp_nodepath_subpath_destroy(sb);
1537     sp_nodepath_ensure_ctrls(sa->nodepath);
1539     sp_nodepath_update_repr(nodepath);
1541     sp_nodepath_update_statusbar(nodepath);
1544 /**
1545  *  Join two nodes by adding a segment between them.
1546  */
1547 void sp_node_selected_join_segment()
1549     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1550     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1552     if (g_list_length(nodepath->selected) != 2) {
1553         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1554         return;
1555     }
1557    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1558    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1560     g_assert(a != b);
1561     g_assert(a->p.other || a->n.other);
1562     g_assert(b->p.other || b->n.other);
1564     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1565         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1566         return;
1567     }
1569     if (a->subpath == b->subpath) {
1570        Inkscape::NodePath::SubPath *sp = a->subpath;
1572         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1573         sp->closed = TRUE;
1575         sp->first->p.other = sp->last;
1576         sp->last->n.other  = sp->first;
1578         sp_node_control_mirror_p_to_n(sp->last);
1579         sp_node_control_mirror_n_to_p(sp->first);
1581         sp->first->code = sp->last->code;
1582         sp->first       = sp->last;
1584         sp_nodepath_ensure_ctrls(sp->nodepath);
1586         sp_nodepath_update_repr(nodepath);
1588         return;
1589     }
1591     /* a and b are separate subpaths */
1592    Inkscape::NodePath::SubPath *sa = a->subpath;
1593    Inkscape::NodePath::SubPath *sb = b->subpath;
1595    Inkscape::NodePath::Node *n;
1596     NR::Point p;
1597     NRPathcode code;
1598     if (a == sa->first) {
1599         code = (NRPathcode) sa->first->n.other->code;
1600        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1601         n = sa->last;
1602         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1603         for (n = n->p.other; n != NULL; n = n->p.other) {
1604             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1605         }
1606         sp_nodepath_subpath_destroy(sa);
1607         sa = t;
1608     } else if (a == sa->last) {
1609         code = (NRPathcode)sa->last->code;
1610     } else {
1611         code = NR_END;
1612         g_assert_not_reached();
1613     }
1615     if (b == sb->first) {
1616         n = sb->first;
1617         sp_node_control_mirror_p_to_n(sa->last);
1618         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1619         sp_node_control_mirror_n_to_p(sa->last);
1620         for (n = n->n.other; n != NULL; n = n->n.other) {
1621             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1622         }
1623     } else if (b == sb->last) {
1624         n = sb->last;
1625         sp_node_control_mirror_p_to_n(sa->last);
1626         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1627         sp_node_control_mirror_n_to_p(sa->last);
1628         for (n = n->p.other; n != NULL; n = n->p.other) {
1629             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1630         }
1631     } else {
1632         g_assert_not_reached();
1633     }
1634     /* and now destroy sb */
1636     sp_nodepath_subpath_destroy(sb);
1638     sp_nodepath_ensure_ctrls(sa->nodepath);
1640     sp_nodepath_update_repr(nodepath);
1643 /**
1644  * Delete one or more selected nodes.
1645  */
1646 void sp_node_selected_delete()
1648     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1649     if (!nodepath) return;
1650     if (!nodepath->selected) return;
1652     /** \todo fixme: do it the right way */
1653     while (nodepath->selected) {
1654        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1655         sp_nodepath_node_destroy(node);
1656     }
1659     //clean up the nodepath (such as for trivial subpaths)
1660     sp_nodepath_cleanup(nodepath);
1662     sp_nodepath_ensure_ctrls(nodepath);
1664     // if the entire nodepath is removed, delete the selected object.
1665     if (nodepath->subpaths == NULL ||
1666         sp_nodepath_get_node_count(nodepath) < 2) {
1667         SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
1668         sp_nodepath_destroy(nodepath);
1669         sp_selection_delete();
1670         sp_document_done (document);
1671         return;
1672     }
1674     sp_nodepath_update_repr(nodepath);
1676     sp_nodepath_update_statusbar(nodepath);
1679 /**
1680  * Delete one or more segments between two selected nodes.
1681  * This is the code for 'split'.
1682  */
1683 void
1684 sp_node_selected_delete_segment(void)
1686    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
1687    Inkscape::NodePath::Node *curr, *next;     //Iterators
1689     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1690     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1692     if (g_list_length(nodepath->selected) != 2) {
1693         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1694                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1695         return;
1696     }
1698     //Selected nodes, not inclusive
1699    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1700    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1702     if ( ( a==b)                       ||  //same node
1703          (a->subpath  != b->subpath )  ||  //not the same path
1704          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
1705          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
1706     {
1707         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1708                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1709         return;
1710     }
1712     //###########################################
1713     //# BEGIN EDITS
1714     //###########################################
1715     //##################################
1716     //# CLOSED PATH
1717     //##################################
1718     if (a->subpath->closed) {
1721         gboolean reversed = FALSE;
1723         //Since we can go in a circle, we need to find the shorter distance.
1724         //  a->b or b->a
1725         start = end = NULL;
1726         int distance    = 0;
1727         int minDistance = 0;
1728         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1729             if (curr==b) {
1730                 //printf("a to b:%d\n", distance);
1731                 start = a;//go from a to b
1732                 end   = b;
1733                 minDistance = distance;
1734                 //printf("A to B :\n");
1735                 break;
1736             }
1737             distance++;
1738         }
1740         //try again, the other direction
1741         distance = 0;
1742         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1743             if (curr==a) {
1744                 //printf("b to a:%d\n", distance);
1745                 if (distance < minDistance) {
1746                     start    = b;  //we go from b to a
1747                     end      = a;
1748                     reversed = TRUE;
1749                     //printf("B to A\n");
1750                 }
1751                 break;
1752             }
1753             distance++;
1754         }
1757         //Copy everything from 'end' to 'start' to a new subpath
1758        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1759         for (curr=end ; curr ; curr=curr->n.other) {
1760             NRPathcode code = (NRPathcode) curr->code;
1761             if (curr == end)
1762                 code = NR_MOVETO;
1763             sp_nodepath_node_new(t, NULL,
1764                                  (Inkscape::NodePath::NodeType)curr->type, code,
1765                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1766             if (curr == start)
1767                 break;
1768         }
1769         sp_nodepath_subpath_destroy(a->subpath);
1772     }
1776     //##################################
1777     //# OPEN PATH
1778     //##################################
1779     else {
1781         //We need to get the direction of the list between A and B
1782         //Can we walk from a to b?
1783         start = end = NULL;
1784         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1785             if (curr==b) {
1786                 start = a;  //did it!  we go from a to b
1787                 end   = b;
1788                 //printf("A to B\n");
1789                 break;
1790             }
1791         }
1792         if (!start) {//didn't work?  let's try the other direction
1793             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1794                 if (curr==a) {
1795                     start = b;  //did it!  we go from b to a
1796                     end   = a;
1797                     //printf("B to A\n");
1798                     break;
1799                 }
1800             }
1801         }
1802         if (!start) {
1803             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1804                                                      _("Cannot find path between nodes."));
1805             return;
1806         }
1810         //Copy everything after 'end' to a new subpath
1811        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1812         for (curr=end ; curr ; curr=curr->n.other) {
1813             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
1814                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1815         }
1817         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
1818         for (curr = start->n.other ; curr  ; curr=next) {
1819             next = curr->n.other;
1820             sp_nodepath_node_destroy(curr);
1821         }
1823     }
1824     //###########################################
1825     //# END EDITS
1826     //###########################################
1828     //clean up the nodepath (such as for trivial subpaths)
1829     sp_nodepath_cleanup(nodepath);
1831     sp_nodepath_ensure_ctrls(nodepath);
1833     sp_nodepath_update_repr(nodepath);
1835     // if the entire nodepath is removed, delete the selected object.
1836     if (nodepath->subpaths == NULL ||
1837         sp_nodepath_get_node_count(nodepath) < 2) {
1838         sp_nodepath_destroy(nodepath);
1839         sp_selection_delete();
1840         return;
1841     }
1843     sp_nodepath_update_statusbar(nodepath);
1846 /**
1847  * Call sp_nodepath_set_line() for all selected segments.
1848  */
1849 void
1850 sp_node_selected_set_line_type(NRPathcode code)
1852     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1853     if (nodepath == NULL) return;
1855     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1856        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1857         g_assert(n->selected);
1858         if (n->p.other && n->p.other->selected) {
1859             sp_nodepath_set_line_type(n, code);
1860         }
1861     }
1863     sp_nodepath_update_repr(nodepath);
1866 /**
1867  * Call sp_nodepath_convert_node_type() for all selected nodes.
1868  */
1869 void
1870 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
1872     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1873     if (nodepath == NULL) return;
1875     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1876         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
1877     }
1879     sp_nodepath_update_repr(nodepath);
1882 /**
1883  * Change select status of node, update its own and neighbour handles.
1884  */
1885 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
1887     node->selected = selected;
1889     if (selected) {
1890         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
1891         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
1892         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
1893         sp_knot_update_ctrl(node->knot);
1894     } else {
1895         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
1896         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
1897         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
1898         sp_knot_update_ctrl(node->knot);
1899     }
1901     sp_node_ensure_ctrls(node);
1902     if (node->n.other) sp_node_ensure_ctrls(node->n.other);
1903     if (node->p.other) sp_node_ensure_ctrls(node->p.other);
1906 /**
1907 \brief Select a node
1908 \param node     The node to select
1909 \param incremental   If true, add to selection, otherwise deselect others
1910 \param override   If true, always select this node, otherwise toggle selected status
1911 */
1912 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
1914     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
1916     if (incremental) {
1917         if (override) {
1918             if (!g_list_find(nodepath->selected, node)) {
1919                 nodepath->selected = g_list_prepend(nodepath->selected, node);
1920             }
1921             sp_node_set_selected(node, TRUE);
1922         } else { // toggle
1923             if (node->selected) {
1924                 g_assert(g_list_find(nodepath->selected, node));
1925                 nodepath->selected = g_list_remove(nodepath->selected, node);
1926             } else {
1927                 g_assert(!g_list_find(nodepath->selected, node));
1928                 nodepath->selected = g_list_prepend(nodepath->selected, node);
1929             }
1930             sp_node_set_selected(node, !node->selected);
1931         }
1932     } else {
1933         sp_nodepath_deselect(nodepath);
1934         nodepath->selected = g_list_prepend(nodepath->selected, node);
1935         sp_node_set_selected(node, TRUE);
1936     }
1938     sp_nodepath_update_statusbar(nodepath);
1942 /**
1943 \brief Deselect all nodes in the nodepath
1944 */
1945 void
1946 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
1948     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1950     while (nodepath->selected) {
1951         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
1952         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
1953     }
1954     sp_nodepath_update_statusbar(nodepath);
1957 /**
1958 \brief Select or invert selection of all nodes in the nodepath
1959 */
1960 void
1961 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
1963     if (!nodepath) return;
1965     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1966        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1967         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1968            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1969            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
1970         }
1971     }
1974 /**
1975  * If nothing selected, does the same as sp_nodepath_select_all();
1976  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
1977  * (i.e., similar to "select all in layer", with the "selected" subpaths
1978  * being treated as "layers" in the path).
1979  */
1980 void
1981 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
1983     if (!nodepath) return;
1985     if (g_list_length (nodepath->selected) == 0) {
1986         sp_nodepath_select_all (nodepath, invert);
1987         return;
1988     }
1990     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
1991     GSList *subpaths = NULL;
1993     for (GList *l = copy; l != NULL; l = l->next) {
1994         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1995         Inkscape::NodePath::SubPath *subpath = n->subpath;
1996         if (!g_slist_find (subpaths, subpath))
1997             subpaths = g_slist_prepend (subpaths, subpath);
1998     }
2000     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2001         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2002         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2003             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2004             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2005         }
2006     }
2008     g_slist_free (subpaths);
2009     g_list_free (copy);
2012 /**
2013  * \brief Select the node after the last selected; if none is selected,
2014  * select the first within path.
2015  */
2016 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2018     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2020    Inkscape::NodePath::Node *last = NULL;
2021     if (nodepath->selected) {
2022         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2023            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2024             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2025             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2026                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2027                 if (node->selected) {
2028                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2029                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2030                             if (spl->next) { // there's a next subpath
2031                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2032                                 last = subpath_next->first;
2033                             } else if (spl->prev) { // there's a previous subpath
2034                                 last = NULL; // to be set later to the first node of first subpath
2035                             } else {
2036                                 last = node->n.other;
2037                             }
2038                         } else {
2039                             last = node->n.other;
2040                         }
2041                     } else {
2042                         if (node->n.other) {
2043                             last = node->n.other;
2044                         } else {
2045                             if (spl->next) { // there's a next subpath
2046                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2047                                 last = subpath_next->first;
2048                             } else if (spl->prev) { // there's a previous subpath
2049                                 last = NULL; // to be set later to the first node of first subpath
2050                             } else {
2051                                 last = (Inkscape::NodePath::Node *) subpath->first;
2052                             }
2053                         }
2054                     }
2055                 }
2056             }
2057         }
2058         sp_nodepath_deselect(nodepath);
2059     }
2061     if (last) { // there's at least one more node after selected
2062         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2063     } else { // no more nodes, select the first one in first subpath
2064        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2065         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2066     }
2069 /**
2070  * \brief Select the node before the first selected; if none is selected,
2071  * select the last within path
2072  */
2073 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2075     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2077    Inkscape::NodePath::Node *last = NULL;
2078     if (nodepath->selected) {
2079         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2080            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2081             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2082                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2083                 if (node->selected) {
2084                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2085                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2086                             if (spl->prev) { // there's a prev subpath
2087                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2088                                 last = subpath_prev->last;
2089                             } else if (spl->next) { // there's a next subpath
2090                                 last = NULL; // to be set later to the last node of last subpath
2091                             } else {
2092                                 last = node->p.other;
2093                             }
2094                         } else {
2095                             last = node->p.other;
2096                         }
2097                     } else {
2098                         if (node->p.other) {
2099                             last = node->p.other;
2100                         } else {
2101                             if (spl->prev) { // there's a prev subpath
2102                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2103                                 last = subpath_prev->last;
2104                             } else if (spl->next) { // there's a next subpath
2105                                 last = NULL; // to be set later to the last node of last subpath
2106                             } else {
2107                                 last = (Inkscape::NodePath::Node *) subpath->last;
2108                             }
2109                         }
2110                     }
2111                 }
2112             }
2113         }
2114         sp_nodepath_deselect(nodepath);
2115     }
2117     if (last) { // there's at least one more node before selected
2118         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2119     } else { // no more nodes, select the last one in last subpath
2120         GList *spl = g_list_last(nodepath->subpaths);
2121        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2122         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2123     }
2126 /**
2127  * \brief Select all nodes that are within the rectangle.
2128  */
2129 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2131     if (!incremental) {
2132         sp_nodepath_deselect(nodepath);
2133     }
2135     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2136        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2137         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2138            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2140             if (b.contains(node->pos)) {
2141                 sp_nodepath_node_select(node, TRUE, TRUE);
2142             }
2143         }
2144     }
2147 /**
2148 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2149 */
2150 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2152     if (!nodepath->selected) {
2153         return NULL;
2154     }
2156     GList *r = NULL;
2157     guint i = 0;
2158     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2159        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2160         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2161            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2162             i++;
2163             if (node->selected) {
2164                 r = g_list_append(r, GINT_TO_POINTER(i));
2165             }
2166         }
2167     }
2168     return r;
2171 /**
2172 \brief  Restores selection by selecting nodes whose positions are in the list
2173 */
2174 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2176     sp_nodepath_deselect(nodepath);
2178     guint i = 0;
2179     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2180        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2181         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2182            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2183             i++;
2184             if (g_list_find(r, GINT_TO_POINTER(i))) {
2185                 sp_nodepath_node_select(node, TRUE, TRUE);
2186             }
2187         }
2188     }
2192 /**
2193 \brief Adjusts control point according to node type and line code.
2194 */
2195 static void sp_node_adjust_knot(Inkscape::NodePath::Node *node, gint which_adjust)
2197     double len, otherlen, linelen;
2199     g_assert(node);
2201    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2202    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2204     /** \todo fixme: */
2205     if (me->other == NULL) return;
2206     if (other->other == NULL) return;
2208     /* I have line */
2210     NRPathcode mecode, ocode;
2211     if (which_adjust == 1) {
2212         mecode = (NRPathcode)me->other->code;
2213         ocode = (NRPathcode)node->code;
2214     } else {
2215         mecode = (NRPathcode)node->code;
2216         ocode = (NRPathcode)other->other->code;
2217     }
2219     if (mecode == NR_LINETO) return;
2221     /* I am curve */
2223     if (other->other == NULL) return;
2225     /* Other has line */
2227     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2229     NR::Point delta;
2230     if (ocode == NR_LINETO) {
2231         /* other is lineto, we are either smooth or symm */
2232        Inkscape::NodePath::Node *othernode = other->other;
2233         len = NR::L2(me->pos - node->pos);
2234         delta = node->pos - othernode->pos;
2235         linelen = NR::L2(delta);
2236         if (linelen < 1e-18) return;
2238         me->pos = node->pos + (len / linelen)*delta;
2240         sp_node_ensure_ctrls(node);
2241         return;
2242     }
2244     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2246         me->pos = 2 * node->pos - other->pos;
2248         sp_node_ensure_ctrls(node);
2249         return;
2250     }
2252     /* We are smooth */
2254     len = NR::L2(me->pos - node->pos);
2255     delta = other->pos - node->pos;
2256     otherlen = NR::L2(delta);
2257     if (otherlen < 1e-18) return;
2259     me->pos = node->pos - (len / otherlen) * delta;
2261     sp_node_ensure_ctrls(node);
2264 /**
2265  \brief Adjusts control point according to node type and line code
2266  */
2267 static void sp_node_adjust_knots(Inkscape::NodePath::Node *node)
2269     g_assert(node);
2271     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2273     /* we are either smooth or symm */
2275     if (node->p.other == NULL) return;
2277     if (node->n.other == NULL) return;
2279     if (node->code == NR_LINETO) {
2280         if (node->n.other->code == NR_LINETO) return;
2281         sp_node_adjust_knot(node, 1);
2282         sp_node_ensure_ctrls(node);
2283         return;
2284     }
2286     if (node->n.other->code == NR_LINETO) {
2287         if (node->code == NR_LINETO) return;
2288         sp_node_adjust_knot(node, -1);
2289         sp_node_ensure_ctrls(node);
2290         return;
2291     }
2293     /* both are curves */
2295     NR::Point const delta( node->n.pos - node->p.pos );
2297     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2298         node->p.pos = node->pos - delta / 2;
2299         node->n.pos = node->pos + delta / 2;
2300         sp_node_ensure_ctrls(node);
2301         return;
2302     }
2304     /* We are smooth */
2306     double plen = NR::L2(node->p.pos - node->pos);
2307     if (plen < 1e-18) return;
2308     double nlen = NR::L2(node->n.pos - node->pos);
2309     if (nlen < 1e-18) return;
2310     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2311     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2312     sp_node_ensure_ctrls(node);
2315 /**
2316  * Knot events handler callback.
2317  */
2318 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2320     gboolean ret = FALSE;
2321     switch (event->type) {
2322         case GDK_ENTER_NOTIFY:
2323             active_node = n;
2324             break;
2325         case GDK_LEAVE_NOTIFY:
2326             active_node = NULL;
2327             break;
2328         case GDK_KEY_PRESS:
2329             switch (get_group0_keyval (&event->key)) {
2330                 case GDK_space:
2331                     if (event->key.state & GDK_BUTTON1_MASK) {
2332                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2333                         stamp_repr(nodepath);
2334                         ret = TRUE;
2335                     }
2336                     break;
2337                 default:
2338                     break;
2339             }
2340             break;
2341         default:
2342             break;
2343     }
2345     return ret;
2348 /**
2349  * Handle keypress on node; directly called.
2350  */
2351 gboolean node_key(GdkEvent *event)
2353     Inkscape::NodePath::Path *np;
2355     // there is no way to verify nodes so set active_node to nil when deleting!!
2356     if (active_node == NULL) return FALSE;
2358     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2359         gint ret = FALSE;
2360         switch (get_group0_keyval (&event->key)) {
2361             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2362             case GDK_BackSpace:
2363                 np = active_node->subpath->nodepath;
2364                 sp_nodepath_node_destroy(active_node);
2365                 sp_nodepath_update_repr(np);
2366                 active_node = NULL;
2367                 ret = TRUE;
2368                 break;
2369             case GDK_c:
2370                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2371                 ret = TRUE;
2372                 break;
2373             case GDK_s:
2374                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2375                 ret = TRUE;
2376                 break;
2377             case GDK_y:
2378                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2379                 ret = TRUE;
2380                 break;
2381             case GDK_b:
2382                 sp_nodepath_node_break(active_node);
2383                 ret = TRUE;
2384                 break;
2385         }
2386         return ret;
2387     }
2388     return FALSE;
2391 /**
2392  * Mouseclick on node callback.
2393  */
2394 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2396    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2398     if (state & GDK_CONTROL_MASK) {
2399         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2401         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2402             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2403                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2404             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2405                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2406             } else {
2407                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2408             }
2409             sp_nodepath_update_repr(nodepath);
2410             sp_nodepath_update_statusbar(nodepath);
2412         } else { //ctrl+alt+click: delete node
2413             sp_nodepath_node_destroy(n);
2414             //clean up the nodepath (such as for trivial subpaths)
2415             sp_nodepath_cleanup(nodepath);
2417             // if the entire nodepath is removed, delete the selected object.
2418             if (nodepath->subpaths == NULL ||
2419                 sp_nodepath_get_node_count(nodepath) < 2) {
2420                 SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
2421                 sp_nodepath_destroy(nodepath);
2422                 sp_selection_delete();
2423                 sp_document_done (document);
2425             } else {
2426                 sp_nodepath_ensure_ctrls(nodepath);
2427                 sp_nodepath_update_repr(nodepath);
2428                 sp_nodepath_update_statusbar(nodepath);
2429             }
2430         }
2432     } else {
2433         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2434     }
2437 /**
2438  * Mouse grabbed node callback.
2439  */
2440 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2442    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2444     n->origin = knot->pos;
2446     if (!n->selected) {
2447         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2448     }
2451 /**
2452  * Mouse ungrabbed node callback.
2453  */
2454 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2456    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2458    n->dragging_out = NULL;
2460    sp_nodepath_update_repr(n->subpath->nodepath);
2463 /**
2464  * The point on a line, given by its angle, closest to the given point.
2465  * \param p  A point.
2466  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2467  * \param closest  Pointer to the point struct where the result is stored.
2468  * \todo FIXME: use dot product perhaps?
2469  */
2470 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2472     if (a == HUGE_VAL) { // vertical
2473         *closest = NR::Point(0, (*p)[NR::Y]);
2474     } else {
2475         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2476         (*closest)[NR::Y] = a * (*closest)[NR::X];
2477     }
2480 /**
2481  * Distance from the point to a line given by its angle.
2482  * \param p  A point.
2483  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2484  */
2485 static double point_line_distance(NR::Point *p, double a)
2487     NR::Point c;
2488     point_line_closest(p, a, &c);
2489     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]));
2492 /**
2493  * Callback for node "request" signal.
2494  * \todo fixme: This goes to "moved" event? (lauris)
2495  */
2496 static gboolean
2497 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2499     double yn, xn, yp, xp;
2500     double an, ap, na, pa;
2501     double d_an, d_ap, d_na, d_pa;
2502     gboolean collinear = FALSE;
2503     NR::Point c;
2504     NR::Point pr;
2506    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2508    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2509    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2511        NR::Point mouse = (*p);
2513        if (!n->dragging_out) {
2514            // This is the first drag-out event; find out which handle to drag out
2515            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2516            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2518            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2519                return FALSE;
2521            Inkscape::NodePath::NodeSide *opposite;
2522            if (appr_p > appr_n) { // closer to p
2523                n->dragging_out = &n->p;
2524                opposite = &n->n;
2525                n->code = NR_CURVETO;
2526            } else if (appr_p < appr_n) { // closer to n
2527                n->dragging_out = &n->n;
2528                opposite = &n->p;
2529                n->n.other->code = NR_CURVETO;
2530            } else { // p and n nodes are the same
2531                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2532                    n->dragging_out = &n->p;
2533                    opposite = &n->n;
2534                    n->code = NR_CURVETO;
2535                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2536                    n->dragging_out = &n->n;
2537                    opposite = &n->p;
2538                    n->n.other->code = NR_CURVETO;
2539                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2540                    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);
2541                    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);
2542                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2543                        n->dragging_out = &n->n;
2544                        opposite = &n->p;
2545                        n->n.other->code = NR_CURVETO;
2546                    } else { // closer to other's n handle
2547                        n->dragging_out = &n->p;
2548                        opposite = &n->n;
2549                        n->code = NR_CURVETO;
2550                    }
2551                }
2552            }
2554            // if there's another handle, make sure the one we drag out starts parallel to it
2555            if (opposite->pos != n->pos) {
2556                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2557            }
2559            // knots might not be created yet!
2560            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2561            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2562        }
2564        // pass this on to the handle-moved callback
2565        node_ctrl_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2566        sp_node_ensure_ctrls(n);
2567        return TRUE;
2568    }
2570     if (state & GDK_CONTROL_MASK) { // constrained motion
2572         // calculate relative distances of handles
2573         // n handle:
2574         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2575         xn = n->n.pos[NR::X] - n->pos[NR::X];
2576         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2577         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2578             if (n->n.other) { // if there is the next point
2579                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2580                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2581                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2582             }
2583         }
2584         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2585         if (yn < 0) { xn = -xn; yn = -yn; }
2587         // p handle:
2588         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2589         xp = n->p.pos[NR::X] - n->pos[NR::X];
2590         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2591         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2592             if (n->p.other) {
2593                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2594                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2595                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2596             }
2597         }
2598         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2599         if (yp < 0) { xp = -xp; yp = -yp; }
2601         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2602             // sliding on handles, only if at least one of the handles is non-vertical
2603             // (otherwise it's the same as ctrl+drag anyway)
2605             // calculate angles of the control handles
2606             if (xn == 0) {
2607                 if (yn == 0) { // no handle, consider it the continuation of the other one
2608                     an = 0;
2609                     collinear = TRUE;
2610                 }
2611                 else an = 0; // vertical; set the angle to horizontal
2612             } else an = yn/xn;
2614             if (xp == 0) {
2615                 if (yp == 0) { // no handle, consider it the continuation of the other one
2616                     ap = an;
2617                 }
2618                 else ap = 0; // vertical; set the angle to horizontal
2619             } else  ap = yp/xp;
2621             if (collinear) an = ap;
2623             // angles of the perpendiculars; HUGE_VAL means vertical
2624             if (an == 0) na = HUGE_VAL; else na = -1/an;
2625             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2627             //g_print("an %g    ap %g\n", an, ap);
2629             // mouse point relative to the node's original pos
2630             pr = (*p) - n->origin;
2632             // distances to the four lines (two handles and two perpendiculars)
2633             d_an = point_line_distance(&pr, an);
2634             d_na = point_line_distance(&pr, na);
2635             d_ap = point_line_distance(&pr, ap);
2636             d_pa = point_line_distance(&pr, pa);
2638             // find out which line is the closest, save its closest point in c
2639             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2640                 point_line_closest(&pr, an, &c);
2641             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2642                 point_line_closest(&pr, ap, &c);
2643             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2644                 point_line_closest(&pr, na, &c);
2645             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2646                 point_line_closest(&pr, pa, &c);
2647             }
2649             // move the node to the closest point
2650             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2651                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2652                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2654         } else {  // constraining to hor/vert
2656             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2657                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2658             } else { // snap to vert
2659                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2660             }
2661         }
2662     } else { // move freely
2663         sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2664                                         (*p)[NR::X] - n->pos[NR::X],
2665                                         (*p)[NR::Y] - n->pos[NR::Y],
2666                                         (state & GDK_SHIFT_MASK) == 0);
2667     }
2669     n->subpath->nodepath->desktop->scroll_to_point(p);
2671     return TRUE;
2674 /**
2675  * Node handle clicked callback.
2676  */
2677 static void node_ctrl_clicked(SPKnot *knot, guint state, gpointer data)
2679    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2681     if (state & GDK_CONTROL_MASK) { // "delete" handle
2682         if (n->p.knot == knot) {
2683             n->p.pos = n->pos;
2684         } else if (n->n.knot == knot) {
2685             n->n.pos = n->pos;
2686         }
2687         sp_node_ensure_ctrls(n);
2688         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2689         sp_nodepath_update_repr(nodepath);
2690         sp_nodepath_update_statusbar(nodepath);
2692     } else { // just select or add to selection, depending in Shift
2693         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2694     }
2697 /**
2698  * Node handle grabbed callback.
2699  */
2700 static void node_ctrl_grabbed(SPKnot *knot, guint state, gpointer data)
2702    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2704     if (!n->selected) {
2705         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2706     }
2708     // remember the origin of the control
2709     if (n->p.knot == knot) {
2710         n->p.origin = n->p.pos - n->pos;
2711     } else if (n->n.knot == knot) {
2712         n->n.origin = n->n.pos - n->pos;
2713     } else {
2714         g_assert_not_reached();
2715     }
2719 /**
2720  * Node handle ungrabbed callback.
2721  */
2722 static void node_ctrl_ungrabbed(SPKnot *knot, guint state, gpointer data)
2724    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2726     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
2727     if (n->p.knot == knot) {
2728         n->p.origin.a = 0;
2729         sp_knot_set_position(knot, &n->p.pos, state);
2730     } else if (n->n.knot == knot) {
2731         n->n.origin.a = 0;
2732         sp_knot_set_position(knot, &n->n.pos, state);
2733     } else {
2734         g_assert_not_reached();
2735     }
2737     sp_nodepath_update_repr(n->subpath->nodepath);
2740 /**
2741  * Node handle "request" signal callback.
2742  */
2743 static gboolean node_ctrl_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2745     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2747     Inkscape::NodePath::NodeSide *me, *opposite;
2748     gint which;
2749     if (n->p.knot == knot) {
2750         me = &n->p;
2751         opposite = &n->n;
2752         which = -1;
2753     } else if (n->n.knot == knot) {
2754         me = &n->n;
2755         opposite = &n->p;
2756         which = 1;
2757     } else {
2758         me = opposite = NULL;
2759         which = 0;
2760         g_assert_not_reached();
2761     }
2763     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
2765     SnapManager const m(n->subpath->nodepath->desktop->namedview);
2767     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
2768         /* We are smooth node adjacent with line */
2769         NR::Point const delta = *p - n->pos;
2770         NR::Coord const len = NR::L2(delta);
2771         Inkscape::NodePath::Node *othernode = opposite->other;
2772         NR::Point const ndelta = n->pos - othernode->pos;
2773         NR::Coord const linelen = NR::L2(ndelta);
2774         if (len > NR_EPSILON && linelen > NR_EPSILON) {
2775             NR::Coord const scal = dot(delta, ndelta) / linelen;
2776             (*p) = n->pos + (scal / linelen) * ndelta;
2777         }
2778         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, ndelta, NULL).getPoint();
2779     } else {
2780         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
2781     }
2783     sp_node_adjust_knot(n, -which);
2785     return FALSE;
2788 /**
2789  * Node handle moved callback.
2790  */
2791 static void node_ctrl_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2793    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2795    Inkscape::NodePath::NodeSide *me;
2796    Inkscape::NodePath::NodeSide *other;
2797     if (n->p.knot == knot) {
2798         me = &n->p;
2799         other = &n->n;
2800     } else if (n->n.knot == knot) {
2801         me = &n->n;
2802         other = &n->p;
2803     } else {
2804         me = NULL;
2805         other = NULL;
2806         g_assert_not_reached();
2807     }
2809     // calculate radial coordinates of the grabbed control, other control, and the mouse point
2810     Radial rme(me->pos - n->pos);
2811     Radial rother(other->pos - n->pos);
2812     Radial rnew(*p - n->pos);
2814     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
2815         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
2816         /* 0 interpreted as "no snapping". */
2818         // The closest PI/snaps angle, starting from zero.
2819         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
2820         if (me->origin.a == HUGE_VAL) {
2821             // ortho doesn't exist: original control was zero length.
2822             rnew.a = a_snapped;
2823         } else {
2824             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
2825              * its opposite and perpendiculars). */
2826             double const a_ortho = me->origin.a + floor((rnew.a - me->origin.a)/(M_PI/2) + 0.5) * (M_PI/2);
2828             // Snap to the closest.
2829             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
2830                        ? a_snapped
2831                        : a_ortho );
2832         }
2833     }
2835     if (state & GDK_MOD1_MASK) {
2836         // lock handle length
2837         rnew.r = me->origin.r;
2838     }
2840     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
2841         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
2842         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
2843         rother.a += rnew.a - rme.a;
2844         other->pos = NR::Point(rother) + n->pos;
2845         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
2846         sp_knot_set_position(other->knot, &other->pos, 0);
2847     }
2849     me->pos = NR::Point(rnew) + n->pos;
2850     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
2852     // this is what sp_knot_set_position does, but without emitting the signal:
2853     // we cannot emit a "moved" signal because we're now processing it
2854     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
2856     knot->desktop->set_coordinate_status(me->pos);
2858     update_object(n->subpath->nodepath);
2860     /* status text */
2861     SPDesktop *desktop = n->subpath->nodepath->desktop;
2862     if (!desktop) return;
2863     SPEventContext *ec = desktop->event_context;
2864     if (!ec) return;
2865     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
2866     if (!mc) return;
2868     double degrees = 180 / M_PI * rnew.a;
2869     if (degrees > 180) degrees -= 360;
2870     if (degrees < -180) degrees += 360;
2871     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
2872         degrees = angle_to_compass (degrees);
2874     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
2876     mc->setF(Inkscape::NORMAL_MESSAGE,
2877          _("<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);
2879     g_string_free(length, TRUE);
2882 /**
2883  * Node handle event callback.
2884  */
2885 static gboolean node_ctrl_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2887     gboolean ret = FALSE;
2888     switch (event->type) {
2889         case GDK_KEY_PRESS:
2890             switch (get_group0_keyval (&event->key)) {
2891                 case GDK_space:
2892                     if (event->key.state & GDK_BUTTON1_MASK) {
2893                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2894                         stamp_repr(nodepath);
2895                         ret = TRUE;
2896                     }
2897                     break;
2898                 default:
2899                     break;
2900             }
2901             break;
2902         default:
2903             break;
2904     }
2906     return ret;
2909 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
2910                                  Radial &rme, Radial &rother, gboolean const both)
2912     rme.a += angle;
2913     if ( both
2914          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2915          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2916     {
2917         rother.a += angle;
2918     }
2921 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
2922                                         Radial &rme, Radial &rother, gboolean const both)
2924     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
2926     gdouble r;
2927     if ( both
2928          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2929          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2930     {
2931         r = MAX(rme.r, rother.r);
2932     } else {
2933         r = rme.r;
2934     }
2936     gdouble const weird_angle = atan2(norm_angle, r);
2937 /* Bulia says norm_angle is just the visible distance that the
2938  * object's end must travel on the screen.  Left as 'angle' for want of
2939  * a better name.*/
2941     rme.a += weird_angle;
2942     if ( both
2943          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2944          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2945     {
2946         rother.a += weird_angle;
2947     }
2950 /**
2951  * Rotate one node.
2952  */
2953 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
2955     Inkscape::NodePath::NodeSide *me, *other;
2956     bool both = false;
2958     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
2959     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
2961     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
2962         me = &(n->p);
2963         other = &(n->n);
2964     } else if (!n->p.other) {
2965         me = &(n->n);
2966         other = &(n->p);
2967     } else {
2968         if (which > 0) { // right handle
2969             if (xn > xp) {
2970                 me = &(n->n);
2971                 other = &(n->p);
2972             } else {
2973                 me = &(n->p);
2974                 other = &(n->n);
2975             }
2976         } else if (which < 0){ // left handle
2977             if (xn <= xp) {
2978                 me = &(n->n);
2979                 other = &(n->p);
2980             } else {
2981                 me = &(n->p);
2982                 other = &(n->n);
2983             }
2984         } else { // both handles
2985             me = &(n->n);
2986             other = &(n->p);
2987             both = true;
2988         }
2989     }
2991     Radial rme(me->pos - n->pos);
2992     Radial rother(other->pos - n->pos);
2994     if (screen) {
2995         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
2996     } else {
2997         node_rotate_one_internal (*n, angle, rme, rother, both);
2998     }
3000     me->pos = n->pos + NR::Point(rme);
3002     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3003         other->pos =  n->pos + NR::Point(rother);
3004     }
3006     sp_node_ensure_ctrls(n);
3009 /**
3010  * Rotate selected nodes.
3011  */
3012 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3014     if (!nodepath || !nodepath->selected) return;
3016     if (g_list_length(nodepath->selected) == 1) {
3017        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3018         node_rotate_one (n, angle, which, screen);
3019     } else {
3020        // rotate as an object:
3022         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3023         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3024         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3025             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3026             box.expandTo (n->pos); // contain all selected nodes
3027         }
3029         gdouble rot;
3030         if (screen) {
3031             gdouble const zoom = nodepath->desktop->current_zoom();
3032             gdouble const zmove = angle / zoom;
3033             gdouble const r = NR::L2(box.max() - box.midpoint());
3034             rot = atan2(zmove, r);
3035         } else {
3036             rot = angle;
3037         }
3039         NR::Matrix t =
3040             NR::Matrix (NR::translate(-box.midpoint())) *
3041             NR::Matrix (NR::rotate(rot)) *
3042             NR::Matrix (NR::translate(box.midpoint()));
3044         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3045             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3046             n->pos *= t;
3047             n->n.pos *= t;
3048             n->p.pos *= t;
3049             sp_node_ensure_ctrls(n);
3050         }
3051     }
3053     update_object(nodepath);
3054     /// \todo fixme: use _keyed
3055     sp_nodepath_update_repr(nodepath);
3058 /**
3059  * Scale one node.
3060  */
3061 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3063     bool both = false;
3064     Inkscape::NodePath::NodeSide *me, *other;
3066     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3067     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3069     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3070         me = &(n->p);
3071         other = &(n->n);
3072         n->code = NR_CURVETO;
3073     } else if (!n->p.other) {
3074         me = &(n->n);
3075         other = &(n->p);
3076         if (n->n.other)
3077             n->n.other->code = NR_CURVETO;
3078     } else {
3079         if (which > 0) { // right handle
3080             if (xn > xp) {
3081                 me = &(n->n);
3082                 other = &(n->p);
3083                 if (n->n.other)
3084                     n->n.other->code = NR_CURVETO;
3085             } else {
3086                 me = &(n->p);
3087                 other = &(n->n);
3088                 n->code = NR_CURVETO;
3089             }
3090         } else if (which < 0){ // left handle
3091             if (xn <= xp) {
3092                 me = &(n->n);
3093                 other = &(n->p);
3094                 if (n->n.other)
3095                     n->n.other->code = NR_CURVETO;
3096             } else {
3097                 me = &(n->p);
3098                 other = &(n->n);
3099                 n->code = NR_CURVETO;
3100             }
3101         } else { // both handles
3102             me = &(n->n);
3103             other = &(n->p);
3104             both = true;
3105             n->code = NR_CURVETO;
3106             if (n->n.other)
3107                 n->n.other->code = NR_CURVETO;
3108         }
3109     }
3111     Radial rme(me->pos - n->pos);
3112     Radial rother(other->pos - n->pos);
3114     rme.r += grow;
3115     if (rme.r < 0) rme.r = 0;
3116     if (rme.a == HUGE_VAL) {
3117         if (me->other) { // if direction is unknown, initialize it towards the next node
3118             Radial rme_next(me->other->pos - n->pos);
3119             rme.a = rme_next.a;
3120         } else { // if there's no next, initialize to 0
3121             rme.a = 0;
3122         }
3123     }
3124     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3125         rother.r += grow;
3126         if (rother.r < 0) rother.r = 0;
3127         if (rother.a == HUGE_VAL) {
3128             rother.a = rme.a + M_PI;
3129         }
3130     }
3132     me->pos = n->pos + NR::Point(rme);
3134     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3135         other->pos = n->pos + NR::Point(rother);
3136     }
3138     sp_node_ensure_ctrls(n);
3141 /**
3142  * Scale selected nodes.
3143  */
3144 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3146     if (!nodepath || !nodepath->selected) return;
3148     if (g_list_length(nodepath->selected) == 1) {
3149         // scale handles of the single selected node
3150         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3151         node_scale_one (n, grow, which);
3152     } else {
3153         // scale nodes as an "object":
3155         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3156         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3157         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3158             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3159             box.expandTo (n->pos); // contain all selected nodes
3160         }
3162         double scale = (box.maxExtent() + grow)/box.maxExtent();
3164         NR::Matrix t =
3165             NR::Matrix (NR::translate(-box.midpoint())) *
3166             NR::Matrix (NR::scale(scale, scale)) *
3167             NR::Matrix (NR::translate(box.midpoint()));
3169         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3170             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3171             n->pos *= t;
3172             n->n.pos *= t;
3173             n->p.pos *= t;
3174             sp_node_ensure_ctrls(n);
3175         }
3176     }
3178     update_object(nodepath);
3179     /// \todo fixme: use _keyed
3180     sp_nodepath_update_repr(nodepath);
3183 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3185     if (!nodepath) return;
3186     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3189 /**
3190  * Flip selected nodes horizontally/vertically.
3191  */
3192 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3194     if (!nodepath || !nodepath->selected) return;
3196     if (g_list_length(nodepath->selected) == 1) {
3197         // flip handles of the single selected node
3198         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3199         double temp = n->p.pos[axis];
3200         n->p.pos[axis] = n->n.pos[axis];
3201         n->n.pos[axis] = temp;
3202         sp_node_ensure_ctrls(n);
3203     } else {
3204         // scale nodes as an "object":
3206         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3207         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3208         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3209             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3210             box.expandTo (n->pos); // contain all selected nodes
3211         }
3213         NR::Matrix t =
3214             NR::Matrix (NR::translate(-box.midpoint())) *
3215             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3216             NR::Matrix (NR::translate(box.midpoint()));
3218         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3219             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3220             n->pos *= t;
3221             n->n.pos *= t;
3222             n->p.pos *= t;
3223             sp_node_ensure_ctrls(n);
3224         }
3225     }
3227     update_object(nodepath);
3228     /// \todo fixme: use _keyed
3229     sp_nodepath_update_repr(nodepath);
3232 //-----------------------------------------------
3233 /**
3234  * Return new subpath under given nodepath.
3235  */
3236 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3238     g_assert(nodepath);
3239     g_assert(nodepath->desktop);
3241    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3243     s->nodepath = nodepath;
3244     s->closed = FALSE;
3245     s->nodes = NULL;
3246     s->first = NULL;
3247     s->last = NULL;
3249     // do not use prepend here because:
3250     // if you have a path like "subpath_1 subpath_2 ... subpath_k" in the svg, you end up with
3251     // subpath_k -> ... ->subpath_1 in the nodepath structure. thus the i-th node of the svg is not
3252     // the i-th node in the nodepath (only if there are multiple subpaths)
3253     // note that the problem only arise when called from subpath_from_bpath(), since for all the other
3254     // cases, the repr is updated after the call to sp_nodepath_subpath_new()
3255     nodepath->subpaths = g_list_append /*g_list_prepend*/ (nodepath->subpaths, s);
3257     return s;
3260 /**
3261  * Destroy nodes in subpath, then subpath itself.
3262  */
3263 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3265     g_assert(subpath);
3266     g_assert(subpath->nodepath);
3267     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3269     while (subpath->nodes) {
3270         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3271     }
3273     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3275     g_free(subpath);
3278 /**
3279  * Link head to tail in subpath.
3280  */
3281 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3283     g_assert(!sp->closed);
3284     g_assert(sp->last != sp->first);
3285     g_assert(sp->first->code == NR_MOVETO);
3287     sp->closed = TRUE;
3289     //Link the head to the tail
3290     sp->first->p.other = sp->last;
3291     sp->last->n.other  = sp->first;
3292     sp->last->n.pos    = sp->first->n.pos;
3293     sp->first          = sp->last;
3295     //Remove the extra end node
3296     sp_nodepath_node_destroy(sp->last->n.other);
3299 /**
3300  * Open closed (loopy) subpath at node.
3301  */
3302 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3304     g_assert(sp->closed);
3305     g_assert(n->subpath == sp);
3306     g_assert(sp->first == sp->last);
3308     /* We create new startpoint, current node will become last one */
3310    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3311                                                 &n->pos, &n->pos, &n->n.pos);
3314     sp->closed        = FALSE;
3316     //Unlink to make a head and tail
3317     sp->first         = new_path;
3318     sp->last          = n;
3319     n->n.other        = NULL;
3320     new_path->p.other = NULL;
3323 /**
3324  * Returns area in triangle given by points; may be negative.
3325  */
3326 inline double
3327 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3329     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]);
3332 /**
3333  * Return new node in subpath with given properties.
3334  * \param pos Position of node.
3335  * \param ppos Handle position in previous direction
3336  * \param npos Handle position in previous direction
3337  */
3338 Inkscape::NodePath::Node *
3339 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)
3341     g_assert(sp);
3342     g_assert(sp->nodepath);
3343     g_assert(sp->nodepath->desktop);
3345     if (nodechunk == NULL)
3346         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3348     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3350     n->subpath  = sp;
3352     if (type != Inkscape::NodePath::NODE_NONE) {
3353         // use the type from sodipodi:nodetypes
3354         n->type = type;
3355     } else {
3356         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3357             // points are (almost) collinear
3358             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3359                 // endnode, or a node with a retracted handle
3360                 n->type = Inkscape::NodePath::NODE_CUSP;
3361             } else {
3362                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3363             }
3364         } else {
3365             n->type = Inkscape::NodePath::NODE_CUSP;
3366         }
3367     }
3369     n->code     = code;
3370     n->selected = FALSE;
3371     n->pos      = *pos;
3372     n->p.pos    = *ppos;
3373     n->n.pos    = *npos;
3375     n->dragging_out = NULL;
3377     Inkscape::NodePath::Node *prev;
3378     if (next) {
3379         //g_assert(g_list_find(sp->nodes, next));
3380         prev = next->p.other;
3381     } else {
3382         prev = sp->last;
3383     }
3385     if (prev)
3386         prev->n.other = n;
3387     else
3388         sp->first = n;
3390     if (next)
3391         next->p.other = n;
3392     else
3393         sp->last = n;
3395     n->p.other = prev;
3396     n->n.other = next;
3398     n->knot = sp_knot_new(sp->nodepath->desktop, _("<b>Node</b>: drag to edit the path; with <b>Ctrl</b> to snap to horizontal/vertical; with <b>Ctrl+Alt</b> to snap to handles' directions"));
3399     sp_knot_set_position(n->knot, pos, 0);
3401     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3402     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3403     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3404     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3405     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3406     sp_knot_update_ctrl(n->knot);
3408     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3409     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3410     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3411     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3412     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3413     sp_knot_show(n->knot);
3415     // We only create side knots and lines on demand
3416     n->p.knot = NULL;
3417     n->p.line = NULL;
3418     n->n.knot = NULL;
3419     n->n.line = NULL;
3421     sp->nodes = g_list_prepend(sp->nodes, n);
3423     return n;
3426 /**
3427  * Destroy node and its knots, link neighbors in subpath.
3428  */
3429 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3431     g_assert(node);
3432     g_assert(node->subpath);
3433     g_assert(SP_IS_KNOT(node->knot));
3434 //    g_assert(g_list_find(node->subpath->nodes, node));
3436    Inkscape::NodePath::SubPath *sp = node->subpath;
3438     if (node->selected) { // first, deselect
3439         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3440         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3441     }
3443     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3445     g_object_unref(G_OBJECT(node->knot));
3446     if (node->p.knot)
3447         g_object_unref(G_OBJECT(node->p.knot));
3448     if (node->n.knot)
3449         g_object_unref(G_OBJECT(node->n.knot));
3451     if (node->p.line)
3452         gtk_object_destroy(GTK_OBJECT(node->p.line));
3453     if (node->n.line)
3454         gtk_object_destroy(GTK_OBJECT(node->n.line));
3456     if (sp->nodes) { // there are others nodes on the subpath
3457         if (sp->closed) {
3458             if (sp->first == node) {
3459                 g_assert(sp->last == node);
3460                 sp->first = node->n.other;
3461                 sp->last = sp->first;
3462             }
3463             node->p.other->n.other = node->n.other;
3464             node->n.other->p.other = node->p.other;
3465         } else {
3466             if (sp->first == node) {
3467                 sp->first = node->n.other;
3468                 sp->first->code = NR_MOVETO;
3469             }
3470             if (sp->last == node) sp->last = node->p.other;
3471             if (node->p.other) node->p.other->n.other = node->n.other;
3472             if (node->n.other) node->n.other->p.other = node->p.other;
3473         }
3474     } else { // this was the last node on subpath
3475         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3476     }
3478     g_mem_chunk_free(nodechunk, node);
3481 /**
3482  * Returns one of the node's two knots (node sides).
3483  * \param which Indicates which side.
3484  * \return Pointer to previous node side if which==-1, next if which==1.
3485  */
3486 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3488     g_assert(node);
3490     switch (which) {
3491         case -1:
3492             return &node->p;
3493         case 1:
3494             return &node->n;
3495         default:
3496             break;
3497     }
3499     g_assert_not_reached();
3501     return NULL;
3504 /**
3505  * Return knot on other side of node.
3506  */
3507 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3509     g_assert(node);
3511     if (me == &node->p) return &node->n;
3512     if (me == &node->n) return &node->p;
3514     g_assert_not_reached();
3516     return NULL;
3519 /**
3520  * Return NRPathcode on this knot's side of the node.
3521  */
3522 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3524     g_assert(node);
3526     if (me == &node->p) {
3527         if (node->p.other) return (NRPathcode)node->code;
3528         return NR_MOVETO;
3529     }
3531     if (me == &node->n) {
3532         if (node->n.other) return (NRPathcode)node->n.other->code;
3533         return NR_MOVETO;
3534     }
3536     g_assert_not_reached();
3538     return NR_END;
3541 /**
3542  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3543  */
3544 Inkscape::NodePath::Node *
3545 sp_nodepath_get_node_by_index(int index)
3547     Inkscape::NodePath::Node *e = NULL;
3549     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3550     if (!nodepath) {
3551         return e;
3552     }
3554     //find segment
3555     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3557         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3558         int n = g_list_length(sp->nodes);
3559         if (sp->closed) {
3560             n++;
3561         }
3563         //if the piece belongs to this subpath grab it
3564         //otherwise move onto the next subpath
3565         if (index < n) {
3566             e = sp->first;
3567             for (int i = 0; i < index; ++i) {
3568                 e = e->n.other;
3569             }
3570             break;
3571         } else {
3572             if (sp->closed) {
3573                 index -= (n+1);
3574             } else {
3575                 index -= n;
3576             }
3577         }
3578     }
3580     return e;
3583 /**
3584  * Returns plain text meaning of node type.
3585  */
3586 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3588     unsigned retracted = 0;
3589     bool endnode = false;
3591     for (int which = -1; which <= 1; which += 2) {
3592         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3593         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3594             retracted ++;
3595         if (!side->other)
3596             endnode = true;
3597     }
3599     if (retracted == 0) {
3600         if (endnode) {
3601                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3602                 return _("end node");
3603         } else {
3604             switch (node->type) {
3605                 case Inkscape::NodePath::NODE_CUSP:
3606                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3607                     return _("cusp");
3608                 case Inkscape::NodePath::NODE_SMOOTH:
3609                     // TRANSLATORS: "smooth" is an adjective here
3610                     return _("smooth");
3611                 case Inkscape::NodePath::NODE_SYMM:
3612                     return _("symmetric");
3613             }
3614         }
3615     } else if (retracted == 1) {
3616         if (endnode) {
3617             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3618             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3619         } else {
3620             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3621         }
3622     } else {
3623         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3624     }
3626     return NULL;
3629 /**
3630  * Handles content of statusbar as long as node tool is active.
3631  */
3632 void
3633 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3635     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>arrow</b> keys to move nodes");
3636     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3638     gint total = 0;
3639     gint selected = 0;
3640     SPDesktop *desktop = NULL;
3642     if (nodepath) {
3643         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3644             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3645             total += g_list_length(subpath->nodes);
3646         }
3647         selected = g_list_length(nodepath->selected);
3648         desktop = nodepath->desktop;
3649     } else {
3650         desktop = SP_ACTIVE_DESKTOP;
3651     }
3653     SPEventContext *ec = desktop->event_context;
3654     if (!ec) return;
3655     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3656     if (!mc) return;
3658     if (selected == 0) {
3659         Inkscape::Selection *sel = desktop->selection;
3660         if (!sel || sel->isEmpty()) {
3661             mc->setF(Inkscape::NORMAL_MESSAGE,
3662                      _("Select a single object to edit its nodes or handles."));
3663         } else {
3664             if (nodepath) {
3665             mc->setF(Inkscape::NORMAL_MESSAGE,
3666                      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.",
3667                               "<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.",
3668                               total),
3669                      total);
3670             } else {
3671                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3672                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3673                 } else {
3674                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3675                 }
3676             }
3677         }
3678     } else if (nodepath && selected == 1) {
3679         mc->setF(Inkscape::NORMAL_MESSAGE,
3680                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3681                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3682                           total),
3683                  selected, total, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3684     } else {
3685         mc->setF(Inkscape::NORMAL_MESSAGE,
3686                  ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
3687                           "<b>%i</b> of <b>%i</b> nodes selected. %s.",
3688                           total),
3689                  selected, total, when_selected);
3690     }
3694 /*
3695   Local Variables:
3696   mode:c++
3697   c-file-style:"stroustrup"
3698   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3699   indent-tabs-mode:nil
3700   fill-column:99
3701   End:
3702 */
3703 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :