Code

when switching node type, preserve the position of the mouseovered handle
[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     // if one of handles is mouseovered, preserve its position
884     if (SP_KNOT_IS_MOSEOVER(node->p.knot)) {
885         sp_node_adjust_handle(node, 1);
886     } else if (SP_KNOT_IS_MOSEOVER(node->n.knot)) {
887         sp_node_adjust_handle(node, -1);
888     } else {
889         sp_node_adjust_handles(node);
890     }
892     sp_node_update_handles(node);
894     sp_nodepath_update_statusbar(node->subpath->nodepath);
896     return node;
899 /**
900  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
901  * adjacent segments from lines to curves.
902 */
903 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
905     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
906         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
907             // convert adjacent segment BEFORE to curve
908             node->code = NR_CURVETO;
909             NR::Point delta;
910             if (node->n.other != NULL)
911                 delta = node->n.other->pos - node->p.other->pos;
912             else
913                 delta = node->pos - node->p.other->pos;
914             node->p.pos = node->pos - delta / 4;
915             sp_node_update_handles(node);
916         }
918         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
919             // convert adjacent segment AFTER to curve
920             node->n.other->code = NR_CURVETO;
921             NR::Point delta;
922             if (node->p.other != NULL)
923                 delta = node->p.other->pos - node->n.other->pos;
924             else
925                 delta = node->pos - node->n.other->pos;
926             node->n.pos = node->pos - delta / 4;
927             sp_node_update_handles(node);
928         }
929     }
931     sp_nodepath_set_node_type (node, type);
934 /**
935  * Move node to point, and adjust its and neighbouring handles.
936  */
937 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
939     NR::Point delta = p - node->pos;
940     node->pos = p;
942     node->p.pos += delta;
943     node->n.pos += delta;
945     if (node->p.other) {
946         if (node->code == NR_LINETO) {
947             sp_node_adjust_handle(node, 1);
948             sp_node_adjust_handle(node->p.other, -1);
949         }
950     }
951     if (node->n.other) {
952         if (node->n.other->code == NR_LINETO) {
953             sp_node_adjust_handle(node, -1);
954             sp_node_adjust_handle(node->n.other, 1);
955         }
956     }
958     // this function is only called from batch movers that will update display at the end
959     // themselves, so here we just move all the knots without emitting move signals, for speed
960     sp_node_update_handles(node, false);
963 /**
964  * Call sp_node_moveto() for node selection and handle possible snapping.
965  */
966 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
967                                             bool const snap = true)
969     NR::Coord best[2] = { NR_HUGE, NR_HUGE };
970     NR::Point delta(dx, dy);
971     NR::Point best_pt = delta;
973     if (snap) {
974         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
975            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
976             NR::Point p = n->pos + delta;
977             for (int dim = 0; dim < 2; dim++) {
978                 NR::Coord dist = namedview_dim_snap(nodepath->desktop->namedview,
979                                                     Inkscape::Snapper::SNAP_POINT, p,
980                                                     NR::Dim2(dim), nodepath->path);
981                 if (dist < best[dim]) {
982                     best[dim] = dist;
983                     best_pt[dim] = p[dim] - n->pos[dim];
984                 }
985             }
986         }
987     }
989     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
990        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
991         sp_node_moveto(n, n->pos + best_pt);
992     }
994     // do not update repr here so that node dragging is acceptably fast
995     update_object(nodepath);
998 /**
999  * Move node selection to point, adjust its and neighbouring handles,
1000  * handle possible snapping, and commit the change with possible undo.
1001  */
1002 void
1003 sp_node_selected_move(gdouble dx, gdouble dy)
1005     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1006     if (!nodepath) return;
1008     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1010     if (dx == 0) {
1011         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1012     } else if (dy == 0) {
1013         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1014     } else {
1015         sp_nodepath_update_repr(nodepath);
1016     }
1019 /**
1020  * Move node selection off screen and commit the change.
1021  */
1022 void
1023 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1025     // borrowed from sp_selection_move_screen in selection-chemistry.c
1026     // we find out the current zoom factor and divide deltas by it
1027     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1029     gdouble zoom = desktop->current_zoom();
1030     gdouble zdx = dx / zoom;
1031     gdouble zdy = dy / zoom;
1033     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1034     if (!nodepath) return;
1036     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1038     if (dx == 0) {
1039         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical");
1040     } else if (dy == 0) {
1041         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal");
1042     } else {
1043         sp_nodepath_update_repr(nodepath);
1044     }
1047 /** If they don't yet exist, creates knot and line for the given side of the node */
1048 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1050     if (!side->knot) {
1051         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"));
1053         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1054         side->knot->setSize (7);
1055         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1056         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1057         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1058         sp_knot_update_ctrl(side->knot);
1060         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1061         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1062         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1063         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1064         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1065         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1066     }
1068     if (!side->line) {
1069         side->line = sp_canvas_item_new(SP_DT_CONTROLS(desktop),
1070                                         SP_TYPE_CTRLLINE, NULL);
1071     }
1074 /**
1075  * Ensure the given handle of the node is visible/invisible, update its screen position
1076  */
1077 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1079     g_assert(node != NULL);
1081    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1082     NRPathcode code = sp_node_path_code_from_side(node, side);
1084     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1086     if (show_handle) {
1087         if (!side->knot) { // No handle knot at all
1088             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1089             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1090             side->knot->pos = side->pos;
1091             if (side->knot->item) 
1092                 SP_CTRL(side->knot->item)->moveto(side->pos);
1093             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1094             sp_knot_show(side->knot);
1095         } else {
1096             if (side->knot->pos != side->pos) { // only if it's really moved
1097                 if (fire_move_signals) {
1098                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1099                 } else {
1100                     sp_knot_moveto(side->knot, &side->pos);
1101                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1102                 }
1103             }
1104             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1105                 sp_knot_show(side->knot);
1106             }
1107         }
1108         sp_canvas_item_show(side->line);
1109     } else {
1110         if (side->knot) {
1111             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1112                 sp_knot_hide(side->knot);
1113             }
1114         }
1115         if (side->line) {
1116             sp_canvas_item_hide(side->line);
1117         }
1118     }
1121 /**
1122  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1123  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1124  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1125  * updated; otherwise, just move the knots silently (used in batch moves).
1126  */
1127 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1129     g_assert(node != NULL);
1131     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1132         sp_knot_show(node->knot);
1133     }
1135     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1136         if (fire_move_signals)
1137             sp_knot_set_position(node->knot, &node->pos, 0);
1138         else 
1139             sp_knot_moveto(node->knot, &node->pos);
1140     }
1142     gboolean show_handles = node->selected;
1143     if (node->p.other != NULL) {
1144         if (node->p.other->selected) show_handles = TRUE;
1145     }
1146     if (node->n.other != NULL) {
1147         if (node->n.other->selected) show_handles = TRUE;
1148     }
1150     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1151     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1154 /**
1155  * Call sp_node_update_handles() for all nodes on subpath.
1156  */
1157 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1159     g_assert(subpath != NULL);
1161     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1162         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1163     }
1166 /**
1167  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1168  */
1169 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1171     g_assert(nodepath != NULL);
1173     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1174         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1175     }
1178 /**
1179  * Adds all selected nodes in nodepath to list.
1180  */
1181 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1183     StlConv<Node *>::list(l, selected);
1184 /// \todo this adds a copying, rework when the selection becomes a stl list
1187 /**
1188  * Align selected nodes on the specified axis.
1189  */
1190 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1192     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1193         return;
1194     }
1196     if ( !nodepath->selected->next ) { // only one node selected
1197         return;
1198     }
1199    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1200     NR::Point dest(pNode->pos);
1201     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1202         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1203         if (pNode) {
1204             dest[axis] = pNode->pos[axis];
1205             sp_node_moveto(pNode, dest);
1206         }
1207     }
1209     sp_nodepath_update_repr(nodepath);
1212 /// Helper struct.
1213 struct NodeSort
1215    Inkscape::NodePath::Node *_node;
1216     NR::Coord _coord;
1217     /// \todo use vectorof pointers instead of calling copy ctor
1218     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1219         _node(node), _coord(node->pos[axis])
1220     {}
1222 };
1224 static bool operator<(NodeSort const &a, NodeSort const &b)
1226     return (a._coord < b._coord);
1229 /**
1230  * Distribute selected nodes on the specified axis.
1231  */
1232 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1234     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1235         return;
1236     }
1238     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1239         return;
1240     }
1242    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1243     std::vector<NodeSort> sorted;
1244     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1245         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1246         if (pNode) {
1247             NodeSort n(pNode, axis);
1248             sorted.push_back(n);
1249             //dest[axis] = pNode->pos[axis];
1250             //sp_node_moveto(pNode, dest);
1251         }
1252     }
1253     std::sort(sorted.begin(), sorted.end());
1254     unsigned int len = sorted.size();
1255     //overall bboxes span
1256     float dist = (sorted.back()._coord -
1257                   sorted.front()._coord);
1258     //new distance between each bbox
1259     float step = (dist) / (len - 1);
1260     float pos = sorted.front()._coord;
1261     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1262           it < sorted.end();
1263           it ++ )
1264     {
1265         NR::Point dest((*it)._node->pos);
1266         dest[axis] = pos;
1267         sp_node_moveto((*it)._node, dest);
1268         pos += step;
1269     }
1271     sp_nodepath_update_repr(nodepath);
1275 /**
1276  * Call sp_nodepath_line_add_node() for all selected segments.
1277  */
1278 void
1279 sp_node_selected_add_node(void)
1281     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1282     if (!nodepath) {
1283         return;
1284     }
1286     GList *nl = NULL;
1288     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1289        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1290         g_assert(t->selected);
1291         if (t->p.other && t->p.other->selected) {
1292             nl = g_list_prepend(nl, t);
1293         }
1294     }
1296     while (nl) {
1297        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1298        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1299         sp_nodepath_node_select(n, TRUE, FALSE);
1300         nl = g_list_remove(nl, t);
1301     }
1303     /** \todo fixme: adjust ? */
1304     sp_nodepath_update_handles(nodepath);
1306     sp_nodepath_update_repr(nodepath);
1308     sp_nodepath_update_statusbar(nodepath);
1311 /**
1312  * Select segment nearest to point
1313  */
1314 void
1315 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1317     if (!nodepath) {
1318         return;
1319     }
1321     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1323     //find segment to segment
1324     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1326     gboolean force = FALSE;
1327     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1328         force = TRUE;
1329     }
1330     sp_nodepath_node_select(e, (gboolean) toggle, force);
1331     if (e->p.other)
1332         sp_nodepath_node_select(e->p.other, TRUE, force);
1334     sp_nodepath_update_handles(nodepath);
1336     sp_nodepath_update_statusbar(nodepath);
1339 /**
1340  * Add a node nearest to point
1341  */
1342 void
1343 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1345     if (!nodepath) {
1346         return;
1347     }
1349     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1351     //find segment to split
1352     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1354     //don't know why but t seems to flip for lines
1355     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1356         position.t = 1.0 - position.t;
1357     }
1358     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1359     sp_nodepath_node_select(n, FALSE, TRUE);
1361     /* fixme: adjust ? */
1362     sp_nodepath_update_handles(nodepath);
1364     sp_nodepath_update_repr(nodepath);
1366     sp_nodepath_update_statusbar(nodepath);
1369 /*
1370  * Adjusts a segment so that t moves by a certain delta for dragging
1371  * converts lines to curves
1372  *
1373  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1374  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1375  */
1376 void
1377 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1379     /* feel good is an arbitrary parameter that distributes the delta between handles
1380      * if t of the drag point is less than 1/6 distance form the endpoint only
1381      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1382      */
1383     double feel_good;
1384     if (t <= 1.0 / 6.0)
1385         feel_good = 0;
1386     else if (t <= 0.5)
1387         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1388     else if (t <= 5.0 / 6.0)
1389         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1390     else
1391         feel_good = 1;
1393     //if we're dragging a line convert it to a curve
1394     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1395         sp_nodepath_set_line_type(e, NR_CURVETO);
1396     }
1398     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1399     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1400     e->p.other->n.pos += offsetcoord0;
1401     e->p.pos += offsetcoord1;
1403     // adjust handles of adjacent nodes where necessary
1404     sp_node_adjust_handle(e,1);
1405     sp_node_adjust_handle(e->p.other,-1);
1407     sp_nodepath_update_handles(e->subpath->nodepath);
1409     update_object(e->subpath->nodepath);
1411     sp_nodepath_update_statusbar(e->subpath->nodepath);
1415 /**
1416  * Call sp_nodepath_break() for all selected segments.
1417  */
1418 void sp_node_selected_break()
1420     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1421     if (!nodepath) return;
1423     GList *temp = NULL;
1424     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1425        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1426        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1427         if (nn == NULL) continue; // no break, no new node
1428         temp = g_list_prepend(temp, nn);
1429     }
1431     if (temp) {
1432         sp_nodepath_deselect(nodepath);
1433     }
1434     for (GList *l = temp; l != NULL; l = l->next) {
1435         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1436     }
1438     sp_nodepath_update_handles(nodepath);
1440     sp_nodepath_update_repr(nodepath);
1443 /**
1444  * Duplicate the selected node(s).
1445  */
1446 void sp_node_selected_duplicate()
1448     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1449     if (!nodepath) {
1450         return;
1451     }
1453     GList *temp = NULL;
1454     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1455        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1456        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1457         if (nn == NULL) continue; // could not duplicate
1458         temp = g_list_prepend(temp, nn);
1459     }
1461     if (temp) {
1462         sp_nodepath_deselect(nodepath);
1463     }
1464     for (GList *l = temp; l != NULL; l = l->next) {
1465         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1466     }
1468     sp_nodepath_update_handles(nodepath);
1470     sp_nodepath_update_repr(nodepath);
1473 /**
1474  *  Join two nodes by merging them into one.
1475  */
1476 void sp_node_selected_join()
1478     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1479     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1481     if (g_list_length(nodepath->selected) != 2) {
1482         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1483         return;
1484     }
1486    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1487    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1489     g_assert(a != b);
1490     g_assert(a->p.other || a->n.other);
1491     g_assert(b->p.other || b->n.other);
1493     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1494         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1495         return;
1496     }
1498     /* a and b are endpoints */
1500     NR::Point c = (a->pos + b->pos) / 2;
1502     if (a->subpath == b->subpath) {
1503        Inkscape::NodePath::SubPath *sp = a->subpath;
1504         sp_nodepath_subpath_close(sp);
1506         sp_nodepath_update_handles(sp->nodepath);
1508         sp_nodepath_update_repr(nodepath);
1510         return;
1511     }
1513     /* a and b are separate subpaths */
1514    Inkscape::NodePath::SubPath *sa = a->subpath;
1515    Inkscape::NodePath::SubPath *sb = b->subpath;
1516     NR::Point p;
1517    Inkscape::NodePath::Node *n;
1518     NRPathcode code;
1519     if (a == sa->first) {
1520         p = sa->first->n.pos;
1521         code = (NRPathcode)sa->first->n.other->code;
1522        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1523         n = sa->last;
1524         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1525         n = n->p.other;
1526         while (n) {
1527             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1528             n = n->p.other;
1529             if (n == sa->first) n = NULL;
1530         }
1531         sp_nodepath_subpath_destroy(sa);
1532         sa = t;
1533     } else if (a == sa->last) {
1534         p = sa->last->p.pos;
1535         code = (NRPathcode)sa->last->code;
1536         sp_nodepath_node_destroy(sa->last);
1537     } else {
1538         code = NR_END;
1539         g_assert_not_reached();
1540     }
1542     if (b == sb->first) {
1543         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1544         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1545             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1546         }
1547     } else if (b == sb->last) {
1548         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1549         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1550             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1551         }
1552     } else {
1553         g_assert_not_reached();
1554     }
1555     /* and now destroy sb */
1557     sp_nodepath_subpath_destroy(sb);
1559     sp_nodepath_update_handles(sa->nodepath);
1561     sp_nodepath_update_repr(nodepath);
1563     sp_nodepath_update_statusbar(nodepath);
1566 /**
1567  *  Join two nodes by adding a segment between them.
1568  */
1569 void sp_node_selected_join_segment()
1571     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1572     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1574     if (g_list_length(nodepath->selected) != 2) {
1575         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1576         return;
1577     }
1579    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1580    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1582     g_assert(a != b);
1583     g_assert(a->p.other || a->n.other);
1584     g_assert(b->p.other || b->n.other);
1586     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1587         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1588         return;
1589     }
1591     if (a->subpath == b->subpath) {
1592        Inkscape::NodePath::SubPath *sp = a->subpath;
1594         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1595         sp->closed = TRUE;
1597         sp->first->p.other = sp->last;
1598         sp->last->n.other  = sp->first;
1600         sp_node_handle_mirror_p_to_n(sp->last);
1601         sp_node_handle_mirror_n_to_p(sp->first);
1603         sp->first->code = sp->last->code;
1604         sp->first       = sp->last;
1606         sp_nodepath_update_handles(sp->nodepath);
1608         sp_nodepath_update_repr(nodepath);
1610         return;
1611     }
1613     /* a and b are separate subpaths */
1614    Inkscape::NodePath::SubPath *sa = a->subpath;
1615    Inkscape::NodePath::SubPath *sb = b->subpath;
1617    Inkscape::NodePath::Node *n;
1618     NR::Point p;
1619     NRPathcode code;
1620     if (a == sa->first) {
1621         code = (NRPathcode) sa->first->n.other->code;
1622        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1623         n = sa->last;
1624         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1625         for (n = n->p.other; n != NULL; n = n->p.other) {
1626             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1627         }
1628         sp_nodepath_subpath_destroy(sa);
1629         sa = t;
1630     } else if (a == sa->last) {
1631         code = (NRPathcode)sa->last->code;
1632     } else {
1633         code = NR_END;
1634         g_assert_not_reached();
1635     }
1637     if (b == sb->first) {
1638         n = sb->first;
1639         sp_node_handle_mirror_p_to_n(sa->last);
1640         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1641         sp_node_handle_mirror_n_to_p(sa->last);
1642         for (n = n->n.other; n != NULL; n = n->n.other) {
1643             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1644         }
1645     } else if (b == sb->last) {
1646         n = sb->last;
1647         sp_node_handle_mirror_p_to_n(sa->last);
1648         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1649         sp_node_handle_mirror_n_to_p(sa->last);
1650         for (n = n->p.other; n != NULL; n = n->p.other) {
1651             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1652         }
1653     } else {
1654         g_assert_not_reached();
1655     }
1656     /* and now destroy sb */
1658     sp_nodepath_subpath_destroy(sb);
1660     sp_nodepath_update_handles(sa->nodepath);
1662     sp_nodepath_update_repr(nodepath);
1665 /**
1666  * Delete one or more selected nodes.
1667  */
1668 void sp_node_selected_delete()
1670     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1671     if (!nodepath) return;
1672     if (!nodepath->selected) return;
1674     /** \todo fixme: do it the right way */
1675     while (nodepath->selected) {
1676        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1677         sp_nodepath_node_destroy(node);
1678     }
1681     //clean up the nodepath (such as for trivial subpaths)
1682     sp_nodepath_cleanup(nodepath);
1684     sp_nodepath_update_handles(nodepath);
1686     // if the entire nodepath is removed, delete the selected object.
1687     if (nodepath->subpaths == NULL ||
1688         sp_nodepath_get_node_count(nodepath) < 2) {
1689         SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
1690         sp_nodepath_destroy(nodepath);
1691         sp_selection_delete();
1692         sp_document_done (document);
1693         return;
1694     }
1696     sp_nodepath_update_repr(nodepath);
1698     sp_nodepath_update_statusbar(nodepath);
1701 /**
1702  * Delete one or more segments between two selected nodes.
1703  * This is the code for 'split'.
1704  */
1705 void
1706 sp_node_selected_delete_segment(void)
1708    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
1709    Inkscape::NodePath::Node *curr, *next;     //Iterators
1711     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1712     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1714     if (g_list_length(nodepath->selected) != 2) {
1715         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1716                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1717         return;
1718     }
1720     //Selected nodes, not inclusive
1721    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1722    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1724     if ( ( a==b)                       ||  //same node
1725          (a->subpath  != b->subpath )  ||  //not the same path
1726          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
1727          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
1728     {
1729         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1730                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1731         return;
1732     }
1734     //###########################################
1735     //# BEGIN EDITS
1736     //###########################################
1737     //##################################
1738     //# CLOSED PATH
1739     //##################################
1740     if (a->subpath->closed) {
1743         gboolean reversed = FALSE;
1745         //Since we can go in a circle, we need to find the shorter distance.
1746         //  a->b or b->a
1747         start = end = NULL;
1748         int distance    = 0;
1749         int minDistance = 0;
1750         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1751             if (curr==b) {
1752                 //printf("a to b:%d\n", distance);
1753                 start = a;//go from a to b
1754                 end   = b;
1755                 minDistance = distance;
1756                 //printf("A to B :\n");
1757                 break;
1758             }
1759             distance++;
1760         }
1762         //try again, the other direction
1763         distance = 0;
1764         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1765             if (curr==a) {
1766                 //printf("b to a:%d\n", distance);
1767                 if (distance < minDistance) {
1768                     start    = b;  //we go from b to a
1769                     end      = a;
1770                     reversed = TRUE;
1771                     //printf("B to A\n");
1772                 }
1773                 break;
1774             }
1775             distance++;
1776         }
1779         //Copy everything from 'end' to 'start' to a new subpath
1780        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1781         for (curr=end ; curr ; curr=curr->n.other) {
1782             NRPathcode code = (NRPathcode) curr->code;
1783             if (curr == end)
1784                 code = NR_MOVETO;
1785             sp_nodepath_node_new(t, NULL,
1786                                  (Inkscape::NodePath::NodeType)curr->type, code,
1787                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1788             if (curr == start)
1789                 break;
1790         }
1791         sp_nodepath_subpath_destroy(a->subpath);
1794     }
1798     //##################################
1799     //# OPEN PATH
1800     //##################################
1801     else {
1803         //We need to get the direction of the list between A and B
1804         //Can we walk from a to b?
1805         start = end = NULL;
1806         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1807             if (curr==b) {
1808                 start = a;  //did it!  we go from a to b
1809                 end   = b;
1810                 //printf("A to B\n");
1811                 break;
1812             }
1813         }
1814         if (!start) {//didn't work?  let's try the other direction
1815             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1816                 if (curr==a) {
1817                     start = b;  //did it!  we go from b to a
1818                     end   = a;
1819                     //printf("B to A\n");
1820                     break;
1821                 }
1822             }
1823         }
1824         if (!start) {
1825             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1826                                                      _("Cannot find path between nodes."));
1827             return;
1828         }
1832         //Copy everything after 'end' to a new subpath
1833        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1834         for (curr=end ; curr ; curr=curr->n.other) {
1835             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
1836                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1837         }
1839         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
1840         for (curr = start->n.other ; curr  ; curr=next) {
1841             next = curr->n.other;
1842             sp_nodepath_node_destroy(curr);
1843         }
1845     }
1846     //###########################################
1847     //# END EDITS
1848     //###########################################
1850     //clean up the nodepath (such as for trivial subpaths)
1851     sp_nodepath_cleanup(nodepath);
1853     sp_nodepath_update_handles(nodepath);
1855     sp_nodepath_update_repr(nodepath);
1857     // if the entire nodepath is removed, delete the selected object.
1858     if (nodepath->subpaths == NULL ||
1859         sp_nodepath_get_node_count(nodepath) < 2) {
1860         sp_nodepath_destroy(nodepath);
1861         sp_selection_delete();
1862         return;
1863     }
1865     sp_nodepath_update_statusbar(nodepath);
1868 /**
1869  * Call sp_nodepath_set_line() for all selected segments.
1870  */
1871 void
1872 sp_node_selected_set_line_type(NRPathcode code)
1874     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1875     if (nodepath == NULL) return;
1877     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1878        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1879         g_assert(n->selected);
1880         if (n->p.other && n->p.other->selected) {
1881             sp_nodepath_set_line_type(n, code);
1882         }
1883     }
1885     sp_nodepath_update_repr(nodepath);
1888 /**
1889  * Call sp_nodepath_convert_node_type() for all selected nodes.
1890  */
1891 void
1892 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
1894     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1895     if (nodepath == NULL) return;
1897     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1898         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
1899     }
1901     sp_nodepath_update_repr(nodepath);
1904 /**
1905  * Change select status of node, update its own and neighbour handles.
1906  */
1907 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
1909     node->selected = selected;
1911     if (selected) {
1912         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
1913         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
1914         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
1915         sp_knot_update_ctrl(node->knot);
1916     } else {
1917         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
1918         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
1919         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
1920         sp_knot_update_ctrl(node->knot);
1921     }
1923     sp_node_update_handles(node);
1924     if (node->n.other) sp_node_update_handles(node->n.other);
1925     if (node->p.other) sp_node_update_handles(node->p.other);
1928 /**
1929 \brief Select a node
1930 \param node     The node to select
1931 \param incremental   If true, add to selection, otherwise deselect others
1932 \param override   If true, always select this node, otherwise toggle selected status
1933 */
1934 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
1936     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
1938     if (incremental) {
1939         if (override) {
1940             if (!g_list_find(nodepath->selected, node)) {
1941                 nodepath->selected = g_list_prepend(nodepath->selected, node);
1942             }
1943             sp_node_set_selected(node, TRUE);
1944         } else { // toggle
1945             if (node->selected) {
1946                 g_assert(g_list_find(nodepath->selected, node));
1947                 nodepath->selected = g_list_remove(nodepath->selected, node);
1948             } else {
1949                 g_assert(!g_list_find(nodepath->selected, node));
1950                 nodepath->selected = g_list_prepend(nodepath->selected, node);
1951             }
1952             sp_node_set_selected(node, !node->selected);
1953         }
1954     } else {
1955         sp_nodepath_deselect(nodepath);
1956         nodepath->selected = g_list_prepend(nodepath->selected, node);
1957         sp_node_set_selected(node, TRUE);
1958     }
1960     sp_nodepath_update_statusbar(nodepath);
1964 /**
1965 \brief Deselect all nodes in the nodepath
1966 */
1967 void
1968 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
1970     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1972     while (nodepath->selected) {
1973         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
1974         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
1975     }
1976     sp_nodepath_update_statusbar(nodepath);
1979 /**
1980 \brief Select or invert selection of all nodes in the nodepath
1981 */
1982 void
1983 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
1985     if (!nodepath) return;
1987     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1988        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1989         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1990            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1991            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
1992         }
1993     }
1996 /**
1997  * If nothing selected, does the same as sp_nodepath_select_all();
1998  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
1999  * (i.e., similar to "select all in layer", with the "selected" subpaths
2000  * being treated as "layers" in the path).
2001  */
2002 void
2003 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2005     if (!nodepath) return;
2007     if (g_list_length (nodepath->selected) == 0) {
2008         sp_nodepath_select_all (nodepath, invert);
2009         return;
2010     }
2012     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2013     GSList *subpaths = NULL;
2015     for (GList *l = copy; l != NULL; l = l->next) {
2016         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2017         Inkscape::NodePath::SubPath *subpath = n->subpath;
2018         if (!g_slist_find (subpaths, subpath))
2019             subpaths = g_slist_prepend (subpaths, subpath);
2020     }
2022     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2023         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2024         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2025             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2026             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2027         }
2028     }
2030     g_slist_free (subpaths);
2031     g_list_free (copy);
2034 /**
2035  * \brief Select the node after the last selected; if none is selected,
2036  * select the first within path.
2037  */
2038 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2040     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2042    Inkscape::NodePath::Node *last = NULL;
2043     if (nodepath->selected) {
2044         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2045            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2046             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2047             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2048                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2049                 if (node->selected) {
2050                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2051                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2052                             if (spl->next) { // there's a next subpath
2053                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2054                                 last = subpath_next->first;
2055                             } else if (spl->prev) { // there's a previous subpath
2056                                 last = NULL; // to be set later to the first node of first subpath
2057                             } else {
2058                                 last = node->n.other;
2059                             }
2060                         } else {
2061                             last = node->n.other;
2062                         }
2063                     } else {
2064                         if (node->n.other) {
2065                             last = node->n.other;
2066                         } else {
2067                             if (spl->next) { // there's a next subpath
2068                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2069                                 last = subpath_next->first;
2070                             } else if (spl->prev) { // there's a previous subpath
2071                                 last = NULL; // to be set later to the first node of first subpath
2072                             } else {
2073                                 last = (Inkscape::NodePath::Node *) subpath->first;
2074                             }
2075                         }
2076                     }
2077                 }
2078             }
2079         }
2080         sp_nodepath_deselect(nodepath);
2081     }
2083     if (last) { // there's at least one more node after selected
2084         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2085     } else { // no more nodes, select the first one in first subpath
2086        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2087         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2088     }
2091 /**
2092  * \brief Select the node before the first selected; if none is selected,
2093  * select the last within path
2094  */
2095 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2097     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2099    Inkscape::NodePath::Node *last = NULL;
2100     if (nodepath->selected) {
2101         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2102            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2103             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2104                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2105                 if (node->selected) {
2106                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2107                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2108                             if (spl->prev) { // there's a prev subpath
2109                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2110                                 last = subpath_prev->last;
2111                             } else if (spl->next) { // there's a next subpath
2112                                 last = NULL; // to be set later to the last node of last subpath
2113                             } else {
2114                                 last = node->p.other;
2115                             }
2116                         } else {
2117                             last = node->p.other;
2118                         }
2119                     } else {
2120                         if (node->p.other) {
2121                             last = node->p.other;
2122                         } else {
2123                             if (spl->prev) { // there's a prev subpath
2124                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2125                                 last = subpath_prev->last;
2126                             } else if (spl->next) { // there's a next subpath
2127                                 last = NULL; // to be set later to the last node of last subpath
2128                             } else {
2129                                 last = (Inkscape::NodePath::Node *) subpath->last;
2130                             }
2131                         }
2132                     }
2133                 }
2134             }
2135         }
2136         sp_nodepath_deselect(nodepath);
2137     }
2139     if (last) { // there's at least one more node before selected
2140         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2141     } else { // no more nodes, select the last one in last subpath
2142         GList *spl = g_list_last(nodepath->subpaths);
2143        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2144         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2145     }
2148 /**
2149  * \brief Select all nodes that are within the rectangle.
2150  */
2151 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2153     if (!incremental) {
2154         sp_nodepath_deselect(nodepath);
2155     }
2157     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2158        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2159         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2160            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2162             if (b.contains(node->pos)) {
2163                 sp_nodepath_node_select(node, TRUE, TRUE);
2164             }
2165         }
2166     }
2169 /**
2170 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2171 */
2172 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2174     if (!nodepath->selected) {
2175         return NULL;
2176     }
2178     GList *r = NULL;
2179     guint i = 0;
2180     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2181        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2182         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2183            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2184             i++;
2185             if (node->selected) {
2186                 r = g_list_append(r, GINT_TO_POINTER(i));
2187             }
2188         }
2189     }
2190     return r;
2193 /**
2194 \brief  Restores selection by selecting nodes whose positions are in the list
2195 */
2196 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2198     sp_nodepath_deselect(nodepath);
2200     guint i = 0;
2201     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2202        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2203         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2204            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2205             i++;
2206             if (g_list_find(r, GINT_TO_POINTER(i))) {
2207                 sp_nodepath_node_select(node, TRUE, TRUE);
2208             }
2209         }
2210     }
2214 /**
2215 \brief Adjusts handle according to node type and line code.
2216 */
2217 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2219     double len, otherlen, linelen;
2221     g_assert(node);
2223    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2224    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2226     /** \todo fixme: */
2227     if (me->other == NULL) return;
2228     if (other->other == NULL) return;
2230     /* I have line */
2232     NRPathcode mecode, ocode;
2233     if (which_adjust == 1) {
2234         mecode = (NRPathcode)me->other->code;
2235         ocode = (NRPathcode)node->code;
2236     } else {
2237         mecode = (NRPathcode)node->code;
2238         ocode = (NRPathcode)other->other->code;
2239     }
2241     if (mecode == NR_LINETO) return;
2243     /* I am curve */
2245     if (other->other == NULL) return;
2247     /* Other has line */
2249     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2251     NR::Point delta;
2252     if (ocode == NR_LINETO) {
2253         /* other is lineto, we are either smooth or symm */
2254        Inkscape::NodePath::Node *othernode = other->other;
2255         len = NR::L2(me->pos - node->pos);
2256         delta = node->pos - othernode->pos;
2257         linelen = NR::L2(delta);
2258         if (linelen < 1e-18) 
2259             return;
2260         me->pos = node->pos + (len / linelen)*delta;
2261         return;
2262     }
2264     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2266         me->pos = 2 * node->pos - other->pos;
2267         return;
2268     }
2270     /* We are smooth */
2272     len = NR::L2(me->pos - node->pos);
2273     delta = other->pos - node->pos;
2274     otherlen = NR::L2(delta);
2275     if (otherlen < 1e-18) return;
2277     me->pos = node->pos - (len / otherlen) * delta;
2280 /**
2281  \brief Adjusts both handles according to node type and line code
2282  */
2283 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2285     g_assert(node);
2287     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2289     /* we are either smooth or symm */
2291     if (node->p.other == NULL) return;
2293     if (node->n.other == NULL) return;
2295     if (node->code == NR_LINETO) {
2296         if (node->n.other->code == NR_LINETO) return;
2297         sp_node_adjust_handle(node, 1);
2298         return;
2299     }
2301     if (node->n.other->code == NR_LINETO) {
2302         if (node->code == NR_LINETO) return;
2303         sp_node_adjust_handle(node, -1);
2304         return;
2305     }
2307     /* both are curves */
2308     NR::Point const delta( node->n.pos - node->p.pos );
2310     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2311         node->p.pos = node->pos - delta / 2;
2312         node->n.pos = node->pos + delta / 2;
2313         return;
2314     }
2316     /* We are smooth */
2317     double plen = NR::L2(node->p.pos - node->pos);
2318     if (plen < 1e-18) return;
2319     double nlen = NR::L2(node->n.pos - node->pos);
2320     if (nlen < 1e-18) return;
2321     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2322     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2325 /**
2326  * Node event callback.
2327  */
2328 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2330     gboolean ret = FALSE;
2331     switch (event->type) {
2332         case GDK_ENTER_NOTIFY:
2333             active_node = n;
2334             break;
2335         case GDK_LEAVE_NOTIFY:
2336             active_node = NULL;
2337             break;
2338         case GDK_KEY_PRESS:
2339             switch (get_group0_keyval (&event->key)) {
2340                 case GDK_space:
2341                     if (event->key.state & GDK_BUTTON1_MASK) {
2342                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2343                         stamp_repr(nodepath);
2344                         ret = TRUE;
2345                     }
2346                     break;
2347                 default:
2348                     break;
2349             }
2350             break;
2351         default:
2352             break;
2353     }
2355     return ret;
2358 /**
2359  * Handle keypress on node; directly called.
2360  */
2361 gboolean node_key(GdkEvent *event)
2363     Inkscape::NodePath::Path *np;
2365     // there is no way to verify nodes so set active_node to nil when deleting!!
2366     if (active_node == NULL) return FALSE;
2368     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2369         gint ret = FALSE;
2370         switch (get_group0_keyval (&event->key)) {
2371             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2372             case GDK_BackSpace:
2373                 np = active_node->subpath->nodepath;
2374                 sp_nodepath_node_destroy(active_node);
2375                 sp_nodepath_update_repr(np);
2376                 active_node = NULL;
2377                 ret = TRUE;
2378                 break;
2379             case GDK_c:
2380                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2381                 ret = TRUE;
2382                 break;
2383             case GDK_s:
2384                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2385                 ret = TRUE;
2386                 break;
2387             case GDK_y:
2388                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2389                 ret = TRUE;
2390                 break;
2391             case GDK_b:
2392                 sp_nodepath_node_break(active_node);
2393                 ret = TRUE;
2394                 break;
2395         }
2396         return ret;
2397     }
2398     return FALSE;
2401 /**
2402  * Mouseclick on node callback.
2403  */
2404 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2406    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2408     if (state & GDK_CONTROL_MASK) {
2409         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2411         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2412             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2413                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2414             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2415                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2416             } else {
2417                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2418             }
2419             sp_nodepath_update_repr(nodepath);
2420             sp_nodepath_update_statusbar(nodepath);
2422         } else { //ctrl+alt+click: delete node
2423             sp_nodepath_node_destroy(n);
2424             //clean up the nodepath (such as for trivial subpaths)
2425             sp_nodepath_cleanup(nodepath);
2427             // if the entire nodepath is removed, delete the selected object.
2428             if (nodepath->subpaths == NULL ||
2429                 sp_nodepath_get_node_count(nodepath) < 2) {
2430                 SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
2431                 sp_nodepath_destroy(nodepath);
2432                 sp_selection_delete();
2433                 sp_document_done (document);
2435             } else {
2436                 sp_nodepath_update_handles(nodepath);
2437                 sp_nodepath_update_repr(nodepath);
2438                 sp_nodepath_update_statusbar(nodepath);
2439             }
2440         }
2442     } else {
2443         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2444     }
2447 /**
2448  * Mouse grabbed node callback.
2449  */
2450 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2452    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2454     n->origin = knot->pos;
2456     if (!n->selected) {
2457         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2458     }
2461 /**
2462  * Mouse ungrabbed node callback.
2463  */
2464 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2466    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2468    n->dragging_out = NULL;
2470    sp_nodepath_update_repr(n->subpath->nodepath);
2473 /**
2474  * The point on a line, given by its angle, closest to the given point.
2475  * \param p  A point.
2476  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2477  * \param closest  Pointer to the point struct where the result is stored.
2478  * \todo FIXME: use dot product perhaps?
2479  */
2480 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2482     if (a == HUGE_VAL) { // vertical
2483         *closest = NR::Point(0, (*p)[NR::Y]);
2484     } else {
2485         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2486         (*closest)[NR::Y] = a * (*closest)[NR::X];
2487     }
2490 /**
2491  * Distance from the point to a line given by its angle.
2492  * \param p  A point.
2493  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2494  */
2495 static double point_line_distance(NR::Point *p, double a)
2497     NR::Point c;
2498     point_line_closest(p, a, &c);
2499     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]));
2502 /**
2503  * Callback for node "request" signal.
2504  * \todo fixme: This goes to "moved" event? (lauris)
2505  */
2506 static gboolean
2507 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2509     double yn, xn, yp, xp;
2510     double an, ap, na, pa;
2511     double d_an, d_ap, d_na, d_pa;
2512     gboolean collinear = FALSE;
2513     NR::Point c;
2514     NR::Point pr;
2516    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2518    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2519    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2521        NR::Point mouse = (*p);
2523        if (!n->dragging_out) {
2524            // This is the first drag-out event; find out which handle to drag out
2525            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2526            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2528            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2529                return FALSE;
2531            Inkscape::NodePath::NodeSide *opposite;
2532            if (appr_p > appr_n) { // closer to p
2533                n->dragging_out = &n->p;
2534                opposite = &n->n;
2535                n->code = NR_CURVETO;
2536            } else if (appr_p < appr_n) { // closer to n
2537                n->dragging_out = &n->n;
2538                opposite = &n->p;
2539                n->n.other->code = NR_CURVETO;
2540            } else { // p and n nodes are the same
2541                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2542                    n->dragging_out = &n->p;
2543                    opposite = &n->n;
2544                    n->code = NR_CURVETO;
2545                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2546                    n->dragging_out = &n->n;
2547                    opposite = &n->p;
2548                    n->n.other->code = NR_CURVETO;
2549                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2550                    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);
2551                    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);
2552                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2553                        n->dragging_out = &n->n;
2554                        opposite = &n->p;
2555                        n->n.other->code = NR_CURVETO;
2556                    } else { // closer to other's n handle
2557                        n->dragging_out = &n->p;
2558                        opposite = &n->n;
2559                        n->code = NR_CURVETO;
2560                    }
2561                }
2562            }
2564            // if there's another handle, make sure the one we drag out starts parallel to it
2565            if (opposite->pos != n->pos) {
2566                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2567            }
2569            // knots might not be created yet!
2570            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2571            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2572        }
2574        // pass this on to the handle-moved callback
2575        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2576        sp_node_update_handles(n);
2577        return TRUE;
2578    }
2580     if (state & GDK_CONTROL_MASK) { // constrained motion
2582         // calculate relative distances of handles
2583         // n handle:
2584         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2585         xn = n->n.pos[NR::X] - n->pos[NR::X];
2586         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2587         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2588             if (n->n.other) { // if there is the next point
2589                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2590                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2591                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2592             }
2593         }
2594         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2595         if (yn < 0) { xn = -xn; yn = -yn; }
2597         // p handle:
2598         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2599         xp = n->p.pos[NR::X] - n->pos[NR::X];
2600         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2601         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2602             if (n->p.other) {
2603                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2604                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2605                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2606             }
2607         }
2608         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2609         if (yp < 0) { xp = -xp; yp = -yp; }
2611         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2612             // sliding on handles, only if at least one of the handles is non-vertical
2613             // (otherwise it's the same as ctrl+drag anyway)
2615             // calculate angles of the handles
2616             if (xn == 0) {
2617                 if (yn == 0) { // no handle, consider it the continuation of the other one
2618                     an = 0;
2619                     collinear = TRUE;
2620                 }
2621                 else an = 0; // vertical; set the angle to horizontal
2622             } else an = yn/xn;
2624             if (xp == 0) {
2625                 if (yp == 0) { // no handle, consider it the continuation of the other one
2626                     ap = an;
2627                 }
2628                 else ap = 0; // vertical; set the angle to horizontal
2629             } else  ap = yp/xp;
2631             if (collinear) an = ap;
2633             // angles of the perpendiculars; HUGE_VAL means vertical
2634             if (an == 0) na = HUGE_VAL; else na = -1/an;
2635             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2637             //g_print("an %g    ap %g\n", an, ap);
2639             // mouse point relative to the node's original pos
2640             pr = (*p) - n->origin;
2642             // distances to the four lines (two handles and two perpendiculars)
2643             d_an = point_line_distance(&pr, an);
2644             d_na = point_line_distance(&pr, na);
2645             d_ap = point_line_distance(&pr, ap);
2646             d_pa = point_line_distance(&pr, pa);
2648             // find out which line is the closest, save its closest point in c
2649             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2650                 point_line_closest(&pr, an, &c);
2651             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2652                 point_line_closest(&pr, ap, &c);
2653             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2654                 point_line_closest(&pr, na, &c);
2655             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2656                 point_line_closest(&pr, pa, &c);
2657             }
2659             // move the node to the closest point
2660             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2661                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2662                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2664         } else {  // constraining to hor/vert
2666             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2667                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2668             } else { // snap to vert
2669                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2670             }
2671         }
2672     } else { // move freely
2673         sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2674                                         (*p)[NR::X] - n->pos[NR::X],
2675                                         (*p)[NR::Y] - n->pos[NR::Y],
2676                                         (state & GDK_SHIFT_MASK) == 0);
2677     }
2679     n->subpath->nodepath->desktop->scroll_to_point(p);
2681     return TRUE;
2684 /**
2685  * Node handle clicked callback.
2686  */
2687 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
2689    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2691     if (state & GDK_CONTROL_MASK) { // "delete" handle
2692         if (n->p.knot == knot) {
2693             n->p.pos = n->pos;
2694         } else if (n->n.knot == knot) {
2695             n->n.pos = n->pos;
2696         }
2697         sp_node_update_handles(n);
2698         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2699         sp_nodepath_update_repr(nodepath);
2700         sp_nodepath_update_statusbar(nodepath);
2702     } else { // just select or add to selection, depending in Shift
2703         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2704     }
2707 /**
2708  * Node handle grabbed callback.
2709  */
2710 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
2712    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2714     if (!n->selected) {
2715         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2716     }
2718     // remember the origin point of the handle
2719     if (n->p.knot == knot) {
2720         n->p.origin = n->p.pos - n->pos;
2721     } else if (n->n.knot == knot) {
2722         n->n.origin = n->n.pos - n->pos;
2723     } else {
2724         g_assert_not_reached();
2725     }
2729 /**
2730  * Node handle ungrabbed callback.
2731  */
2732 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
2734    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2736     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
2737     if (n->p.knot == knot) {
2738         n->p.origin.a = 0;
2739         sp_knot_set_position(knot, &n->p.pos, state);
2740     } else if (n->n.knot == knot) {
2741         n->n.origin.a = 0;
2742         sp_knot_set_position(knot, &n->n.pos, state);
2743     } else {
2744         g_assert_not_reached();
2745     }
2747     sp_nodepath_update_repr(n->subpath->nodepath);
2750 /**
2751  * Node handle "request" signal callback.
2752  */
2753 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2755     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2757     Inkscape::NodePath::NodeSide *me, *opposite;
2758     gint which;
2759     if (n->p.knot == knot) {
2760         me = &n->p;
2761         opposite = &n->n;
2762         which = -1;
2763     } else if (n->n.knot == knot) {
2764         me = &n->n;
2765         opposite = &n->p;
2766         which = 1;
2767     } else {
2768         me = opposite = NULL;
2769         which = 0;
2770         g_assert_not_reached();
2771     }
2773     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
2775     SnapManager const m(n->subpath->nodepath->desktop->namedview);
2777     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
2778         /* We are smooth node adjacent with line */
2779         NR::Point const delta = *p - n->pos;
2780         NR::Coord const len = NR::L2(delta);
2781         Inkscape::NodePath::Node *othernode = opposite->other;
2782         NR::Point const ndelta = n->pos - othernode->pos;
2783         NR::Coord const linelen = NR::L2(ndelta);
2784         if (len > NR_EPSILON && linelen > NR_EPSILON) {
2785             NR::Coord const scal = dot(delta, ndelta) / linelen;
2786             (*p) = n->pos + (scal / linelen) * ndelta;
2787         }
2788         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, ndelta, NULL).getPoint();
2789     } else {
2790         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
2791     }
2793     sp_node_adjust_handle(n, -which);
2795     return FALSE;
2798 /**
2799  * Node handle moved callback.
2800  */
2801 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2803    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2805    Inkscape::NodePath::NodeSide *me;
2806    Inkscape::NodePath::NodeSide *other;
2807     if (n->p.knot == knot) {
2808         me = &n->p;
2809         other = &n->n;
2810     } else if (n->n.knot == knot) {
2811         me = &n->n;
2812         other = &n->p;
2813     } else {
2814         me = NULL;
2815         other = NULL;
2816         g_assert_not_reached();
2817     }
2819     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
2820     Radial rme(me->pos - n->pos);
2821     Radial rother(other->pos - n->pos);
2822     Radial rnew(*p - n->pos);
2824     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
2825         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
2826         /* 0 interpreted as "no snapping". */
2828         // The closest PI/snaps angle, starting from zero.
2829         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
2830         if (me->origin.a == HUGE_VAL) {
2831             // ortho doesn't exist: original handle was zero length.
2832             rnew.a = a_snapped;
2833         } else {
2834             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
2835              * its opposite and perpendiculars). */
2836             double const a_ortho = me->origin.a + floor((rnew.a - me->origin.a)/(M_PI/2) + 0.5) * (M_PI/2);
2838             // Snap to the closest.
2839             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
2840                        ? a_snapped
2841                        : a_ortho );
2842         }
2843     }
2845     if (state & GDK_MOD1_MASK) {
2846         // lock handle length
2847         rnew.r = me->origin.r;
2848     }
2850     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
2851         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
2852         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
2853         rother.a += rnew.a - rme.a;
2854         other->pos = NR::Point(rother) + n->pos;
2855         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
2856         sp_knot_set_position(other->knot, &other->pos, 0);
2857     }
2859     me->pos = NR::Point(rnew) + n->pos;
2860     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
2862     // this is what sp_knot_set_position does, but without emitting the signal:
2863     // we cannot emit a "moved" signal because we're now processing it
2864     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
2866     knot->desktop->set_coordinate_status(me->pos);
2868     update_object(n->subpath->nodepath);
2870     /* status text */
2871     SPDesktop *desktop = n->subpath->nodepath->desktop;
2872     if (!desktop) return;
2873     SPEventContext *ec = desktop->event_context;
2874     if (!ec) return;
2875     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
2876     if (!mc) return;
2878     double degrees = 180 / M_PI * rnew.a;
2879     if (degrees > 180) degrees -= 360;
2880     if (degrees < -180) degrees += 360;
2881     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
2882         degrees = angle_to_compass (degrees);
2884     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
2886     mc->setF(Inkscape::NORMAL_MESSAGE,
2887          _("<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);
2889     g_string_free(length, TRUE);
2892 /**
2893  * Node handle event callback.
2894  */
2895 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2897     gboolean ret = FALSE;
2898     switch (event->type) {
2899         case GDK_KEY_PRESS:
2900             switch (get_group0_keyval (&event->key)) {
2901                 case GDK_space:
2902                     if (event->key.state & GDK_BUTTON1_MASK) {
2903                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2904                         stamp_repr(nodepath);
2905                         ret = TRUE;
2906                     }
2907                     break;
2908                 default:
2909                     break;
2910             }
2911             break;
2912         default:
2913             break;
2914     }
2916     return ret;
2919 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
2920                                  Radial &rme, Radial &rother, gboolean const both)
2922     rme.a += angle;
2923     if ( both
2924          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2925          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2926     {
2927         rother.a += angle;
2928     }
2931 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
2932                                         Radial &rme, Radial &rother, gboolean const both)
2934     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
2936     gdouble r;
2937     if ( both
2938          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2939          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2940     {
2941         r = MAX(rme.r, rother.r);
2942     } else {
2943         r = rme.r;
2944     }
2946     gdouble const weird_angle = atan2(norm_angle, r);
2947 /* Bulia says norm_angle is just the visible distance that the
2948  * object's end must travel on the screen.  Left as 'angle' for want of
2949  * a better name.*/
2951     rme.a += weird_angle;
2952     if ( both
2953          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2954          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2955     {
2956         rother.a += weird_angle;
2957     }
2960 /**
2961  * Rotate one node.
2962  */
2963 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
2965     Inkscape::NodePath::NodeSide *me, *other;
2966     bool both = false;
2968     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
2969     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
2971     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
2972         me = &(n->p);
2973         other = &(n->n);
2974     } else if (!n->p.other) {
2975         me = &(n->n);
2976         other = &(n->p);
2977     } else {
2978         if (which > 0) { // right 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 if (which < 0){ // left handle
2987             if (xn <= xp) {
2988                 me = &(n->n);
2989                 other = &(n->p);
2990             } else {
2991                 me = &(n->p);
2992                 other = &(n->n);
2993             }
2994         } else { // both handles
2995             me = &(n->n);
2996             other = &(n->p);
2997             both = true;
2998         }
2999     }
3001     Radial rme(me->pos - n->pos);
3002     Radial rother(other->pos - n->pos);
3004     if (screen) {
3005         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3006     } else {
3007         node_rotate_one_internal (*n, angle, rme, rother, both);
3008     }
3010     me->pos = n->pos + NR::Point(rme);
3012     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3013         other->pos =  n->pos + NR::Point(rother);
3014     }
3016     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3017     // so here we just move all the knots without emitting move signals, for speed
3018     sp_node_update_handles(n, false);
3021 /**
3022  * Rotate selected nodes.
3023  */
3024 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3026     if (!nodepath || !nodepath->selected) return;
3028     if (g_list_length(nodepath->selected) == 1) {
3029        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3030         node_rotate_one (n, angle, which, screen);
3031     } else {
3032        // rotate as an object:
3034         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3035         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3036         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3037             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3038             box.expandTo (n->pos); // contain all selected nodes
3039         }
3041         gdouble rot;
3042         if (screen) {
3043             gdouble const zoom = nodepath->desktop->current_zoom();
3044             gdouble const zmove = angle / zoom;
3045             gdouble const r = NR::L2(box.max() - box.midpoint());
3046             rot = atan2(zmove, r);
3047         } else {
3048             rot = angle;
3049         }
3051         NR::Matrix t =
3052             NR::Matrix (NR::translate(-box.midpoint())) *
3053             NR::Matrix (NR::rotate(rot)) *
3054             NR::Matrix (NR::translate(box.midpoint()));
3056         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3057             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3058             n->pos *= t;
3059             n->n.pos *= t;
3060             n->p.pos *= t;
3061             sp_node_update_handles(n, false);
3062         }
3063     }
3065     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n");
3068 /**
3069  * Scale one node.
3070  */
3071 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3073     bool both = false;
3074     Inkscape::NodePath::NodeSide *me, *other;
3076     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3077     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3079     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3080         me = &(n->p);
3081         other = &(n->n);
3082         n->code = NR_CURVETO;
3083     } else if (!n->p.other) {
3084         me = &(n->n);
3085         other = &(n->p);
3086         if (n->n.other)
3087             n->n.other->code = NR_CURVETO;
3088     } else {
3089         if (which > 0) { // right handle
3090             if (xn > xp) {
3091                 me = &(n->n);
3092                 other = &(n->p);
3093                 if (n->n.other)
3094                     n->n.other->code = NR_CURVETO;
3095             } else {
3096                 me = &(n->p);
3097                 other = &(n->n);
3098                 n->code = NR_CURVETO;
3099             }
3100         } else if (which < 0){ // left handle
3101             if (xn <= xp) {
3102                 me = &(n->n);
3103                 other = &(n->p);
3104                 if (n->n.other)
3105                     n->n.other->code = NR_CURVETO;
3106             } else {
3107                 me = &(n->p);
3108                 other = &(n->n);
3109                 n->code = NR_CURVETO;
3110             }
3111         } else { // both handles
3112             me = &(n->n);
3113             other = &(n->p);
3114             both = true;
3115             n->code = NR_CURVETO;
3116             if (n->n.other)
3117                 n->n.other->code = NR_CURVETO;
3118         }
3119     }
3121     Radial rme(me->pos - n->pos);
3122     Radial rother(other->pos - n->pos);
3124     rme.r += grow;
3125     if (rme.r < 0) rme.r = 0;
3126     if (rme.a == HUGE_VAL) {
3127         if (me->other) { // if direction is unknown, initialize it towards the next node
3128             Radial rme_next(me->other->pos - n->pos);
3129             rme.a = rme_next.a;
3130         } else { // if there's no next, initialize to 0
3131             rme.a = 0;
3132         }
3133     }
3134     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3135         rother.r += grow;
3136         if (rother.r < 0) rother.r = 0;
3137         if (rother.a == HUGE_VAL) {
3138             rother.a = rme.a + M_PI;
3139         }
3140     }
3142     me->pos = n->pos + NR::Point(rme);
3144     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3145         other->pos = n->pos + NR::Point(rother);
3146     }
3148     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3149     // so here we just move all the knots without emitting move signals, for speed
3150     sp_node_update_handles(n, false);
3153 /**
3154  * Scale selected nodes.
3155  */
3156 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3158     if (!nodepath || !nodepath->selected) return;
3160     if (g_list_length(nodepath->selected) == 1) {
3161         // scale handles of the single selected node
3162         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3163         node_scale_one (n, grow, which);
3164     } else {
3165         // scale nodes as an "object":
3167         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3168         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3169         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3170             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3171             box.expandTo (n->pos); // contain all selected nodes
3172         }
3174         double scale = (box.maxExtent() + grow)/box.maxExtent();
3176         NR::Matrix t =
3177             NR::Matrix (NR::translate(-box.midpoint())) *
3178             NR::Matrix (NR::scale(scale, scale)) *
3179             NR::Matrix (NR::translate(box.midpoint()));
3181         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3182             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3183             n->pos *= t;
3184             n->n.pos *= t;
3185             n->p.pos *= t;
3186             sp_node_update_handles(n, false);
3187         }
3188     }
3190     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n");
3193 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3195     if (!nodepath) return;
3196     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3199 /**
3200  * Flip selected nodes horizontally/vertically.
3201  */
3202 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3204     if (!nodepath || !nodepath->selected) return;
3206     if (g_list_length(nodepath->selected) == 1) {
3207         // flip handles of the single selected node
3208         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3209         double temp = n->p.pos[axis];
3210         n->p.pos[axis] = n->n.pos[axis];
3211         n->n.pos[axis] = temp;
3212         sp_node_update_handles(n, false);
3213     } else {
3214         // scale nodes as an "object":
3216         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3217         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3218         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3219             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3220             box.expandTo (n->pos); // contain all selected nodes
3221         }
3223         NR::Matrix t =
3224             NR::Matrix (NR::translate(-box.midpoint())) *
3225             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3226             NR::Matrix (NR::translate(box.midpoint()));
3228         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3229             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3230             n->pos *= t;
3231             n->n.pos *= t;
3232             n->p.pos *= t;
3233             sp_node_update_handles(n, false);
3234         }
3235     }
3237     sp_nodepath_update_repr(nodepath);
3240 //-----------------------------------------------
3241 /**
3242  * Return new subpath under given nodepath.
3243  */
3244 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3246     g_assert(nodepath);
3247     g_assert(nodepath->desktop);
3249    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3251     s->nodepath = nodepath;
3252     s->closed = FALSE;
3253     s->nodes = NULL;
3254     s->first = NULL;
3255     s->last = NULL;
3257     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3258     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3259     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3261     return s;
3264 /**
3265  * Destroy nodes in subpath, then subpath itself.
3266  */
3267 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3269     g_assert(subpath);
3270     g_assert(subpath->nodepath);
3271     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3273     while (subpath->nodes) {
3274         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3275     }
3277     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3279     g_free(subpath);
3282 /**
3283  * Link head to tail in subpath.
3284  */
3285 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3287     g_assert(!sp->closed);
3288     g_assert(sp->last != sp->first);
3289     g_assert(sp->first->code == NR_MOVETO);
3291     sp->closed = TRUE;
3293     //Link the head to the tail
3294     sp->first->p.other = sp->last;
3295     sp->last->n.other  = sp->first;
3296     sp->last->n.pos    = sp->first->n.pos;
3297     sp->first          = sp->last;
3299     //Remove the extra end node
3300     sp_nodepath_node_destroy(sp->last->n.other);
3303 /**
3304  * Open closed (loopy) subpath at node.
3305  */
3306 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3308     g_assert(sp->closed);
3309     g_assert(n->subpath == sp);
3310     g_assert(sp->first == sp->last);
3312     /* We create new startpoint, current node will become last one */
3314    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3315                                                 &n->pos, &n->pos, &n->n.pos);
3318     sp->closed        = FALSE;
3320     //Unlink to make a head and tail
3321     sp->first         = new_path;
3322     sp->last          = n;
3323     n->n.other        = NULL;
3324     new_path->p.other = NULL;
3327 /**
3328  * Returns area in triangle given by points; may be negative.
3329  */
3330 inline double
3331 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3333     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]);
3336 /**
3337  * Return new node in subpath with given properties.
3338  * \param pos Position of node.
3339  * \param ppos Handle position in previous direction
3340  * \param npos Handle position in previous direction
3341  */
3342 Inkscape::NodePath::Node *
3343 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)
3345     g_assert(sp);
3346     g_assert(sp->nodepath);
3347     g_assert(sp->nodepath->desktop);
3349     if (nodechunk == NULL)
3350         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3352     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3354     n->subpath  = sp;
3356     if (type != Inkscape::NodePath::NODE_NONE) {
3357         // use the type from sodipodi:nodetypes
3358         n->type = type;
3359     } else {
3360         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3361             // points are (almost) collinear
3362             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3363                 // endnode, or a node with a retracted handle
3364                 n->type = Inkscape::NodePath::NODE_CUSP;
3365             } else {
3366                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3367             }
3368         } else {
3369             n->type = Inkscape::NodePath::NODE_CUSP;
3370         }
3371     }
3373     n->code     = code;
3374     n->selected = FALSE;
3375     n->pos      = *pos;
3376     n->p.pos    = *ppos;
3377     n->n.pos    = *npos;
3379     n->dragging_out = NULL;
3381     Inkscape::NodePath::Node *prev;
3382     if (next) {
3383         //g_assert(g_list_find(sp->nodes, next));
3384         prev = next->p.other;
3385     } else {
3386         prev = sp->last;
3387     }
3389     if (prev)
3390         prev->n.other = n;
3391     else
3392         sp->first = n;
3394     if (next)
3395         next->p.other = n;
3396     else
3397         sp->last = n;
3399     n->p.other = prev;
3400     n->n.other = next;
3402     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"));
3403     sp_knot_set_position(n->knot, pos, 0);
3405     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3406     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3407     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3408     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3409     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3410     sp_knot_update_ctrl(n->knot);
3412     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3413     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3414     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3415     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3416     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3417     sp_knot_show(n->knot);
3419     // We only create handle knots and lines on demand
3420     n->p.knot = NULL;
3421     n->p.line = NULL;
3422     n->n.knot = NULL;
3423     n->n.line = NULL;
3425     sp->nodes = g_list_prepend(sp->nodes, n);
3427     return n;
3430 /**
3431  * Destroy node and its knots, link neighbors in subpath.
3432  */
3433 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3435     g_assert(node);
3436     g_assert(node->subpath);
3437     g_assert(SP_IS_KNOT(node->knot));
3439    Inkscape::NodePath::SubPath *sp = node->subpath;
3441     if (node->selected) { // first, deselect
3442         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3443         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3444     }
3446     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3448     g_object_unref(G_OBJECT(node->knot));
3449     if (node->p.knot)
3450         g_object_unref(G_OBJECT(node->p.knot));
3451     if (node->n.knot)
3452         g_object_unref(G_OBJECT(node->n.knot));
3454     if (node->p.line)
3455         gtk_object_destroy(GTK_OBJECT(node->p.line));
3456     if (node->n.line)
3457         gtk_object_destroy(GTK_OBJECT(node->n.line));
3459     if (sp->nodes) { // there are others nodes on the subpath
3460         if (sp->closed) {
3461             if (sp->first == node) {
3462                 g_assert(sp->last == node);
3463                 sp->first = node->n.other;
3464                 sp->last = sp->first;
3465             }
3466             node->p.other->n.other = node->n.other;
3467             node->n.other->p.other = node->p.other;
3468         } else {
3469             if (sp->first == node) {
3470                 sp->first = node->n.other;
3471                 sp->first->code = NR_MOVETO;
3472             }
3473             if (sp->last == node) sp->last = node->p.other;
3474             if (node->p.other) node->p.other->n.other = node->n.other;
3475             if (node->n.other) node->n.other->p.other = node->p.other;
3476         }
3477     } else { // this was the last node on subpath
3478         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3479     }
3481     g_mem_chunk_free(nodechunk, node);
3484 /**
3485  * Returns one of the node's two sides.
3486  * \param which Indicates which side.
3487  * \return Pointer to previous node side if which==-1, next if which==1.
3488  */
3489 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3491     g_assert(node);
3493     switch (which) {
3494         case -1:
3495             return &node->p;
3496         case 1:
3497             return &node->n;
3498         default:
3499             break;
3500     }
3502     g_assert_not_reached();
3504     return NULL;
3507 /**
3508  * Return the other side of the node, given one of its sides.
3509  */
3510 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
3512     g_assert(node);
3514     if (me == &node->p) return &node->n;
3515     if (me == &node->n) return &node->p;
3517     g_assert_not_reached();
3519     return NULL;
3522 /**
3523  * Return NRPathcode on the given side of the node.
3524  */
3525 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3527     g_assert(node);
3529     if (me == &node->p) {
3530         if (node->p.other) return (NRPathcode)node->code;
3531         return NR_MOVETO;
3532     }
3534     if (me == &node->n) {
3535         if (node->n.other) return (NRPathcode)node->n.other->code;
3536         return NR_MOVETO;
3537     }
3539     g_assert_not_reached();
3541     return NR_END;
3544 /**
3545  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3546  */
3547 Inkscape::NodePath::Node *
3548 sp_nodepath_get_node_by_index(int index)
3550     Inkscape::NodePath::Node *e = NULL;
3552     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3553     if (!nodepath) {
3554         return e;
3555     }
3557     //find segment
3558     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3560         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3561         int n = g_list_length(sp->nodes);
3562         if (sp->closed) {
3563             n++;
3564         }
3566         //if the piece belongs to this subpath grab it
3567         //otherwise move onto the next subpath
3568         if (index < n) {
3569             e = sp->first;
3570             for (int i = 0; i < index; ++i) {
3571                 e = e->n.other;
3572             }
3573             break;
3574         } else {
3575             if (sp->closed) {
3576                 index -= (n+1);
3577             } else {
3578                 index -= n;
3579             }
3580         }
3581     }
3583     return e;
3586 /**
3587  * Returns plain text meaning of node type.
3588  */
3589 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3591     unsigned retracted = 0;
3592     bool endnode = false;
3594     for (int which = -1; which <= 1; which += 2) {
3595         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3596         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3597             retracted ++;
3598         if (!side->other)
3599             endnode = true;
3600     }
3602     if (retracted == 0) {
3603         if (endnode) {
3604                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3605                 return _("end node");
3606         } else {
3607             switch (node->type) {
3608                 case Inkscape::NodePath::NODE_CUSP:
3609                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3610                     return _("cusp");
3611                 case Inkscape::NodePath::NODE_SMOOTH:
3612                     // TRANSLATORS: "smooth" is an adjective here
3613                     return _("smooth");
3614                 case Inkscape::NodePath::NODE_SYMM:
3615                     return _("symmetric");
3616             }
3617         }
3618     } else if (retracted == 1) {
3619         if (endnode) {
3620             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3621             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3622         } else {
3623             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3624         }
3625     } else {
3626         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3627     }
3629     return NULL;
3632 /**
3633  * Handles content of statusbar as long as node tool is active.
3634  */
3635 void
3636 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3638     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>arrow</b> keys to move nodes");
3639     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3641     gint total = 0;
3642     gint selected = 0;
3643     SPDesktop *desktop = NULL;
3645     if (nodepath) {
3646         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3647             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3648             total += g_list_length(subpath->nodes);
3649         }
3650         selected = g_list_length(nodepath->selected);
3651         desktop = nodepath->desktop;
3652     } else {
3653         desktop = SP_ACTIVE_DESKTOP;
3654     }
3656     SPEventContext *ec = desktop->event_context;
3657     if (!ec) return;
3658     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3659     if (!mc) return;
3661     if (selected == 0) {
3662         Inkscape::Selection *sel = desktop->selection;
3663         if (!sel || sel->isEmpty()) {
3664             mc->setF(Inkscape::NORMAL_MESSAGE,
3665                      _("Select a single object to edit its nodes or handles."));
3666         } else {
3667             if (nodepath) {
3668             mc->setF(Inkscape::NORMAL_MESSAGE,
3669                      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.",
3670                               "<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.",
3671                               total),
3672                      total);
3673             } else {
3674                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3675                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3676                 } else {
3677                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3678                 }
3679             }
3680         }
3681     } else if (nodepath && selected == 1) {
3682         mc->setF(Inkscape::NORMAL_MESSAGE,
3683                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3684                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3685                           total),
3686                  selected, total, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3687     } else {
3688         mc->setF(Inkscape::NORMAL_MESSAGE,
3689                  ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
3690                           "<b>%i</b> of <b>%i</b> nodes selected. %s.",
3691                           total),
3692                  selected, total, when_selected);
3693     }
3697 /*
3698   Local Variables:
3699   mode:c++
3700   c-file-style:"stroustrup"
3701   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3702   indent-tabs-mode:nil
3703   fill-column:99
3704   End:
3705 */
3706 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :