Code

5cb4ed7f8ca543e621fa9989bfb9ca3c41c0964b
[inkscape.git] / src / nodepath.cpp
1 #define __SP_NODEPATH_C__
3 /** \file
4  * Path handler in node edit mode
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * This code is in public domain
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/curve.h"
19 #include "display/sp-ctrlline.h"
20 #include "display/sodipodi-ctrl.h"
21 #include <glibmm/i18n.h>
22 #include "libnr/n-art-bpath.h"
23 #include "helper/units.h"
24 #include "knot.h"
25 #include "inkscape.h"
26 #include "document.h"
27 #include "sp-namedview.h"
28 #include "desktop.h"
29 #include "desktop-handles.h"
30 #include "snap.h"
31 #include "message-stack.h"
32 #include "message-context.h"
33 #include "node-context.h"
34 #include "selection-chemistry.h"
35 #include "selection.h"
36 #include "xml/repr.h"
37 #include "prefs-utils.h"
38 #include "sp-metrics.h"
39 #include "sp-path.h"
40 #include <libnr/nr-matrix-ops.h>
41 #include "splivarot.h"
42 #include "svg/svg.h"
44 class NR::Matrix;
46 /// \todo
47 /// evil evil evil. FIXME: conflict of two different Path classes!
48 /// There is a conflict in the namespace between two classes named Path.
49 /// #include "sp-flowtext.h"
50 /// #include "sp-flowregion.h"
52 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
53 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
54 GType sp_flowregion_get_type (void);
55 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
56 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
57 GType sp_flowtext_get_type (void);
58 // end evil workaround
60 #include "helper/stlport.h"
63 /// \todo fixme: Implement these via preferences */
65 #define NODE_FILL          0xbfbfbf00
66 #define NODE_STROKE        0x000000ff
67 #define NODE_FILL_HI       0xff000000
68 #define NODE_STROKE_HI     0x000000ff
69 #define NODE_FILL_SEL      0x0000ffff
70 #define NODE_STROKE_SEL    0x000000ff
71 #define NODE_FILL_SEL_HI   0xff000000
72 #define NODE_STROKE_SEL_HI 0x000000ff
73 #define KNOT_FILL          0xffffffff
74 #define KNOT_STROKE        0x000000ff
75 #define KNOT_FILL_HI       0xff000000
76 #define KNOT_STROKE_HI     0x000000ff
78 static GMemChunk *nodechunk = NULL;
80 /* Creation from object */
82 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
83 static gchar *parse_nodetypes(gchar const *types, gint length);
85 /* Object updating */
87 static void stamp_repr(Inkscape::NodePath::Path *np);
88 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
89 static gchar *create_typestr(Inkscape::NodePath::Path *np);
91 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
93 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
95 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
97 /* Adjust handle placement, if the node or the other handle is moved */
98 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
99 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
101 /* Node event callbacks */
102 static void node_clicked(SPKnot *knot, guint state, gpointer data);
103 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
104 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
105 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
107 /* Handle event callbacks */
108 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
109 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
110 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
111 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
112 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
113 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
115 /* Constructors and destructors */
117 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
118 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
119 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
120 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
121 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
122                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
123 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
125 /* Helpers */
127 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
128 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
129 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
131 // active_node indicates mouseover node
132 static Inkscape::NodePath::Node *active_node = NULL;
134 /**
135  * \brief Creates new nodepath from item
136  */
137 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item)
139     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
141     /** \todo
142      * FIXME: remove this. We don't want to edit paths inside flowtext.
143      * Instead we will build our flowtext with cloned paths, so that the
144      * real paths are outside the flowtext and thus editable as usual.
145      */
146     if (SP_IS_FLOWTEXT(item)) {
147         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
148             if SP_IS_FLOWREGION(child) {
149                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
150                 if (grandchild && SP_IS_PATH(grandchild)) {
151                     item = SP_ITEM(grandchild);
152                     break;
153                 }
154             }
155         }
156     }
158     if (!SP_IS_PATH(item))
159         return NULL;
160     SPPath *path = SP_PATH(item);
161     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
162     if (curve == NULL)
163         return NULL;
165     NArtBpath *bpath = sp_curve_first_bpath(curve);
166     gint length = curve->end;
167     if (length == 0)
168         return NULL; // prevent crash for one-node paths
170     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
171     gchar *typestr = parse_nodetypes(nodetypes, length);
173     //Create new nodepath
174     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
175     if (!np)
176         return NULL;
178     // Set defaults
179     np->desktop     = desktop;
180     np->path        = path;
181     np->subpaths    = NULL;
182     np->selected    = NULL;
183     np->nodeContext = NULL; //Let the context that makes this set it
184     np->livarot_path = NULL;
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     // create the subpath(s) from the bpath
196     NArtBpath *b = bpath;
197     while (b->code != NR_END) {
198         b = subpath_from_bpath(np, b, typestr + (b - bpath));
199     }
201     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
202     np->subpaths = g_list_reverse(np->subpaths);
204     g_free(typestr);
205     sp_curve_unref(curve);
207     // create the livarot representation from the same item
208     np->livarot_path = Path_for_item(item, true, true);
209     if (np->livarot_path)
210         np->livarot_path->ConvertWithBackData(0.01);
212     return np;
215 /**
216  * Destroys nodepath's subpaths, then itself, also tell context about it.
217  */
218 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
220     if (!np)  //soft fail, like delete
221         return;
223     while (np->subpaths) {
224         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
225     }
227     //Inform the context that made me, if any, that I am gone.
228     if (np->nodeContext)
229         np->nodeContext->nodepath = NULL;
231     g_assert(!np->selected);
233     if (np->livarot_path) {
234         delete np->livarot_path;
235         np->livarot_path = NULL;
236     }
238     np->desktop = NULL;
240     g_free(np);
244 /**
245  *  Return the node count of a given NodeSubPath.
246  */
247 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
249     if (!subpath)
250         return 0;
251     gint nodeCount = g_list_length(subpath->nodes);
252     return nodeCount;
255 /**
256  *  Return the node count of a given NodePath.
257  */
258 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
260     if (!np)
261         return 0;
262     gint nodeCount = 0;
263     for (GList *item = np->subpaths ; item ; item=item->next) {
264        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
265         nodeCount += g_list_length(subpath->nodes);
266     }
267     return nodeCount;
271 /**
272  * Clean up a nodepath after editing.
273  *
274  * Currently we are deleting trivial subpaths.
275  */
276 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
278     GList *badSubPaths = NULL;
280     //Check all subpaths to be >=2 nodes
281     for (GList *l = nodepath->subpaths; l ; l=l->next) {
282        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
283         if (sp_nodepath_subpath_get_node_count(sp)<2)
284             badSubPaths = g_list_append(badSubPaths, sp);
285     }
287     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
288     //also removes the subpath from nodepath->subpaths
289     for (GList *l = badSubPaths; l ; l=l->next) {
290        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
291         sp_nodepath_subpath_destroy(sp);
292     }
294     g_list_free(badSubPaths);
299 /**
300  * \brief Returns true if the argument nodepath and the d attribute in
301  * its repr do not match.
302  *
303  * This may happen if repr was changed in, e.g., XML editor or by undo.
304  *
305  * \todo
306  * UGLY HACK, think how we can eliminate it. IDEA: try instead a local_change flag in node context?
307  */
308 gboolean nodepath_repr_d_changed(Inkscape::NodePath::Path *np, char const *newd)
310     g_assert(np);
312     SPCurve *curve = create_curve(np);
314     gchar *svgpath = sp_svg_write_path(curve->bpath);
316     char const *attr_d = ( newd
317                            ? newd
318                            : SP_OBJECT(np->path)->repr->attribute("d") );
320     gboolean ret;
321     if (attr_d && svgpath)
322         ret = strcmp(attr_d, svgpath);
323     else
324         ret = TRUE;
326     g_free(svgpath);
327     sp_curve_unref(curve);
329     return ret;
332 /**
333  * \brief Returns true if the argument nodepath and the sodipodi:nodetypes
334  * attribute in its repr do not match.
335  *
336  * This may happen if repr was changed in, e.g., the XML editor or by undo.
337  * IDEA: try instead a local_change flag in node context?
338  */
339 gboolean nodepath_repr_typestr_changed(Inkscape::NodePath::Path *np, char const *newtypestr)
341     g_assert(np);
342     gchar *typestr = create_typestr(np);
343     char const *attr_typestr = ( newtypestr
344                                  ? newtypestr
345                                  : SP_OBJECT(np->path)->repr->attribute("sodipodi:nodetypes") );
346     gboolean const ret = (attr_typestr && strcmp(attr_typestr, typestr));
348     g_free(typestr);
350     return ret;
353 /**
354  * Create new nodepath from b, make it subpath of np.
355  * \param t The node type.
356  * \todo Fixme: t should be a proper type, rather than gchar
357  */
358 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
360     NR::Point ppos, pos, npos;
362     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
364     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
365     bool const closed = (b->code == NR_MOVETO);
367     pos = NR::Point(b->x3, b->y3) * np->i2d;
368     if (b[1].code == NR_CURVETO) {
369         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
370     } else {
371         npos = pos;
372     }
373     Inkscape::NodePath::Node *n;
374     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
375     g_assert(sp->first == n);
376     g_assert(sp->last  == n);
378     b++;
379     t++;
380     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
381         pos = NR::Point(b->x3, b->y3) * np->i2d;
382         if (b->code == NR_CURVETO) {
383             ppos = NR::Point(b->x2, b->y2) * np->i2d;
384         } else {
385             ppos = pos;
386         }
387         if (b[1].code == NR_CURVETO) {
388             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
389         } else {
390             npos = pos;
391         }
392         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
393         b++;
394         t++;
395     }
397     if (closed) sp_nodepath_subpath_close(sp);
399     return b;
402 /**
403  * Convert from sodipodi:nodetypes to new style type string.
404  */
405 static gchar *parse_nodetypes(gchar const *types, gint length)
407     g_assert(length > 0);
409     gchar *typestr = g_new(gchar, length + 1);
411     gint pos = 0;
413     if (types) {
414         for (gint i = 0; types[i] && ( i < length ); i++) {
415             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
416             if (types[i] != '\0') {
417                 switch (types[i]) {
418                     case 's':
419                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
420                         break;
421                     case 'z':
422                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
423                         break;
424                     case 'c':
425                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
426                         break;
427                     default:
428                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
429                         break;
430                 }
431             }
432         }
433     }
435     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
437     return typestr;
440 /**
441  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
442  * updated but repr is not (for speed). Used during curve and node drag.
443  */
444 static void update_object(Inkscape::NodePath::Path *np)
446     g_assert(np);
448     SPCurve *curve = create_curve(np);
450     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
452     sp_curve_unref(curve);
455 /**
456  * Update XML path node with data from path object.
457  */
458 static void update_repr_internal(Inkscape::NodePath::Path *np)
460     g_assert(np);
462     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
464     SPCurve *curve = create_curve(np);
465     gchar *typestr = create_typestr(np);
466     gchar *svgpath = sp_svg_write_path(curve->bpath);
468     repr->setAttribute("d", svgpath);
469     repr->setAttribute("sodipodi:nodetypes", typestr);
471     g_free(svgpath);
472     g_free(typestr);
473     sp_curve_unref(curve);
476 /**
477  * Update XML path node with data from path object, commit changes forever.
478  */
479 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np)
481     update_repr_internal(np);
482     sp_document_done(SP_DT_DOCUMENT(np->desktop));
484     if (np->livarot_path) {
485         delete np->livarot_path;
486         np->livarot_path = NULL;
487     }
489     if (np->path && SP_IS_ITEM(np->path)) {
490         np->livarot_path = Path_for_item (np->path, true, true);
491         if (np->livarot_path)
492             np->livarot_path->ConvertWithBackData(0.01);
493     }
496 /**
497  * Update XML path node with data from path object, commit changes with undo.
498  */
499 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key)
501     update_repr_internal(np);
502     sp_document_maybe_done(SP_DT_DOCUMENT(np->desktop), key);
504     if (np->livarot_path) {
505         delete np->livarot_path;
506         np->livarot_path = NULL;
507     }
509     if (np->path && SP_IS_ITEM(np->path)) {
510         np->livarot_path = Path_for_item (np->path, true, true);
511         if (np->livarot_path)
512             np->livarot_path->ConvertWithBackData(0.01);
513     }
516 /**
517  * Make duplicate of path, replace corresponding XML node in tree, commit.
518  */
519 static void stamp_repr(Inkscape::NodePath::Path *np)
521     g_assert(np);
523     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
524     Inkscape::XML::Node *new_repr = old_repr->duplicate();
526     // remember the position of the item
527     gint pos = old_repr->position();
528     // remember parent
529     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
531     SPCurve *curve = create_curve(np);
532     gchar *typestr = create_typestr(np);
534     gchar *svgpath = sp_svg_write_path(curve->bpath);
536     new_repr->setAttribute("d", svgpath);
537     new_repr->setAttribute("sodipodi:nodetypes", typestr);
539     // add the new repr to the parent
540     parent->appendChild(new_repr);
541     // move to the saved position
542     new_repr->setPosition(pos > 0 ? pos : 0);
544     sp_document_done(SP_DT_DOCUMENT(np->desktop));
546     Inkscape::GC::release(new_repr);
547     g_free(svgpath);
548     g_free(typestr);
549     sp_curve_unref(curve);
552 /**
553  * Create curve from path.
554  */
555 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
557     SPCurve *curve = sp_curve_new();
559     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
560        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
561         sp_curve_moveto(curve,
562                         sp->first->pos * np->d2i);
563        Inkscape::NodePath::Node *n = sp->first->n.other;
564         while (n) {
565             NR::Point const end_pt = n->pos * np->d2i;
566             switch (n->code) {
567                 case NR_LINETO:
568                     sp_curve_lineto(curve, end_pt);
569                     break;
570                 case NR_CURVETO:
571                     sp_curve_curveto(curve,
572                                      n->p.other->n.pos * np->d2i,
573                                      n->p.pos * np->d2i,
574                                      end_pt);
575                     break;
576                 default:
577                     g_assert_not_reached();
578                     break;
579             }
580             if (n != sp->last) {
581                 n = n->n.other;
582             } else {
583                 n = NULL;
584             }
585         }
586         if (sp->closed) {
587             sp_curve_closepath(curve);
588         }
589     }
591     return curve;
594 /**
595  * Convert path type string to sodipodi:nodetypes style.
596  */
597 static gchar *create_typestr(Inkscape::NodePath::Path *np)
599     gchar *typestr = g_new(gchar, 32);
600     gint len = 32;
601     gint pos = 0;
603     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
604        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
606         if (pos >= len) {
607             typestr = g_renew(gchar, typestr, len + 32);
608             len += 32;
609         }
611         typestr[pos++] = 'c';
613        Inkscape::NodePath::Node *n;
614         n = sp->first->n.other;
615         while (n) {
616             gchar code;
618             switch (n->type) {
619                 case Inkscape::NodePath::NODE_CUSP:
620                     code = 'c';
621                     break;
622                 case Inkscape::NodePath::NODE_SMOOTH:
623                     code = 's';
624                     break;
625                 case Inkscape::NodePath::NODE_SYMM:
626                     code = 'z';
627                     break;
628                 default:
629                     g_assert_not_reached();
630                     code = '\0';
631                     break;
632             }
634             if (pos >= len) {
635                 typestr = g_renew(gchar, typestr, len + 32);
636                 len += 32;
637             }
639             typestr[pos++] = code;
641             if (n != sp->last) {
642                 n = n->n.other;
643             } else {
644                 n = NULL;
645             }
646         }
647     }
649     if (pos >= len) {
650         typestr = g_renew(gchar, typestr, len + 1);
651         len += 1;
652     }
654     typestr[pos++] = '\0';
656     return typestr;
659 /**
660  * Returns current path in context.
661  */
662 static Inkscape::NodePath::Path *sp_nodepath_current()
664     if (!SP_ACTIVE_DESKTOP) {
665         return NULL;
666     }
668     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
670     if (!SP_IS_NODE_CONTEXT(event_context)) {
671         return NULL;
672     }
674     return SP_NODE_CONTEXT(event_context)->nodepath;
679 /**
680  \brief Fills node and handle positions for three nodes, splitting line
681   marked by end at distance t.
682  */
683 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
685     g_assert(new_path != NULL);
686     g_assert(end      != NULL);
688     g_assert(end->p.other == new_path);
689    Inkscape::NodePath::Node *start = new_path->p.other;
690     g_assert(start);
692     if (end->code == NR_LINETO) {
693         new_path->type =Inkscape::NodePath::NODE_CUSP;
694         new_path->code = NR_LINETO;
695         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
696     } else {
697         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
698         new_path->code = NR_CURVETO;
699         gdouble s      = 1 - t;
700         for (int dim = 0; dim < 2; dim++) {
701             NR::Coord const f000 = start->pos[dim];
702             NR::Coord const f001 = start->n.pos[dim];
703             NR::Coord const f011 = end->p.pos[dim];
704             NR::Coord const f111 = end->pos[dim];
705             NR::Coord const f00t = s * f000 + t * f001;
706             NR::Coord const f01t = s * f001 + t * f011;
707             NR::Coord const f11t = s * f011 + t * f111;
708             NR::Coord const f0tt = s * f00t + t * f01t;
709             NR::Coord const f1tt = s * f01t + t * f11t;
710             NR::Coord const fttt = s * f0tt + t * f1tt;
711             start->n.pos[dim]    = f00t;
712             new_path->p.pos[dim] = f0tt;
713             new_path->pos[dim]   = fttt;
714             new_path->n.pos[dim] = f1tt;
715             end->p.pos[dim]      = f11t;
716         }
717     }
720 /**
721  * Adds new node on direct line between two nodes, activates handles of all
722  * three nodes.
723  */
724 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
726     g_assert(end);
727     g_assert(end->subpath);
728     g_assert(g_list_find(end->subpath->nodes, end));
730    Inkscape::NodePath::Node *start = end->p.other;
731     g_assert( start->n.other == end );
732    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
733                                                end,
734                                               Inkscape::NodePath::NODE_SMOOTH,
735                                                (NRPathcode)end->code,
736                                                &start->pos, &start->pos, &start->n.pos);
737     sp_nodepath_line_midpoint(newnode, end, t);
739     sp_node_update_handles(start);
740     sp_node_update_handles(newnode);
741     sp_node_update_handles(end);
743     return newnode;
746 /**
747 \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
748 */
749 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
751     g_assert(node);
752     g_assert(node->subpath);
753     g_assert(g_list_find(node->subpath->nodes, node));
755    Inkscape::NodePath::SubPath *sp = node->subpath;
756     Inkscape::NodePath::Path *np    = sp->nodepath;
758     if (sp->closed) {
759         sp_nodepath_subpath_open(sp, node);
760         return sp->first;
761     } else {
762         // no break for end nodes
763         if (node == sp->first) return NULL;
764         if (node == sp->last ) return NULL;
766         // create a new subpath
767        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
769         // duplicate the break node as start of the new subpath
770        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
772         while (node->n.other) { // copy the remaining nodes into the new subpath
773            Inkscape::NodePath::Node *n  = node->n.other;
774            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);
775             if (n->selected) {
776                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
777             }
778             sp_nodepath_node_destroy(n); // remove the point on the original subpath
779         }
781         return newnode;
782     }
785 /**
786  * Duplicate node and connect to neighbours.
787  */
788 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
790     g_assert(node);
791     g_assert(node->subpath);
792     g_assert(g_list_find(node->subpath->nodes, node));
794    Inkscape::NodePath::SubPath *sp = node->subpath;
796     NRPathcode code = (NRPathcode) node->code;
797     if (code == NR_MOVETO) { // if node is the endnode,
798         node->code = NR_LINETO; // new one is inserted before it, so change that to line
799     }
801     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
803     if (!node->n.other || !node->p.other) // if node is an endnode, select it
804         return node;
805     else
806         return newnode; // otherwise select the newly created node
809 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
811     node->p.pos = (node->pos + (node->pos - node->n.pos));
814 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
816     node->n.pos = (node->pos + (node->pos - node->p.pos));
819 /**
820  * Change line type at node, with side effects on neighbours.
821  */
822 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
824     g_assert(end);
825     g_assert(end->subpath);
826     g_assert(end->p.other);
828     if (end->code == static_cast< guint > ( code ) )
829         return;
831    Inkscape::NodePath::Node *start = end->p.other;
833     end->code = code;
835     if (code == NR_LINETO) {
836         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
837         if (end->n.other) {
838             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
839         }
840         sp_node_adjust_handle(start, -1);
841         sp_node_adjust_handle(end, 1);
842     } else {
843         NR::Point delta = end->pos - start->pos;
844         start->n.pos = start->pos + delta / 3;
845         end->p.pos = end->pos - delta / 3;
846         sp_node_adjust_handle(start, 1);
847         sp_node_adjust_handle(end, -1);
848     }
850     sp_node_update_handles(start);
851     sp_node_update_handles(end);
854 /**
855  * Change node type, and its handles accordingly.
856  */
857 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeType type)
859     g_assert(node);
860     g_assert(node->subpath);
862     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
863         return node;
865     if ((node->p.other != NULL) && (node->n.other != NULL)) {
866         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
867             type =Inkscape::NodePath::NODE_CUSP;
868         }
869     }
871     node->type = type;
873     if (node->type == Inkscape::NodePath::NODE_CUSP) {
874         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
875         node->knot->setSize (node->selected? 11 : 9);
876         sp_knot_update_ctrl(node->knot);
877     } else {
878         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
879         node->knot->setSize (node->selected? 9 : 7);
880         sp_knot_update_ctrl(node->knot);
881     }
883     sp_node_adjust_handles(node);
884     sp_node_update_handles(node);
886     sp_nodepath_update_statusbar(node->subpath->nodepath);
888     return node;
891 /**
892  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
893  * adjacent segments from lines to curves.
894 */
895 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
897     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
898         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
899             // convert adjacent segment BEFORE to curve
900             node->code = NR_CURVETO;
901             NR::Point delta;
902             if (node->n.other != NULL)
903                 delta = node->n.other->pos - node->p.other->pos;
904             else
905                 delta = node->pos - node->p.other->pos;
906             node->p.pos = node->pos - delta / 4;
907             sp_node_update_handles(node);
908         }
910         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
911             // convert adjacent segment AFTER to curve
912             node->n.other->code = NR_CURVETO;
913             NR::Point delta;
914             if (node->p.other != NULL)
915                 delta = node->p.other->pos - node->n.other->pos;
916             else
917                 delta = node->pos - node->n.other->pos;
918             node->n.pos = node->pos - delta / 4;
919             sp_node_update_handles(node);
920         }
921     }
923     sp_nodepath_set_node_type (node, type);
926 /**
927  * Move node to point, and adjust its and neighbouring handles.
928  */
929 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
931     NR::Point delta = p - node->pos;
932     node->pos = p;
934     node->p.pos += delta;
935     node->n.pos += delta;
937     if (node->p.other) {
938         if (node->code == NR_LINETO) {
939             sp_node_adjust_handle(node, 1);
940             sp_node_adjust_handle(node->p.other, -1);
941         }
942     }
943     if (node->n.other) {
944         if (node->n.other->code == NR_LINETO) {
945             sp_node_adjust_handle(node, -1);
946             sp_node_adjust_handle(node->n.other, 1);
947         }
948     }
950     // this function is only called from batch movers that will update display at the end
951     // themselves, so here we just move all the knots without emitting move signals, for speed
952     sp_node_update_handles(node, false);
955 /**
956  * Call sp_node_moveto() for node selection and handle possible snapping.
957  */
958 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
959                                             bool const snap = true)
961     NR::Coord best[2] = { NR_HUGE, NR_HUGE };
962     NR::Point delta(dx, dy);
963     NR::Point best_pt = delta;
965     if (snap) {
966         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
967            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
968             NR::Point p = n->pos + delta;
969             for (int dim = 0; dim < 2; dim++) {
970                 NR::Coord dist = namedview_dim_snap(nodepath->desktop->namedview,
971                                                     Inkscape::Snapper::SNAP_POINT, p,
972                                                     NR::Dim2(dim), nodepath->path);
973                 if (dist < best[dim]) {
974                     best[dim] = dist;
975                     best_pt[dim] = p[dim] - n->pos[dim];
976                 }
977             }
978         }
979     }
981     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
982        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
983         sp_node_moveto(n, n->pos + best_pt);
984     }
986     // do not update repr here so that node dragging is acceptably fast
987     update_object(nodepath);
990 /**
991  * Move node selection to point, adjust its and neighbouring handles,
992  * handle possible snapping, and commit the change with possible undo.
993  */
994 void
995 sp_node_selected_move(gdouble dx, gdouble dy)
997     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
998     if (!nodepath) return;
1000     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1002     if (dx == 0) {
1003         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1004     } else if (dy == 0) {
1005         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1006     } else {
1007         sp_nodepath_update_repr(nodepath);
1008     }
1011 /**
1012  * Move node selection off screen and commit the change.
1013  */
1014 void
1015 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1017     // borrowed from sp_selection_move_screen in selection-chemistry.c
1018     // we find out the current zoom factor and divide deltas by it
1019     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1021     gdouble zoom = desktop->current_zoom();
1022     gdouble zdx = dx / zoom;
1023     gdouble zdy = dy / zoom;
1025     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1026     if (!nodepath) return;
1028     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1030     if (dx == 0) {
1031         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1032     } else if (dy == 0) {
1033         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1034     } else {
1035         sp_nodepath_update_repr(nodepath);
1036     }
1039 /** If they don't yet exist, creates knot and line for the given side of the node */
1040 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1042     if (!side->knot) {
1043         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"));
1045         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1046         side->knot->setSize (7);
1047         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1048         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1049         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1050         sp_knot_update_ctrl(side->knot);
1052         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1053         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1054         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1055         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1056         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1057         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1058     }
1060     if (!side->line) {
1061         side->line = sp_canvas_item_new(SP_DT_CONTROLS(desktop),
1062                                         SP_TYPE_CTRLLINE, NULL);
1063     }
1066 /**
1067  * Ensure the given handle of the node is visible/invisible, update its screen position
1068  */
1069 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1071     g_assert(node != NULL);
1073    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1074     NRPathcode code = sp_node_path_code_from_side(node, side);
1076     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1078     if (show_handle) {
1079         if (!side->knot) { // No handle knot at all
1080             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1081             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1082             side->knot->pos = side->pos;
1083             if (side->knot->item) 
1084                 SP_CTRL(side->knot->item)->moveto(side->pos);
1085             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1086             sp_knot_show(side->knot);
1087         } else {
1088             if (side->knot->pos != side->pos) { // only if it's really moved
1089                 if (fire_move_signals) {
1090                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1091                 } else {
1092                     sp_knot_moveto(side->knot, &side->pos);
1093                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1094                 }
1095             }
1096             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1097                 sp_knot_show(side->knot);
1098             }
1099         }
1100         sp_canvas_item_show(side->line);
1101     } else {
1102         if (side->knot) {
1103             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1104                 sp_knot_hide(side->knot);
1105             }
1106         }
1107         if (side->line) {
1108             sp_canvas_item_hide(side->line);
1109         }
1110     }
1113 /**
1114  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1115  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1116  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1117  * updated; otherwise, just move the knots silently (used in batch moves).
1118  */
1119 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1121     g_assert(node != NULL);
1123     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1124         sp_knot_show(node->knot);
1125     }
1127     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1128         if (fire_move_signals)
1129             sp_knot_set_position(node->knot, &node->pos, 0);
1130         else 
1131             sp_knot_moveto(node->knot, &node->pos);
1132     }
1134     gboolean show_handles = node->selected;
1135     if (node->p.other != NULL) {
1136         if (node->p.other->selected) show_handles = TRUE;
1137     }
1138     if (node->n.other != NULL) {
1139         if (node->n.other->selected) show_handles = TRUE;
1140     }
1142     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1143     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1146 /**
1147  * Call sp_node_update_handles() for all nodes on subpath.
1148  */
1149 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1151     g_assert(subpath != NULL);
1153     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1154         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1155     }
1158 /**
1159  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1160  */
1161 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1163     g_assert(nodepath != NULL);
1165     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1166         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1167     }
1170 /**
1171  * Adds all selected nodes in nodepath to list.
1172  */
1173 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1175     StlConv<Node *>::list(l, selected);
1176 /// \todo this adds a copying, rework when the selection becomes a stl list
1179 /**
1180  * Align selected nodes on the specified axis.
1181  */
1182 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1184     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1185         return;
1186     }
1188     if ( !nodepath->selected->next ) { // only one node selected
1189         return;
1190     }
1191    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1192     NR::Point dest(pNode->pos);
1193     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1194         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1195         if (pNode) {
1196             dest[axis] = pNode->pos[axis];
1197             sp_node_moveto(pNode, dest);
1198         }
1199     }
1201     sp_nodepath_update_repr(nodepath);
1204 /// Helper struct.
1205 struct NodeSort
1207    Inkscape::NodePath::Node *_node;
1208     NR::Coord _coord;
1209     /// \todo use vectorof pointers instead of calling copy ctor
1210     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1211         _node(node), _coord(node->pos[axis])
1212     {}
1214 };
1216 static bool operator<(NodeSort const &a, NodeSort const &b)
1218     return (a._coord < b._coord);
1221 /**
1222  * Distribute selected nodes on the specified axis.
1223  */
1224 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1226     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1227         return;
1228     }
1230     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1231         return;
1232     }
1234    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1235     std::vector<NodeSort> sorted;
1236     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1237         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1238         if (pNode) {
1239             NodeSort n(pNode, axis);
1240             sorted.push_back(n);
1241             //dest[axis] = pNode->pos[axis];
1242             //sp_node_moveto(pNode, dest);
1243         }
1244     }
1245     std::sort(sorted.begin(), sorted.end());
1246     unsigned int len = sorted.size();
1247     //overall bboxes span
1248     float dist = (sorted.back()._coord -
1249                   sorted.front()._coord);
1250     //new distance between each bbox
1251     float step = (dist) / (len - 1);
1252     float pos = sorted.front()._coord;
1253     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1254           it < sorted.end();
1255           it ++ )
1256     {
1257         NR::Point dest((*it)._node->pos);
1258         dest[axis] = pos;
1259         sp_node_moveto((*it)._node, dest);
1260         pos += step;
1261     }
1263     sp_nodepath_update_repr(nodepath);
1267 /**
1268  * Call sp_nodepath_line_add_node() for all selected segments.
1269  */
1270 void
1271 sp_node_selected_add_node(void)
1273     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1274     if (!nodepath) {
1275         return;
1276     }
1278     GList *nl = NULL;
1280     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1281        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1282         g_assert(t->selected);
1283         if (t->p.other && t->p.other->selected) {
1284             nl = g_list_prepend(nl, t);
1285         }
1286     }
1288     while (nl) {
1289        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1290        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1291         sp_nodepath_node_select(n, TRUE, FALSE);
1292         nl = g_list_remove(nl, t);
1293     }
1295     /** \todo fixme: adjust ? */
1296     sp_nodepath_update_handles(nodepath);
1298     sp_nodepath_update_repr(nodepath);
1300     sp_nodepath_update_statusbar(nodepath);
1303 /**
1304  * Select segment nearest to point
1305  */
1306 void
1307 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1309     if (!nodepath) {
1310         return;
1311     }
1313     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1315     //find segment to segment
1316     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1318     gboolean force = FALSE;
1319     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1320         force = TRUE;
1321     }
1322     sp_nodepath_node_select(e, (gboolean) toggle, force);
1323     if (e->p.other)
1324         sp_nodepath_node_select(e->p.other, TRUE, force);
1326     sp_nodepath_update_handles(nodepath);
1328     sp_nodepath_update_statusbar(nodepath);
1331 /**
1332  * Add a node nearest to point
1333  */
1334 void
1335 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1337     if (!nodepath) {
1338         return;
1339     }
1341     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1343     //find segment to split
1344     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1346     //don't know why but t seems to flip for lines
1347     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1348         position.t = 1.0 - position.t;
1349     }
1350     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1351     sp_nodepath_node_select(n, FALSE, TRUE);
1353     /* fixme: adjust ? */
1354     sp_nodepath_update_handles(nodepath);
1356     sp_nodepath_update_repr(nodepath);
1358     sp_nodepath_update_statusbar(nodepath);
1361 /*
1362  * Adjusts a segment so that t moves by a certain delta for dragging
1363  * converts lines to curves
1364  *
1365  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1366  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1367  */
1368 void
1369 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1371     /* feel good is an arbitrary parameter that distributes the delta between handles
1372      * if t of the drag point is less than 1/6 distance form the endpoint only
1373      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1374      */
1375     double feel_good;
1376     if (t <= 1.0 / 6.0)
1377         feel_good = 0;
1378     else if (t <= 0.5)
1379         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1380     else if (t <= 5.0 / 6.0)
1381         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1382     else
1383         feel_good = 1;
1385     //if we're dragging a line convert it to a curve
1386     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1387         sp_nodepath_set_line_type(e, NR_CURVETO);
1388     }
1390     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1391     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1392     e->p.other->n.pos += offsetcoord0;
1393     e->p.pos += offsetcoord1;
1395     // adjust handles of adjacent nodes where necessary
1396     sp_node_adjust_handle(e,1);
1397     sp_node_adjust_handle(e->p.other,-1);
1399     sp_nodepath_update_handles(e->subpath->nodepath);
1401     update_object(e->subpath->nodepath);
1403     sp_nodepath_update_statusbar(e->subpath->nodepath);
1407 /**
1408  * Call sp_nodepath_break() for all selected segments.
1409  */
1410 void sp_node_selected_break()
1412     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1413     if (!nodepath) return;
1415     GList *temp = NULL;
1416     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1417        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1418        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1419         if (nn == NULL) continue; // no break, no new node
1420         temp = g_list_prepend(temp, nn);
1421     }
1423     if (temp) {
1424         sp_nodepath_deselect(nodepath);
1425     }
1426     for (GList *l = temp; l != NULL; l = l->next) {
1427         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1428     }
1430     sp_nodepath_update_handles(nodepath);
1432     sp_nodepath_update_repr(nodepath);
1435 /**
1436  * Duplicate the selected node(s).
1437  */
1438 void sp_node_selected_duplicate()
1440     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1441     if (!nodepath) {
1442         return;
1443     }
1445     GList *temp = NULL;
1446     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1447        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1448        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1449         if (nn == NULL) continue; // could not duplicate
1450         temp = g_list_prepend(temp, nn);
1451     }
1453     if (temp) {
1454         sp_nodepath_deselect(nodepath);
1455     }
1456     for (GList *l = temp; l != NULL; l = l->next) {
1457         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1458     }
1460     sp_nodepath_update_handles(nodepath);
1462     sp_nodepath_update_repr(nodepath);
1465 /**
1466  *  Join two nodes by merging them into one.
1467  */
1468 void sp_node_selected_join()
1470     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1471     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1473     if (g_list_length(nodepath->selected) != 2) {
1474         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1475         return;
1476     }
1478    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1479    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1481     g_assert(a != b);
1482     g_assert(a->p.other || a->n.other);
1483     g_assert(b->p.other || b->n.other);
1485     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1486         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1487         return;
1488     }
1490     /* a and b are endpoints */
1492     NR::Point c = (a->pos + b->pos) / 2;
1494     if (a->subpath == b->subpath) {
1495        Inkscape::NodePath::SubPath *sp = a->subpath;
1496         sp_nodepath_subpath_close(sp);
1498         sp_nodepath_update_handles(sp->nodepath);
1500         sp_nodepath_update_repr(nodepath);
1502         return;
1503     }
1505     /* a and b are separate subpaths */
1506    Inkscape::NodePath::SubPath *sa = a->subpath;
1507    Inkscape::NodePath::SubPath *sb = b->subpath;
1508     NR::Point p;
1509    Inkscape::NodePath::Node *n;
1510     NRPathcode code;
1511     if (a == sa->first) {
1512         p = sa->first->n.pos;
1513         code = (NRPathcode)sa->first->n.other->code;
1514        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1515         n = sa->last;
1516         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1517         n = n->p.other;
1518         while (n) {
1519             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1520             n = n->p.other;
1521             if (n == sa->first) n = NULL;
1522         }
1523         sp_nodepath_subpath_destroy(sa);
1524         sa = t;
1525     } else if (a == sa->last) {
1526         p = sa->last->p.pos;
1527         code = (NRPathcode)sa->last->code;
1528         sp_nodepath_node_destroy(sa->last);
1529     } else {
1530         code = NR_END;
1531         g_assert_not_reached();
1532     }
1534     if (b == sb->first) {
1535         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1536         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1537             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1538         }
1539     } else if (b == sb->last) {
1540         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1541         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1542             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1543         }
1544     } else {
1545         g_assert_not_reached();
1546     }
1547     /* and now destroy sb */
1549     sp_nodepath_subpath_destroy(sb);
1551     sp_nodepath_update_handles(sa->nodepath);
1553     sp_nodepath_update_repr(nodepath);
1555     sp_nodepath_update_statusbar(nodepath);
1558 /**
1559  *  Join two nodes by adding a segment between them.
1560  */
1561 void sp_node_selected_join_segment()
1563     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1564     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1566     if (g_list_length(nodepath->selected) != 2) {
1567         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1568         return;
1569     }
1571    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1572    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1574     g_assert(a != b);
1575     g_assert(a->p.other || a->n.other);
1576     g_assert(b->p.other || b->n.other);
1578     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1579         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1580         return;
1581     }
1583     if (a->subpath == b->subpath) {
1584        Inkscape::NodePath::SubPath *sp = a->subpath;
1586         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1587         sp->closed = TRUE;
1589         sp->first->p.other = sp->last;
1590         sp->last->n.other  = sp->first;
1592         sp_node_handle_mirror_p_to_n(sp->last);
1593         sp_node_handle_mirror_n_to_p(sp->first);
1595         sp->first->code = sp->last->code;
1596         sp->first       = sp->last;
1598         sp_nodepath_update_handles(sp->nodepath);
1600         sp_nodepath_update_repr(nodepath);
1602         return;
1603     }
1605     /* a and b are separate subpaths */
1606    Inkscape::NodePath::SubPath *sa = a->subpath;
1607    Inkscape::NodePath::SubPath *sb = b->subpath;
1609    Inkscape::NodePath::Node *n;
1610     NR::Point p;
1611     NRPathcode code;
1612     if (a == sa->first) {
1613         code = (NRPathcode) sa->first->n.other->code;
1614        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1615         n = sa->last;
1616         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1617         for (n = n->p.other; n != NULL; n = n->p.other) {
1618             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1619         }
1620         sp_nodepath_subpath_destroy(sa);
1621         sa = t;
1622     } else if (a == sa->last) {
1623         code = (NRPathcode)sa->last->code;
1624     } else {
1625         code = NR_END;
1626         g_assert_not_reached();
1627     }
1629     if (b == sb->first) {
1630         n = sb->first;
1631         sp_node_handle_mirror_p_to_n(sa->last);
1632         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1633         sp_node_handle_mirror_n_to_p(sa->last);
1634         for (n = n->n.other; n != NULL; n = n->n.other) {
1635             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1636         }
1637     } else if (b == sb->last) {
1638         n = sb->last;
1639         sp_node_handle_mirror_p_to_n(sa->last);
1640         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1641         sp_node_handle_mirror_n_to_p(sa->last);
1642         for (n = n->p.other; n != NULL; n = n->p.other) {
1643             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1644         }
1645     } else {
1646         g_assert_not_reached();
1647     }
1648     /* and now destroy sb */
1650     sp_nodepath_subpath_destroy(sb);
1652     sp_nodepath_update_handles(sa->nodepath);
1654     sp_nodepath_update_repr(nodepath);
1657 /**
1658  * Delete one or more selected nodes.
1659  */
1660 void sp_node_selected_delete()
1662     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1663     if (!nodepath) return;
1664     if (!nodepath->selected) return;
1666     /** \todo fixme: do it the right way */
1667     while (nodepath->selected) {
1668        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1669         sp_nodepath_node_destroy(node);
1670     }
1673     //clean up the nodepath (such as for trivial subpaths)
1674     sp_nodepath_cleanup(nodepath);
1676     sp_nodepath_update_handles(nodepath);
1678     // if the entire nodepath is removed, delete the selected object.
1679     if (nodepath->subpaths == NULL ||
1680         sp_nodepath_get_node_count(nodepath) < 2) {
1681         SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
1682         sp_nodepath_destroy(nodepath);
1683         sp_selection_delete();
1684         sp_document_done (document);
1685         return;
1686     }
1688     sp_nodepath_update_repr(nodepath);
1690     sp_nodepath_update_statusbar(nodepath);
1693 /**
1694  * Delete one or more segments between two selected nodes.
1695  * This is the code for 'split'.
1696  */
1697 void
1698 sp_node_selected_delete_segment(void)
1700    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
1701    Inkscape::NodePath::Node *curr, *next;     //Iterators
1703     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1704     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1706     if (g_list_length(nodepath->selected) != 2) {
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     //Selected nodes, not inclusive
1713    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1714    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1716     if ( ( a==b)                       ||  //same node
1717          (a->subpath  != b->subpath )  ||  //not the same path
1718          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
1719          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
1720     {
1721         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1722                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1723         return;
1724     }
1726     //###########################################
1727     //# BEGIN EDITS
1728     //###########################################
1729     //##################################
1730     //# CLOSED PATH
1731     //##################################
1732     if (a->subpath->closed) {
1735         gboolean reversed = FALSE;
1737         //Since we can go in a circle, we need to find the shorter distance.
1738         //  a->b or b->a
1739         start = end = NULL;
1740         int distance    = 0;
1741         int minDistance = 0;
1742         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1743             if (curr==b) {
1744                 //printf("a to b:%d\n", distance);
1745                 start = a;//go from a to b
1746                 end   = b;
1747                 minDistance = distance;
1748                 //printf("A to B :\n");
1749                 break;
1750             }
1751             distance++;
1752         }
1754         //try again, the other direction
1755         distance = 0;
1756         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1757             if (curr==a) {
1758                 //printf("b to a:%d\n", distance);
1759                 if (distance < minDistance) {
1760                     start    = b;  //we go from b to a
1761                     end      = a;
1762                     reversed = TRUE;
1763                     //printf("B to A\n");
1764                 }
1765                 break;
1766             }
1767             distance++;
1768         }
1771         //Copy everything from 'end' to 'start' to a new subpath
1772        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1773         for (curr=end ; curr ; curr=curr->n.other) {
1774             NRPathcode code = (NRPathcode) curr->code;
1775             if (curr == end)
1776                 code = NR_MOVETO;
1777             sp_nodepath_node_new(t, NULL,
1778                                  (Inkscape::NodePath::NodeType)curr->type, code,
1779                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1780             if (curr == start)
1781                 break;
1782         }
1783         sp_nodepath_subpath_destroy(a->subpath);
1786     }
1790     //##################################
1791     //# OPEN PATH
1792     //##################################
1793     else {
1795         //We need to get the direction of the list between A and B
1796         //Can we walk from a to b?
1797         start = end = NULL;
1798         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1799             if (curr==b) {
1800                 start = a;  //did it!  we go from a to b
1801                 end   = b;
1802                 //printf("A to B\n");
1803                 break;
1804             }
1805         }
1806         if (!start) {//didn't work?  let's try the other direction
1807             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1808                 if (curr==a) {
1809                     start = b;  //did it!  we go from b to a
1810                     end   = a;
1811                     //printf("B to A\n");
1812                     break;
1813                 }
1814             }
1815         }
1816         if (!start) {
1817             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1818                                                      _("Cannot find path between nodes."));
1819             return;
1820         }
1824         //Copy everything after 'end' to a new subpath
1825        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1826         for (curr=end ; curr ; curr=curr->n.other) {
1827             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
1828                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1829         }
1831         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
1832         for (curr = start->n.other ; curr  ; curr=next) {
1833             next = curr->n.other;
1834             sp_nodepath_node_destroy(curr);
1835         }
1837     }
1838     //###########################################
1839     //# END EDITS
1840     //###########################################
1842     //clean up the nodepath (such as for trivial subpaths)
1843     sp_nodepath_cleanup(nodepath);
1845     sp_nodepath_update_handles(nodepath);
1847     sp_nodepath_update_repr(nodepath);
1849     // if the entire nodepath is removed, delete the selected object.
1850     if (nodepath->subpaths == NULL ||
1851         sp_nodepath_get_node_count(nodepath) < 2) {
1852         sp_nodepath_destroy(nodepath);
1853         sp_selection_delete();
1854         return;
1855     }
1857     sp_nodepath_update_statusbar(nodepath);
1860 /**
1861  * Call sp_nodepath_set_line() for all selected segments.
1862  */
1863 void
1864 sp_node_selected_set_line_type(NRPathcode code)
1866     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1867     if (nodepath == NULL) return;
1869     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1870        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1871         g_assert(n->selected);
1872         if (n->p.other && n->p.other->selected) {
1873             sp_nodepath_set_line_type(n, code);
1874         }
1875     }
1877     sp_nodepath_update_repr(nodepath);
1880 /**
1881  * Call sp_nodepath_convert_node_type() for all selected nodes.
1882  */
1883 void
1884 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
1886     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1887     if (nodepath == NULL) return;
1889     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1890         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
1891     }
1893     sp_nodepath_update_repr(nodepath);
1896 /**
1897  * Change select status of node, update its own and neighbour handles.
1898  */
1899 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
1901     node->selected = selected;
1903     if (selected) {
1904         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
1905         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
1906         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
1907         sp_knot_update_ctrl(node->knot);
1908     } else {
1909         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
1910         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
1911         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
1912         sp_knot_update_ctrl(node->knot);
1913     }
1915     sp_node_update_handles(node);
1916     if (node->n.other) sp_node_update_handles(node->n.other);
1917     if (node->p.other) sp_node_update_handles(node->p.other);
1920 /**
1921 \brief Select a node
1922 \param node     The node to select
1923 \param incremental   If true, add to selection, otherwise deselect others
1924 \param override   If true, always select this node, otherwise toggle selected status
1925 */
1926 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
1928     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
1930     if (incremental) {
1931         if (override) {
1932             if (!g_list_find(nodepath->selected, node)) {
1933                 nodepath->selected = g_list_prepend(nodepath->selected, node);
1934             }
1935             sp_node_set_selected(node, TRUE);
1936         } else { // toggle
1937             if (node->selected) {
1938                 g_assert(g_list_find(nodepath->selected, node));
1939                 nodepath->selected = g_list_remove(nodepath->selected, node);
1940             } else {
1941                 g_assert(!g_list_find(nodepath->selected, node));
1942                 nodepath->selected = g_list_prepend(nodepath->selected, node);
1943             }
1944             sp_node_set_selected(node, !node->selected);
1945         }
1946     } else {
1947         sp_nodepath_deselect(nodepath);
1948         nodepath->selected = g_list_prepend(nodepath->selected, node);
1949         sp_node_set_selected(node, TRUE);
1950     }
1952     sp_nodepath_update_statusbar(nodepath);
1956 /**
1957 \brief Deselect all nodes in the nodepath
1958 */
1959 void
1960 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
1962     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1964     while (nodepath->selected) {
1965         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
1966         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
1967     }
1968     sp_nodepath_update_statusbar(nodepath);
1971 /**
1972 \brief Select or invert selection of all nodes in the nodepath
1973 */
1974 void
1975 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
1977     if (!nodepath) return;
1979     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1980        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1981         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1982            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1983            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
1984         }
1985     }
1988 /**
1989  * If nothing selected, does the same as sp_nodepath_select_all();
1990  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
1991  * (i.e., similar to "select all in layer", with the "selected" subpaths
1992  * being treated as "layers" in the path).
1993  */
1994 void
1995 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
1997     if (!nodepath) return;
1999     if (g_list_length (nodepath->selected) == 0) {
2000         sp_nodepath_select_all (nodepath, invert);
2001         return;
2002     }
2004     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2005     GSList *subpaths = NULL;
2007     for (GList *l = copy; l != NULL; l = l->next) {
2008         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2009         Inkscape::NodePath::SubPath *subpath = n->subpath;
2010         if (!g_slist_find (subpaths, subpath))
2011             subpaths = g_slist_prepend (subpaths, subpath);
2012     }
2014     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2015         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2016         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2017             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2018             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2019         }
2020     }
2022     g_slist_free (subpaths);
2023     g_list_free (copy);
2026 /**
2027  * \brief Select the node after the last selected; if none is selected,
2028  * select the first within path.
2029  */
2030 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2032     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2034    Inkscape::NodePath::Node *last = NULL;
2035     if (nodepath->selected) {
2036         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2037            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2038             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2039             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2040                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2041                 if (node->selected) {
2042                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2043                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2044                             if (spl->next) { // there's a next subpath
2045                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2046                                 last = subpath_next->first;
2047                             } else if (spl->prev) { // there's a previous subpath
2048                                 last = NULL; // to be set later to the first node of first subpath
2049                             } else {
2050                                 last = node->n.other;
2051                             }
2052                         } else {
2053                             last = node->n.other;
2054                         }
2055                     } else {
2056                         if (node->n.other) {
2057                             last = node->n.other;
2058                         } else {
2059                             if (spl->next) { // there's a next subpath
2060                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2061                                 last = subpath_next->first;
2062                             } else if (spl->prev) { // there's a previous subpath
2063                                 last = NULL; // to be set later to the first node of first subpath
2064                             } else {
2065                                 last = (Inkscape::NodePath::Node *) subpath->first;
2066                             }
2067                         }
2068                     }
2069                 }
2070             }
2071         }
2072         sp_nodepath_deselect(nodepath);
2073     }
2075     if (last) { // there's at least one more node after selected
2076         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2077     } else { // no more nodes, select the first one in first subpath
2078        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2079         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2080     }
2083 /**
2084  * \brief Select the node before the first selected; if none is selected,
2085  * select the last within path
2086  */
2087 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2089     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2091    Inkscape::NodePath::Node *last = NULL;
2092     if (nodepath->selected) {
2093         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2094            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2095             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2096                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2097                 if (node->selected) {
2098                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2099                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2100                             if (spl->prev) { // there's a prev subpath
2101                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2102                                 last = subpath_prev->last;
2103                             } else if (spl->next) { // there's a next subpath
2104                                 last = NULL; // to be set later to the last node of last subpath
2105                             } else {
2106                                 last = node->p.other;
2107                             }
2108                         } else {
2109                             last = node->p.other;
2110                         }
2111                     } else {
2112                         if (node->p.other) {
2113                             last = node->p.other;
2114                         } else {
2115                             if (spl->prev) { // there's a prev subpath
2116                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2117                                 last = subpath_prev->last;
2118                             } else if (spl->next) { // there's a next subpath
2119                                 last = NULL; // to be set later to the last node of last subpath
2120                             } else {
2121                                 last = (Inkscape::NodePath::Node *) subpath->last;
2122                             }
2123                         }
2124                     }
2125                 }
2126             }
2127         }
2128         sp_nodepath_deselect(nodepath);
2129     }
2131     if (last) { // there's at least one more node before selected
2132         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2133     } else { // no more nodes, select the last one in last subpath
2134         GList *spl = g_list_last(nodepath->subpaths);
2135        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2136         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2137     }
2140 /**
2141  * \brief Select all nodes that are within the rectangle.
2142  */
2143 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2145     if (!incremental) {
2146         sp_nodepath_deselect(nodepath);
2147     }
2149     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2150        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2151         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2152            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2154             if (b.contains(node->pos)) {
2155                 sp_nodepath_node_select(node, TRUE, TRUE);
2156             }
2157         }
2158     }
2161 /**
2162 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2163 */
2164 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2166     if (!nodepath->selected) {
2167         return NULL;
2168     }
2170     GList *r = NULL;
2171     guint i = 0;
2172     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2173        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2174         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2175            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2176             i++;
2177             if (node->selected) {
2178                 r = g_list_append(r, GINT_TO_POINTER(i));
2179             }
2180         }
2181     }
2182     return r;
2185 /**
2186 \brief  Restores selection by selecting nodes whose positions are in the list
2187 */
2188 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2190     sp_nodepath_deselect(nodepath);
2192     guint i = 0;
2193     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2194        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2195         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2196            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2197             i++;
2198             if (g_list_find(r, GINT_TO_POINTER(i))) {
2199                 sp_nodepath_node_select(node, TRUE, TRUE);
2200             }
2201         }
2202     }
2206 /**
2207 \brief Adjusts handle according to node type and line code.
2208 */
2209 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2211     double len, otherlen, linelen;
2213     g_assert(node);
2215    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2216    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2218     /** \todo fixme: */
2219     if (me->other == NULL) return;
2220     if (other->other == NULL) return;
2222     /* I have line */
2224     NRPathcode mecode, ocode;
2225     if (which_adjust == 1) {
2226         mecode = (NRPathcode)me->other->code;
2227         ocode = (NRPathcode)node->code;
2228     } else {
2229         mecode = (NRPathcode)node->code;
2230         ocode = (NRPathcode)other->other->code;
2231     }
2233     if (mecode == NR_LINETO) return;
2235     /* I am curve */
2237     if (other->other == NULL) return;
2239     /* Other has line */
2241     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2243     NR::Point delta;
2244     if (ocode == NR_LINETO) {
2245         /* other is lineto, we are either smooth or symm */
2246        Inkscape::NodePath::Node *othernode = other->other;
2247         len = NR::L2(me->pos - node->pos);
2248         delta = node->pos - othernode->pos;
2249         linelen = NR::L2(delta);
2250         if (linelen < 1e-18) 
2251             return;
2252         me->pos = node->pos + (len / linelen)*delta;
2253         return;
2254     }
2256     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2258         me->pos = 2 * node->pos - other->pos;
2259         return;
2260     }
2262     /* We are smooth */
2264     len = NR::L2(me->pos - node->pos);
2265     delta = other->pos - node->pos;
2266     otherlen = NR::L2(delta);
2267     if (otherlen < 1e-18) return;
2269     me->pos = node->pos - (len / otherlen) * delta;
2272 /**
2273  \brief Adjusts both handles according to node type and line code
2274  */
2275 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2277     g_assert(node);
2279     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2281     /* we are either smooth or symm */
2283     if (node->p.other == NULL) return;
2285     if (node->n.other == NULL) return;
2287     if (node->code == NR_LINETO) {
2288         if (node->n.other->code == NR_LINETO) return;
2289         sp_node_adjust_handle(node, 1);
2290         return;
2291     }
2293     if (node->n.other->code == NR_LINETO) {
2294         if (node->code == NR_LINETO) return;
2295         sp_node_adjust_handle(node, -1);
2296         return;
2297     }
2299     /* both are curves */
2300     NR::Point const delta( node->n.pos - node->p.pos );
2302     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2303         node->p.pos = node->pos - delta / 2;
2304         node->n.pos = node->pos + delta / 2;
2305         return;
2306     }
2308     /* We are smooth */
2309     double plen = NR::L2(node->p.pos - node->pos);
2310     if (plen < 1e-18) return;
2311     double nlen = NR::L2(node->n.pos - node->pos);
2312     if (nlen < 1e-18) return;
2313     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2314     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2317 /**
2318  * Node event callback.
2319  */
2320 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2322     gboolean ret = FALSE;
2323     switch (event->type) {
2324         case GDK_ENTER_NOTIFY:
2325             active_node = n;
2326             break;
2327         case GDK_LEAVE_NOTIFY:
2328             active_node = NULL;
2329             break;
2330         case GDK_KEY_PRESS:
2331             switch (get_group0_keyval (&event->key)) {
2332                 case GDK_space:
2333                     if (event->key.state & GDK_BUTTON1_MASK) {
2334                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2335                         stamp_repr(nodepath);
2336                         ret = TRUE;
2337                     }
2338                     break;
2339                 default:
2340                     break;
2341             }
2342             break;
2343         default:
2344             break;
2345     }
2347     return ret;
2350 /**
2351  * Handle keypress on node; directly called.
2352  */
2353 gboolean node_key(GdkEvent *event)
2355     Inkscape::NodePath::Path *np;
2357     // there is no way to verify nodes so set active_node to nil when deleting!!
2358     if (active_node == NULL) return FALSE;
2360     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2361         gint ret = FALSE;
2362         switch (get_group0_keyval (&event->key)) {
2363             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2364             case GDK_BackSpace:
2365                 np = active_node->subpath->nodepath;
2366                 sp_nodepath_node_destroy(active_node);
2367                 sp_nodepath_update_repr(np);
2368                 active_node = NULL;
2369                 ret = TRUE;
2370                 break;
2371             case GDK_c:
2372                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2373                 ret = TRUE;
2374                 break;
2375             case GDK_s:
2376                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2377                 ret = TRUE;
2378                 break;
2379             case GDK_y:
2380                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2381                 ret = TRUE;
2382                 break;
2383             case GDK_b:
2384                 sp_nodepath_node_break(active_node);
2385                 ret = TRUE;
2386                 break;
2387         }
2388         return ret;
2389     }
2390     return FALSE;
2393 /**
2394  * Mouseclick on node callback.
2395  */
2396 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2398    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2400     if (state & GDK_CONTROL_MASK) {
2401         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2403         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2404             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2405                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2406             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2407                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2408             } else {
2409                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2410             }
2411             sp_nodepath_update_repr(nodepath);
2412             sp_nodepath_update_statusbar(nodepath);
2414         } else { //ctrl+alt+click: delete node
2415             sp_nodepath_node_destroy(n);
2416             //clean up the nodepath (such as for trivial subpaths)
2417             sp_nodepath_cleanup(nodepath);
2419             // if the entire nodepath is removed, delete the selected object.
2420             if (nodepath->subpaths == NULL ||
2421                 sp_nodepath_get_node_count(nodepath) < 2) {
2422                 SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
2423                 sp_nodepath_destroy(nodepath);
2424                 sp_selection_delete();
2425                 sp_document_done (document);
2427             } else {
2428                 sp_nodepath_update_handles(nodepath);
2429                 sp_nodepath_update_repr(nodepath);
2430                 sp_nodepath_update_statusbar(nodepath);
2431             }
2432         }
2434     } else {
2435         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2436     }
2439 /**
2440  * Mouse grabbed node callback.
2441  */
2442 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2444    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2446     n->origin = knot->pos;
2448     if (!n->selected) {
2449         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2450     }
2453 /**
2454  * Mouse ungrabbed node callback.
2455  */
2456 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2458    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2460    n->dragging_out = NULL;
2462    sp_nodepath_update_repr(n->subpath->nodepath);
2465 /**
2466  * The point on a line, given by its angle, closest to the given point.
2467  * \param p  A point.
2468  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2469  * \param closest  Pointer to the point struct where the result is stored.
2470  * \todo FIXME: use dot product perhaps?
2471  */
2472 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2474     if (a == HUGE_VAL) { // vertical
2475         *closest = NR::Point(0, (*p)[NR::Y]);
2476     } else {
2477         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2478         (*closest)[NR::Y] = a * (*closest)[NR::X];
2479     }
2482 /**
2483  * Distance from the point to a line given by its angle.
2484  * \param p  A point.
2485  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2486  */
2487 static double point_line_distance(NR::Point *p, double a)
2489     NR::Point c;
2490     point_line_closest(p, a, &c);
2491     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]));
2494 /**
2495  * Callback for node "request" signal.
2496  * \todo fixme: This goes to "moved" event? (lauris)
2497  */
2498 static gboolean
2499 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2501     double yn, xn, yp, xp;
2502     double an, ap, na, pa;
2503     double d_an, d_ap, d_na, d_pa;
2504     gboolean collinear = FALSE;
2505     NR::Point c;
2506     NR::Point pr;
2508    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2510    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2511    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2513        NR::Point mouse = (*p);
2515        if (!n->dragging_out) {
2516            // This is the first drag-out event; find out which handle to drag out
2517            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2518            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2520            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2521                return FALSE;
2523            Inkscape::NodePath::NodeSide *opposite;
2524            if (appr_p > appr_n) { // closer to p
2525                n->dragging_out = &n->p;
2526                opposite = &n->n;
2527                n->code = NR_CURVETO;
2528            } else if (appr_p < appr_n) { // closer to n
2529                n->dragging_out = &n->n;
2530                opposite = &n->p;
2531                n->n.other->code = NR_CURVETO;
2532            } else { // p and n nodes are the same
2533                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2534                    n->dragging_out = &n->p;
2535                    opposite = &n->n;
2536                    n->code = NR_CURVETO;
2537                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2538                    n->dragging_out = &n->n;
2539                    opposite = &n->p;
2540                    n->n.other->code = NR_CURVETO;
2541                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2542                    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);
2543                    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);
2544                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2545                        n->dragging_out = &n->n;
2546                        opposite = &n->p;
2547                        n->n.other->code = NR_CURVETO;
2548                    } else { // closer to other's n handle
2549                        n->dragging_out = &n->p;
2550                        opposite = &n->n;
2551                        n->code = NR_CURVETO;
2552                    }
2553                }
2554            }
2556            // if there's another handle, make sure the one we drag out starts parallel to it
2557            if (opposite->pos != n->pos) {
2558                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2559            }
2561            // knots might not be created yet!
2562            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2563            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2564        }
2566        // pass this on to the handle-moved callback
2567        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2568        sp_node_update_handles(n);
2569        return TRUE;
2570    }
2572     if (state & GDK_CONTROL_MASK) { // constrained motion
2574         // calculate relative distances of handles
2575         // n handle:
2576         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2577         xn = n->n.pos[NR::X] - n->pos[NR::X];
2578         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2579         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2580             if (n->n.other) { // if there is the next point
2581                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2582                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2583                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2584             }
2585         }
2586         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2587         if (yn < 0) { xn = -xn; yn = -yn; }
2589         // p handle:
2590         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2591         xp = n->p.pos[NR::X] - n->pos[NR::X];
2592         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2593         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2594             if (n->p.other) {
2595                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2596                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2597                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2598             }
2599         }
2600         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2601         if (yp < 0) { xp = -xp; yp = -yp; }
2603         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2604             // sliding on handles, only if at least one of the handles is non-vertical
2605             // (otherwise it's the same as ctrl+drag anyway)
2607             // calculate angles of the handles
2608             if (xn == 0) {
2609                 if (yn == 0) { // no handle, consider it the continuation of the other one
2610                     an = 0;
2611                     collinear = TRUE;
2612                 }
2613                 else an = 0; // vertical; set the angle to horizontal
2614             } else an = yn/xn;
2616             if (xp == 0) {
2617                 if (yp == 0) { // no handle, consider it the continuation of the other one
2618                     ap = an;
2619                 }
2620                 else ap = 0; // vertical; set the angle to horizontal
2621             } else  ap = yp/xp;
2623             if (collinear) an = ap;
2625             // angles of the perpendiculars; HUGE_VAL means vertical
2626             if (an == 0) na = HUGE_VAL; else na = -1/an;
2627             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2629             //g_print("an %g    ap %g\n", an, ap);
2631             // mouse point relative to the node's original pos
2632             pr = (*p) - n->origin;
2634             // distances to the four lines (two handles and two perpendiculars)
2635             d_an = point_line_distance(&pr, an);
2636             d_na = point_line_distance(&pr, na);
2637             d_ap = point_line_distance(&pr, ap);
2638             d_pa = point_line_distance(&pr, pa);
2640             // find out which line is the closest, save its closest point in c
2641             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2642                 point_line_closest(&pr, an, &c);
2643             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2644                 point_line_closest(&pr, ap, &c);
2645             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2646                 point_line_closest(&pr, na, &c);
2647             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2648                 point_line_closest(&pr, pa, &c);
2649             }
2651             // move the node to the closest point
2652             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2653                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2654                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2656         } else {  // constraining to hor/vert
2658             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2659                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2660             } else { // snap to vert
2661                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2662             }
2663         }
2664     } else { // move freely
2665         sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2666                                         (*p)[NR::X] - n->pos[NR::X],
2667                                         (*p)[NR::Y] - n->pos[NR::Y],
2668                                         (state & GDK_SHIFT_MASK) == 0);
2669     }
2671     n->subpath->nodepath->desktop->scroll_to_point(p);
2673     return TRUE;
2676 /**
2677  * Node handle clicked callback.
2678  */
2679 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
2681    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2683     if (state & GDK_CONTROL_MASK) { // "delete" handle
2684         if (n->p.knot == knot) {
2685             n->p.pos = n->pos;
2686         } else if (n->n.knot == knot) {
2687             n->n.pos = n->pos;
2688         }
2689         sp_node_update_handles(n);
2690         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2691         sp_nodepath_update_repr(nodepath);
2692         sp_nodepath_update_statusbar(nodepath);
2694     } else { // just select or add to selection, depending in Shift
2695         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2696     }
2699 /**
2700  * Node handle grabbed callback.
2701  */
2702 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
2704    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2706     if (!n->selected) {
2707         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2708     }
2710     // remember the origin point of the handle
2711     if (n->p.knot == knot) {
2712         n->p.origin = n->p.pos - n->pos;
2713     } else if (n->n.knot == knot) {
2714         n->n.origin = n->n.pos - n->pos;
2715     } else {
2716         g_assert_not_reached();
2717     }
2721 /**
2722  * Node handle ungrabbed callback.
2723  */
2724 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
2726    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2728     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
2729     if (n->p.knot == knot) {
2730         n->p.origin.a = 0;
2731         sp_knot_set_position(knot, &n->p.pos, state);
2732     } else if (n->n.knot == knot) {
2733         n->n.origin.a = 0;
2734         sp_knot_set_position(knot, &n->n.pos, state);
2735     } else {
2736         g_assert_not_reached();
2737     }
2739     sp_nodepath_update_repr(n->subpath->nodepath);
2742 /**
2743  * Node handle "request" signal callback.
2744  */
2745 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2747     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2749     Inkscape::NodePath::NodeSide *me, *opposite;
2750     gint which;
2751     if (n->p.knot == knot) {
2752         me = &n->p;
2753         opposite = &n->n;
2754         which = -1;
2755     } else if (n->n.knot == knot) {
2756         me = &n->n;
2757         opposite = &n->p;
2758         which = 1;
2759     } else {
2760         me = opposite = NULL;
2761         which = 0;
2762         g_assert_not_reached();
2763     }
2765     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
2767     SnapManager const m(n->subpath->nodepath->desktop->namedview);
2769     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
2770         /* We are smooth node adjacent with line */
2771         NR::Point const delta = *p - n->pos;
2772         NR::Coord const len = NR::L2(delta);
2773         Inkscape::NodePath::Node *othernode = opposite->other;
2774         NR::Point const ndelta = n->pos - othernode->pos;
2775         NR::Coord const linelen = NR::L2(ndelta);
2776         if (len > NR_EPSILON && linelen > NR_EPSILON) {
2777             NR::Coord const scal = dot(delta, ndelta) / linelen;
2778             (*p) = n->pos + (scal / linelen) * ndelta;
2779         }
2780         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, ndelta, NULL).getPoint();
2781     } else {
2782         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
2783     }
2785     sp_node_adjust_handle(n, -which);
2787     return FALSE;
2790 /**
2791  * Node handle moved callback.
2792  */
2793 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2795    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2797    Inkscape::NodePath::NodeSide *me;
2798    Inkscape::NodePath::NodeSide *other;
2799     if (n->p.knot == knot) {
2800         me = &n->p;
2801         other = &n->n;
2802     } else if (n->n.knot == knot) {
2803         me = &n->n;
2804         other = &n->p;
2805     } else {
2806         me = NULL;
2807         other = NULL;
2808         g_assert_not_reached();
2809     }
2811     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
2812     Radial rme(me->pos - n->pos);
2813     Radial rother(other->pos - n->pos);
2814     Radial rnew(*p - n->pos);
2816     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
2817         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
2818         /* 0 interpreted as "no snapping". */
2820         // The closest PI/snaps angle, starting from zero.
2821         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
2822         if (me->origin.a == HUGE_VAL) {
2823             // ortho doesn't exist: original handle was zero length.
2824             rnew.a = a_snapped;
2825         } else {
2826             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
2827              * its opposite and perpendiculars). */
2828             double const a_ortho = me->origin.a + floor((rnew.a - me->origin.a)/(M_PI/2) + 0.5) * (M_PI/2);
2830             // Snap to the closest.
2831             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
2832                        ? a_snapped
2833                        : a_ortho );
2834         }
2835     }
2837     if (state & GDK_MOD1_MASK) {
2838         // lock handle length
2839         rnew.r = me->origin.r;
2840     }
2842     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
2843         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
2844         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
2845         rother.a += rnew.a - rme.a;
2846         other->pos = NR::Point(rother) + n->pos;
2847         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
2848         sp_knot_set_position(other->knot, &other->pos, 0);
2849     }
2851     me->pos = NR::Point(rnew) + n->pos;
2852     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
2854     // this is what sp_knot_set_position does, but without emitting the signal:
2855     // we cannot emit a "moved" signal because we're now processing it
2856     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
2858     knot->desktop->set_coordinate_status(me->pos);
2860     update_object(n->subpath->nodepath);
2862     /* status text */
2863     SPDesktop *desktop = n->subpath->nodepath->desktop;
2864     if (!desktop) return;
2865     SPEventContext *ec = desktop->event_context;
2866     if (!ec) return;
2867     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
2868     if (!mc) return;
2870     double degrees = 180 / M_PI * rnew.a;
2871     if (degrees > 180) degrees -= 360;
2872     if (degrees < -180) degrees += 360;
2873     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
2874         degrees = angle_to_compass (degrees);
2876     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
2878     mc->setF(Inkscape::NORMAL_MESSAGE,
2879          _("<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);
2881     g_string_free(length, TRUE);
2884 /**
2885  * Node handle event callback.
2886  */
2887 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2889     gboolean ret = FALSE;
2890     switch (event->type) {
2891         case GDK_KEY_PRESS:
2892             switch (get_group0_keyval (&event->key)) {
2893                 case GDK_space:
2894                     if (event->key.state & GDK_BUTTON1_MASK) {
2895                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2896                         stamp_repr(nodepath);
2897                         ret = TRUE;
2898                     }
2899                     break;
2900                 default:
2901                     break;
2902             }
2903             break;
2904         default:
2905             break;
2906     }
2908     return ret;
2911 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
2912                                  Radial &rme, Radial &rother, gboolean const both)
2914     rme.a += angle;
2915     if ( both
2916          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2917          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2918     {
2919         rother.a += angle;
2920     }
2923 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
2924                                         Radial &rme, Radial &rother, gboolean const both)
2926     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
2928     gdouble r;
2929     if ( both
2930          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2931          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2932     {
2933         r = MAX(rme.r, rother.r);
2934     } else {
2935         r = rme.r;
2936     }
2938     gdouble const weird_angle = atan2(norm_angle, r);
2939 /* Bulia says norm_angle is just the visible distance that the
2940  * object's end must travel on the screen.  Left as 'angle' for want of
2941  * a better name.*/
2943     rme.a += weird_angle;
2944     if ( both
2945          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2946          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2947     {
2948         rother.a += weird_angle;
2949     }
2952 /**
2953  * Rotate one node.
2954  */
2955 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
2957     Inkscape::NodePath::NodeSide *me, *other;
2958     bool both = false;
2960     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
2961     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
2963     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
2964         me = &(n->p);
2965         other = &(n->n);
2966     } else if (!n->p.other) {
2967         me = &(n->n);
2968         other = &(n->p);
2969     } else {
2970         if (which > 0) { // right handle
2971             if (xn > xp) {
2972                 me = &(n->n);
2973                 other = &(n->p);
2974             } else {
2975                 me = &(n->p);
2976                 other = &(n->n);
2977             }
2978         } else if (which < 0){ // left handle
2979             if (xn <= xp) {
2980                 me = &(n->n);
2981                 other = &(n->p);
2982             } else {
2983                 me = &(n->p);
2984                 other = &(n->n);
2985             }
2986         } else { // both handles
2987             me = &(n->n);
2988             other = &(n->p);
2989             both = true;
2990         }
2991     }
2993     Radial rme(me->pos - n->pos);
2994     Radial rother(other->pos - n->pos);
2996     if (screen) {
2997         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
2998     } else {
2999         node_rotate_one_internal (*n, angle, rme, rother, both);
3000     }
3002     me->pos = n->pos + NR::Point(rme);
3004     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3005         other->pos =  n->pos + NR::Point(rother);
3006     }
3008     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3009     // so here we just move all the knots without emitting move signals, for speed
3010     sp_node_update_handles(n, false);
3013 /**
3014  * Rotate selected nodes.
3015  */
3016 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3018     if (!nodepath || !nodepath->selected) return;
3020     if (g_list_length(nodepath->selected) == 1) {
3021        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3022         node_rotate_one (n, angle, which, screen);
3023     } else {
3024        // rotate as an object:
3026         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3027         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3028         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3029             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3030             box.expandTo (n->pos); // contain all selected nodes
3031         }
3033         gdouble rot;
3034         if (screen) {
3035             gdouble const zoom = nodepath->desktop->current_zoom();
3036             gdouble const zmove = angle / zoom;
3037             gdouble const r = NR::L2(box.max() - box.midpoint());
3038             rot = atan2(zmove, r);
3039         } else {
3040             rot = angle;
3041         }
3043         NR::Matrix t =
3044             NR::Matrix (NR::translate(-box.midpoint())) *
3045             NR::Matrix (NR::rotate(rot)) *
3046             NR::Matrix (NR::translate(box.midpoint()));
3048         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3049             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3050             n->pos *= t;
3051             n->n.pos *= t;
3052             n->p.pos *= t;
3053             sp_node_update_handles(n, false);
3054         }
3055     }
3057     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3060 /**
3061  * Scale one node.
3062  */
3063 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3065     bool both = false;
3066     Inkscape::NodePath::NodeSide *me, *other;
3068     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3069     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3071     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3072         me = &(n->p);
3073         other = &(n->n);
3074         n->code = NR_CURVETO;
3075     } else if (!n->p.other) {
3076         me = &(n->n);
3077         other = &(n->p);
3078         if (n->n.other)
3079             n->n.other->code = NR_CURVETO;
3080     } else {
3081         if (which > 0) { // right handle
3082             if (xn > xp) {
3083                 me = &(n->n);
3084                 other = &(n->p);
3085                 if (n->n.other)
3086                     n->n.other->code = NR_CURVETO;
3087             } else {
3088                 me = &(n->p);
3089                 other = &(n->n);
3090                 n->code = NR_CURVETO;
3091             }
3092         } else if (which < 0){ // left handle
3093             if (xn <= xp) {
3094                 me = &(n->n);
3095                 other = &(n->p);
3096                 if (n->n.other)
3097                     n->n.other->code = NR_CURVETO;
3098             } else {
3099                 me = &(n->p);
3100                 other = &(n->n);
3101                 n->code = NR_CURVETO;
3102             }
3103         } else { // both handles
3104             me = &(n->n);
3105             other = &(n->p);
3106             both = true;
3107             n->code = NR_CURVETO;
3108             if (n->n.other)
3109                 n->n.other->code = NR_CURVETO;
3110         }
3111     }
3113     Radial rme(me->pos - n->pos);
3114     Radial rother(other->pos - n->pos);
3116     rme.r += grow;
3117     if (rme.r < 0) rme.r = 0;
3118     if (rme.a == HUGE_VAL) {
3119         if (me->other) { // if direction is unknown, initialize it towards the next node
3120             Radial rme_next(me->other->pos - n->pos);
3121             rme.a = rme_next.a;
3122         } else { // if there's no next, initialize to 0
3123             rme.a = 0;
3124         }
3125     }
3126     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3127         rother.r += grow;
3128         if (rother.r < 0) rother.r = 0;
3129         if (rother.a == HUGE_VAL) {
3130             rother.a = rme.a + M_PI;
3131         }
3132     }
3134     me->pos = n->pos + NR::Point(rme);
3136     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3137         other->pos = n->pos + NR::Point(rother);
3138     }
3140     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3141     // so here we just move all the knots without emitting move signals, for speed
3142     sp_node_update_handles(n, false);
3145 /**
3146  * Scale selected nodes.
3147  */
3148 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3150     if (!nodepath || !nodepath->selected) return;
3152     if (g_list_length(nodepath->selected) == 1) {
3153         // scale handles of the single selected node
3154         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3155         node_scale_one (n, grow, which);
3156     } else {
3157         // scale nodes as an "object":
3159         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3160         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3161         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3162             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3163             box.expandTo (n->pos); // contain all selected nodes
3164         }
3166         double scale = (box.maxExtent() + grow)/box.maxExtent();
3168         NR::Matrix t =
3169             NR::Matrix (NR::translate(-box.midpoint())) *
3170             NR::Matrix (NR::scale(scale, scale)) *
3171             NR::Matrix (NR::translate(box.midpoint()));
3173         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3174             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3175             n->pos *= t;
3176             n->n.pos *= t;
3177             n->p.pos *= t;
3178             sp_node_update_handles(n, false);
3179         }
3180     }
3182     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3185 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3187     if (!nodepath) return;
3188     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3191 /**
3192  * Flip selected nodes horizontally/vertically.
3193  */
3194 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3196     if (!nodepath || !nodepath->selected) return;
3198     if (g_list_length(nodepath->selected) == 1) {
3199         // flip handles of the single selected node
3200         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3201         double temp = n->p.pos[axis];
3202         n->p.pos[axis] = n->n.pos[axis];
3203         n->n.pos[axis] = temp;
3204         sp_node_update_handles(n, false);
3205     } else {
3206         // scale nodes as an "object":
3208         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3209         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3210         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3211             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3212             box.expandTo (n->pos); // contain all selected nodes
3213         }
3215         NR::Matrix t =
3216             NR::Matrix (NR::translate(-box.midpoint())) *
3217             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3218             NR::Matrix (NR::translate(box.midpoint()));
3220         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3221             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3222             n->pos *= t;
3223             n->n.pos *= t;
3224             n->p.pos *= t;
3225             sp_node_update_handles(n, false);
3226         }
3227     }
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     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3250     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3251     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3253     return s;
3256 /**
3257  * Destroy nodes in subpath, then subpath itself.
3258  */
3259 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3261     g_assert(subpath);
3262     g_assert(subpath->nodepath);
3263     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3265     while (subpath->nodes) {
3266         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3267     }
3269     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3271     g_free(subpath);
3274 /**
3275  * Link head to tail in subpath.
3276  */
3277 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3279     g_assert(!sp->closed);
3280     g_assert(sp->last != sp->first);
3281     g_assert(sp->first->code == NR_MOVETO);
3283     sp->closed = TRUE;
3285     //Link the head to the tail
3286     sp->first->p.other = sp->last;
3287     sp->last->n.other  = sp->first;
3288     sp->last->n.pos    = sp->first->n.pos;
3289     sp->first          = sp->last;
3291     //Remove the extra end node
3292     sp_nodepath_node_destroy(sp->last->n.other);
3295 /**
3296  * Open closed (loopy) subpath at node.
3297  */
3298 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3300     g_assert(sp->closed);
3301     g_assert(n->subpath == sp);
3302     g_assert(sp->first == sp->last);
3304     /* We create new startpoint, current node will become last one */
3306    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3307                                                 &n->pos, &n->pos, &n->n.pos);
3310     sp->closed        = FALSE;
3312     //Unlink to make a head and tail
3313     sp->first         = new_path;
3314     sp->last          = n;
3315     n->n.other        = NULL;
3316     new_path->p.other = NULL;
3319 /**
3320  * Returns area in triangle given by points; may be negative.
3321  */
3322 inline double
3323 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3325     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]);
3328 /**
3329  * Return new node in subpath with given properties.
3330  * \param pos Position of node.
3331  * \param ppos Handle position in previous direction
3332  * \param npos Handle position in previous direction
3333  */
3334 Inkscape::NodePath::Node *
3335 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)
3337     g_assert(sp);
3338     g_assert(sp->nodepath);
3339     g_assert(sp->nodepath->desktop);
3341     if (nodechunk == NULL)
3342         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3344     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3346     n->subpath  = sp;
3348     if (type != Inkscape::NodePath::NODE_NONE) {
3349         // use the type from sodipodi:nodetypes
3350         n->type = type;
3351     } else {
3352         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3353             // points are (almost) collinear
3354             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3355                 // endnode, or a node with a retracted handle
3356                 n->type = Inkscape::NodePath::NODE_CUSP;
3357             } else {
3358                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3359             }
3360         } else {
3361             n->type = Inkscape::NodePath::NODE_CUSP;
3362         }
3363     }
3365     n->code     = code;
3366     n->selected = FALSE;
3367     n->pos      = *pos;
3368     n->p.pos    = *ppos;
3369     n->n.pos    = *npos;
3371     n->dragging_out = NULL;
3373     Inkscape::NodePath::Node *prev;
3374     if (next) {
3375         //g_assert(g_list_find(sp->nodes, next));
3376         prev = next->p.other;
3377     } else {
3378         prev = sp->last;
3379     }
3381     if (prev)
3382         prev->n.other = n;
3383     else
3384         sp->first = n;
3386     if (next)
3387         next->p.other = n;
3388     else
3389         sp->last = n;
3391     n->p.other = prev;
3392     n->n.other = next;
3394     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"));
3395     sp_knot_set_position(n->knot, pos, 0);
3397     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3398     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3399     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3400     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3401     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3402     sp_knot_update_ctrl(n->knot);
3404     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3405     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3406     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3407     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3408     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3409     sp_knot_show(n->knot);
3411     // We only create handle knots and lines on demand
3412     n->p.knot = NULL;
3413     n->p.line = NULL;
3414     n->n.knot = NULL;
3415     n->n.line = NULL;
3417     sp->nodes = g_list_prepend(sp->nodes, n);
3419     return n;
3422 /**
3423  * Destroy node and its knots, link neighbors in subpath.
3424  */
3425 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3427     g_assert(node);
3428     g_assert(node->subpath);
3429     g_assert(SP_IS_KNOT(node->knot));
3431    Inkscape::NodePath::SubPath *sp = node->subpath;
3433     if (node->selected) { // first, deselect
3434         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3435         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3436     }
3438     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3440     g_object_unref(G_OBJECT(node->knot));
3441     if (node->p.knot)
3442         g_object_unref(G_OBJECT(node->p.knot));
3443     if (node->n.knot)
3444         g_object_unref(G_OBJECT(node->n.knot));
3446     if (node->p.line)
3447         gtk_object_destroy(GTK_OBJECT(node->p.line));
3448     if (node->n.line)
3449         gtk_object_destroy(GTK_OBJECT(node->n.line));
3451     if (sp->nodes) { // there are others nodes on the subpath
3452         if (sp->closed) {
3453             if (sp->first == node) {
3454                 g_assert(sp->last == node);
3455                 sp->first = node->n.other;
3456                 sp->last = sp->first;
3457             }
3458             node->p.other->n.other = node->n.other;
3459             node->n.other->p.other = node->p.other;
3460         } else {
3461             if (sp->first == node) {
3462                 sp->first = node->n.other;
3463                 sp->first->code = NR_MOVETO;
3464             }
3465             if (sp->last == node) sp->last = node->p.other;
3466             if (node->p.other) node->p.other->n.other = node->n.other;
3467             if (node->n.other) node->n.other->p.other = node->p.other;
3468         }
3469     } else { // this was the last node on subpath
3470         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3471     }
3473     g_mem_chunk_free(nodechunk, node);
3476 /**
3477  * Returns one of the node's two sides.
3478  * \param which Indicates which side.
3479  * \return Pointer to previous node side if which==-1, next if which==1.
3480  */
3481 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3483     g_assert(node);
3485     switch (which) {
3486         case -1:
3487             return &node->p;
3488         case 1:
3489             return &node->n;
3490         default:
3491             break;
3492     }
3494     g_assert_not_reached();
3496     return NULL;
3499 /**
3500  * Return the other side of the node, given one of its sides.
3501  */
3502 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
3504     g_assert(node);
3506     if (me == &node->p) return &node->n;
3507     if (me == &node->n) return &node->p;
3509     g_assert_not_reached();
3511     return NULL;
3514 /**
3515  * Return NRPathcode on the given side of the node.
3516  */
3517 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3519     g_assert(node);
3521     if (me == &node->p) {
3522         if (node->p.other) return (NRPathcode)node->code;
3523         return NR_MOVETO;
3524     }
3526     if (me == &node->n) {
3527         if (node->n.other) return (NRPathcode)node->n.other->code;
3528         return NR_MOVETO;
3529     }
3531     g_assert_not_reached();
3533     return NR_END;
3536 /**
3537  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3538  */
3539 Inkscape::NodePath::Node *
3540 sp_nodepath_get_node_by_index(int index)
3542     Inkscape::NodePath::Node *e = NULL;
3544     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3545     if (!nodepath) {
3546         return e;
3547     }
3549     //find segment
3550     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3552         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3553         int n = g_list_length(sp->nodes);
3554         if (sp->closed) {
3555             n++;
3556         }
3558         //if the piece belongs to this subpath grab it
3559         //otherwise move onto the next subpath
3560         if (index < n) {
3561             e = sp->first;
3562             for (int i = 0; i < index; ++i) {
3563                 e = e->n.other;
3564             }
3565             break;
3566         } else {
3567             if (sp->closed) {
3568                 index -= (n+1);
3569             } else {
3570                 index -= n;
3571             }
3572         }
3573     }
3575     return e;
3578 /**
3579  * Returns plain text meaning of node type.
3580  */
3581 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3583     unsigned retracted = 0;
3584     bool endnode = false;
3586     for (int which = -1; which <= 1; which += 2) {
3587         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3588         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3589             retracted ++;
3590         if (!side->other)
3591             endnode = true;
3592     }
3594     if (retracted == 0) {
3595         if (endnode) {
3596                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3597                 return _("end node");
3598         } else {
3599             switch (node->type) {
3600                 case Inkscape::NodePath::NODE_CUSP:
3601                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3602                     return _("cusp");
3603                 case Inkscape::NodePath::NODE_SMOOTH:
3604                     // TRANSLATORS: "smooth" is an adjective here
3605                     return _("smooth");
3606                 case Inkscape::NodePath::NODE_SYMM:
3607                     return _("symmetric");
3608             }
3609         }
3610     } else if (retracted == 1) {
3611         if (endnode) {
3612             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3613             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3614         } else {
3615             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3616         }
3617     } else {
3618         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3619     }
3621     return NULL;
3624 /**
3625  * Handles content of statusbar as long as node tool is active.
3626  */
3627 void
3628 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3630     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>arrow</b> keys to move nodes");
3631     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3633     gint total = 0;
3634     gint selected = 0;
3635     SPDesktop *desktop = NULL;
3637     if (nodepath) {
3638         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3639             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3640             total += g_list_length(subpath->nodes);
3641         }
3642         selected = g_list_length(nodepath->selected);
3643         desktop = nodepath->desktop;
3644     } else {
3645         desktop = SP_ACTIVE_DESKTOP;
3646     }
3648     SPEventContext *ec = desktop->event_context;
3649     if (!ec) return;
3650     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3651     if (!mc) return;
3653     if (selected == 0) {
3654         Inkscape::Selection *sel = desktop->selection;
3655         if (!sel || sel->isEmpty()) {
3656             mc->setF(Inkscape::NORMAL_MESSAGE,
3657                      _("Select a single object to edit its nodes or handles."));
3658         } else {
3659             if (nodepath) {
3660             mc->setF(Inkscape::NORMAL_MESSAGE,
3661                      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.",
3662                               "<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.",
3663                               total),
3664                      total);
3665             } else {
3666                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3667                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3668                 } else {
3669                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3670                 }
3671             }
3672         }
3673     } else if (nodepath && selected == 1) {
3674         mc->setF(Inkscape::NORMAL_MESSAGE,
3675                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3676                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3677                           total),
3678                  selected, total, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3679     } else {
3680         mc->setF(Inkscape::NORMAL_MESSAGE,
3681                  ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
3682                           "<b>%i</b> of <b>%i</b> nodes selected. %s.",
3683                           total),
3684                  selected, total, when_selected);
3685     }
3689 /*
3690   Local Variables:
3691   mode:c++
3692   c-file-style:"stroustrup"
3693   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3694   indent-tabs-mode:nil
3695   fill-column:99
3696   End:
3697 */
3698 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :