Code

067cf5435428d6a2b2452d900532bd464ffaa652
[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  * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
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"
43 #include "verbs.h"
44 #include "display/bezier-utils.h"
45 #include <vector>
46 #include <algorithm>
48 class NR::Matrix;
50 /// \todo
51 /// evil evil evil. FIXME: conflict of two different Path classes!
52 /// There is a conflict in the namespace between two classes named Path.
53 /// #include "sp-flowtext.h"
54 /// #include "sp-flowregion.h"
56 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
57 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
58 GType sp_flowregion_get_type (void);
59 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
60 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
61 GType sp_flowtext_get_type (void);
62 // end evil workaround
64 #include "helper/stlport.h"
67 /// \todo fixme: Implement these via preferences */
69 #define NODE_FILL          0xbfbfbf00
70 #define NODE_STROKE        0x000000ff
71 #define NODE_FILL_HI       0xff000000
72 #define NODE_STROKE_HI     0x000000ff
73 #define NODE_FILL_SEL      0x0000ffff
74 #define NODE_STROKE_SEL    0x000000ff
75 #define NODE_FILL_SEL_HI   0xff000000
76 #define NODE_STROKE_SEL_HI 0x000000ff
77 #define KNOT_FILL          0xffffffff
78 #define KNOT_STROKE        0x000000ff
79 #define KNOT_FILL_HI       0xff000000
80 #define KNOT_STROKE_HI     0x000000ff
82 static GMemChunk *nodechunk = NULL;
84 /* Creation from object */
86 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
87 static gchar *parse_nodetypes(gchar const *types, gint length);
89 /* Object updating */
91 static void stamp_repr(Inkscape::NodePath::Path *np);
92 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
93 static gchar *create_typestr(Inkscape::NodePath::Path *np);
95 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
97 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
99 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
101 /* Adjust handle placement, if the node or the other handle is moved */
102 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
103 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
105 /* Node event callbacks */
106 static void node_clicked(SPKnot *knot, guint state, gpointer data);
107 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
108 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
109 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
111 /* Handle event callbacks */
112 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
113 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
114 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
115 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
116 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
117 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
119 /* Constructors and destructors */
121 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
122 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
123 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
124 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
125 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
126                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
127 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
129 /* Helpers */
131 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
132 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
133 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
135 // active_node indicates mouseover node
136 static Inkscape::NodePath::Node *active_node = NULL;
138 /**
139  * \brief Creates new nodepath from item
140  */
141 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool show_handles)
143     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
145     /** \todo
146      * FIXME: remove this. We don't want to edit paths inside flowtext.
147      * Instead we will build our flowtext with cloned paths, so that the
148      * real paths are outside the flowtext and thus editable as usual.
149      */
150     if (SP_IS_FLOWTEXT(item)) {
151         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
152             if SP_IS_FLOWREGION(child) {
153                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
154                 if (grandchild && SP_IS_PATH(grandchild)) {
155                     item = SP_ITEM(grandchild);
156                     break;
157                 }
158             }
159         }
160     }
162     if (!SP_IS_PATH(item))
163         return NULL;
164     SPPath *path = SP_PATH(item);
165     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
166     if (curve == NULL)
167         return NULL;
169     NArtBpath *bpath = sp_curve_first_bpath(curve);
170     gint length = curve->end;
171     if (length == 0)
172         return NULL; // prevent crash for one-node paths
174     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
175     gchar *typestr = parse_nodetypes(nodetypes, length);
177     //Create new nodepath
178     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
179     if (!np)
180         return NULL;
182     // Set defaults
183     np->desktop     = desktop;
184     np->path        = path;
185     np->subpaths    = NULL;
186     np->selected    = NULL;
187     np->nodeContext = NULL; //Let the context that makes this set it
188     np->livarot_path = NULL;
189     np->local_change = 0;
190     np->show_handles = show_handles;
192     // we need to update item's transform from the repr here,
193     // because they may be out of sync when we respond
194     // to a change in repr by regenerating nodepath     --bb
195     sp_object_read_attr(SP_OBJECT(item), "transform");
197     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
198     np->d2i  = np->i2d.inverse();
199     np->repr = repr;
201     // create the subpath(s) from the bpath
202     NArtBpath *b = bpath;
203     while (b->code != NR_END) {
204         b = subpath_from_bpath(np, b, typestr + (b - bpath));
205     }
207     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
208     np->subpaths = g_list_reverse(np->subpaths);
210     g_free(typestr);
211     sp_curve_unref(curve);
213     // create the livarot representation from the same item
214     np->livarot_path = Path_for_item(item, true, true);
215     if (np->livarot_path)
216         np->livarot_path->ConvertWithBackData(0.01);
218     return np;
221 /**
222  * Destroys nodepath's subpaths, then itself, also tell context about it.
223  */
224 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
226     if (!np)  //soft fail, like delete
227         return;
229     while (np->subpaths) {
230         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
231     }
233     //Inform the context that made me, if any, that I am gone.
234     if (np->nodeContext)
235         np->nodeContext->nodepath = NULL;
237     g_assert(!np->selected);
239     if (np->livarot_path) {
240         delete np->livarot_path;
241         np->livarot_path = NULL;
242     }
244     np->desktop = NULL;
246     g_free(np);
250 /**
251  *  Return the node count of a given NodeSubPath.
252  */
253 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
255     if (!subpath)
256         return 0;
257     gint nodeCount = g_list_length(subpath->nodes);
258     return nodeCount;
261 /**
262  *  Return the node count of a given NodePath.
263  */
264 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
266     if (!np)
267         return 0;
268     gint nodeCount = 0;
269     for (GList *item = np->subpaths ; item ; item=item->next) {
270        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
271         nodeCount += g_list_length(subpath->nodes);
272     }
273     return nodeCount;
276 /**
277  *  Return the subpath count of a given NodePath.
278  */
279 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
281     if (!np)
282         return 0;
283     return g_list_length (np->subpaths);
286 /**
287  *  Return the selected node count of a given NodePath.
288  */
289 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
291     if (!np)
292         return 0;
293     return g_list_length (np->selected);
296 /**
297  *  Return the number of subpaths where nodes are selected in a given NodePath.
298  */
299 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
301     if (!np)
302         return 0;
303     if (!np->selected)
304         return 0;
305     if (!np->selected->next)
306         return 1;
307     gint count = 0;
308     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
309         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
310         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
311             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
312             if (node->selected) {
313                 count ++;
314                 break;
315             }
316         }
317     }
318     return count;
320  
321 /**
322  * Clean up a nodepath after editing.
323  *
324  * Currently we are deleting trivial subpaths.
325  */
326 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
328     GList *badSubPaths = NULL;
330     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
331     for (GList *l = nodepath->subpaths; l ; l=l->next) {
332        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
333        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
334             badSubPaths = g_list_append(badSubPaths, sp);
335     }
337     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
338     //also removes the subpath from nodepath->subpaths
339     for (GList *l = badSubPaths; l ; l=l->next) {
340        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
341         sp_nodepath_subpath_destroy(sp);
342     }
344     g_list_free(badSubPaths);
347 /**
348  * Create new nodepath from b, make it subpath of np.
349  * \param t The node type.
350  * \todo Fixme: t should be a proper type, rather than gchar
351  */
352 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
354     NR::Point ppos, pos, npos;
356     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
358     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
359     bool const closed = (b->code == NR_MOVETO);
361     pos = NR::Point(b->x3, b->y3) * np->i2d;
362     if (b[1].code == NR_CURVETO) {
363         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
364     } else {
365         npos = pos;
366     }
367     Inkscape::NodePath::Node *n;
368     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
369     g_assert(sp->first == n);
370     g_assert(sp->last  == n);
372     b++;
373     t++;
374     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
375         pos = NR::Point(b->x3, b->y3) * np->i2d;
376         if (b->code == NR_CURVETO) {
377             ppos = NR::Point(b->x2, b->y2) * np->i2d;
378         } else {
379             ppos = pos;
380         }
381         if (b[1].code == NR_CURVETO) {
382             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
383         } else {
384             npos = pos;
385         }
386         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
387         b++;
388         t++;
389     }
391     if (closed) sp_nodepath_subpath_close(sp);
393     return b;
396 /**
397  * Convert from sodipodi:nodetypes to new style type string.
398  */
399 static gchar *parse_nodetypes(gchar const *types, gint length)
401     g_assert(length > 0);
403     gchar *typestr = g_new(gchar, length + 1);
405     gint pos = 0;
407     if (types) {
408         for (gint i = 0; types[i] && ( i < length ); i++) {
409             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
410             if (types[i] != '\0') {
411                 switch (types[i]) {
412                     case 's':
413                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
414                         break;
415                     case 'z':
416                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
417                         break;
418                     case 'c':
419                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
420                         break;
421                     default:
422                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
423                         break;
424                 }
425             }
426         }
427     }
429     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
431     return typestr;
434 /**
435  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
436  * updated but repr is not (for speed). Used during curve and node drag.
437  */
438 static void update_object(Inkscape::NodePath::Path *np)
440     g_assert(np);
442     SPCurve *curve = create_curve(np);
444     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
446     sp_curve_unref(curve);
449 /**
450  * Update XML path node with data from path object.
451  */
452 static void update_repr_internal(Inkscape::NodePath::Path *np)
454     g_assert(np);
456     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
458     SPCurve *curve = create_curve(np);
459     gchar *typestr = create_typestr(np);
460     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
462     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
463         np->local_change++;
464         repr->setAttribute("d", svgpath);
465     }
467     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
468         np->local_change++;
469         repr->setAttribute("sodipodi:nodetypes", typestr);
470     }
472     g_free(svgpath);
473     g_free(typestr);
474     sp_curve_unref(curve);
477 /**
478  * Update XML path node with data from path object, commit changes forever.
479  */
480 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
482     //fixme: np can be NULL, so check before proceeding
483     g_return_if_fail(np != NULL);
485     update_repr_internal(np);
486     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
487                      annotation);
489     if (np->livarot_path) {
490         delete np->livarot_path;
491         np->livarot_path = NULL;
492     }
494     if (np->path && SP_IS_ITEM(np->path)) {
495         np->livarot_path = Path_for_item (np->path, true, true);
496         if (np->livarot_path)
497             np->livarot_path->ConvertWithBackData(0.01);
498     }
502 /**
503  * Update XML path node with data from path object, commit changes with undo.
504  */
505 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
507     update_repr_internal(np);
508     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
509                            annotation);
511     if (np->livarot_path) {
512         delete np->livarot_path;
513         np->livarot_path = NULL;
514     }
516     if (np->path && SP_IS_ITEM(np->path)) {
517         np->livarot_path = Path_for_item (np->path, true, true);
518         if (np->livarot_path)
519             np->livarot_path->ConvertWithBackData(0.01);
520     }
523 /**
524  * Make duplicate of path, replace corresponding XML node in tree, commit.
525  */
526 static void stamp_repr(Inkscape::NodePath::Path *np)
528     g_assert(np);
530     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
531     Inkscape::XML::Node *new_repr = old_repr->duplicate();
533     // remember the position of the item
534     gint pos = old_repr->position();
535     // remember parent
536     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
538     SPCurve *curve = create_curve(np);
539     gchar *typestr = create_typestr(np);
541     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
543     new_repr->setAttribute("d", svgpath);
544     new_repr->setAttribute("sodipodi:nodetypes", typestr);
546     // add the new repr to the parent
547     parent->appendChild(new_repr);
548     // move to the saved position
549     new_repr->setPosition(pos > 0 ? pos : 0);
551     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
552                      _("Stamp"));
554     Inkscape::GC::release(new_repr);
555     g_free(svgpath);
556     g_free(typestr);
557     sp_curve_unref(curve);
560 /**
561  * Create curve from path.
562  */
563 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
565     SPCurve *curve = sp_curve_new();
567     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
568        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
569         sp_curve_moveto(curve,
570                         sp->first->pos * np->d2i);
571        Inkscape::NodePath::Node *n = sp->first->n.other;
572         while (n) {
573             NR::Point const end_pt = n->pos * np->d2i;
574             switch (n->code) {
575                 case NR_LINETO:
576                     sp_curve_lineto(curve, end_pt);
577                     break;
578                 case NR_CURVETO:
579                     sp_curve_curveto(curve,
580                                      n->p.other->n.pos * np->d2i,
581                                      n->p.pos * np->d2i,
582                                      end_pt);
583                     break;
584                 default:
585                     g_assert_not_reached();
586                     break;
587             }
588             if (n != sp->last) {
589                 n = n->n.other;
590             } else {
591                 n = NULL;
592             }
593         }
594         if (sp->closed) {
595             sp_curve_closepath(curve);
596         }
597     }
599     return curve;
602 /**
603  * Convert path type string to sodipodi:nodetypes style.
604  */
605 static gchar *create_typestr(Inkscape::NodePath::Path *np)
607     gchar *typestr = g_new(gchar, 32);
608     gint len = 32;
609     gint pos = 0;
611     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
612        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
614         if (pos >= len) {
615             typestr = g_renew(gchar, typestr, len + 32);
616             len += 32;
617         }
619         typestr[pos++] = 'c';
621        Inkscape::NodePath::Node *n;
622         n = sp->first->n.other;
623         while (n) {
624             gchar code;
626             switch (n->type) {
627                 case Inkscape::NodePath::NODE_CUSP:
628                     code = 'c';
629                     break;
630                 case Inkscape::NodePath::NODE_SMOOTH:
631                     code = 's';
632                     break;
633                 case Inkscape::NodePath::NODE_SYMM:
634                     code = 'z';
635                     break;
636                 default:
637                     g_assert_not_reached();
638                     code = '\0';
639                     break;
640             }
642             if (pos >= len) {
643                 typestr = g_renew(gchar, typestr, len + 32);
644                 len += 32;
645             }
647             typestr[pos++] = code;
649             if (n != sp->last) {
650                 n = n->n.other;
651             } else {
652                 n = NULL;
653             }
654         }
655     }
657     if (pos >= len) {
658         typestr = g_renew(gchar, typestr, len + 1);
659         len += 1;
660     }
662     typestr[pos++] = '\0';
664     return typestr;
667 /**
668  * Returns current path in context.
669  */
670 static Inkscape::NodePath::Path *sp_nodepath_current()
672     if (!SP_ACTIVE_DESKTOP) {
673         return NULL;
674     }
676     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
678     if (!SP_IS_NODE_CONTEXT(event_context)) {
679         return NULL;
680     }
682     return SP_NODE_CONTEXT(event_context)->nodepath;
687 /**
688  \brief Fills node and handle positions for three nodes, splitting line
689   marked by end at distance t.
690  */
691 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
693     g_assert(new_path != NULL);
694     g_assert(end      != NULL);
696     g_assert(end->p.other == new_path);
697    Inkscape::NodePath::Node *start = new_path->p.other;
698     g_assert(start);
700     if (end->code == NR_LINETO) {
701         new_path->type =Inkscape::NodePath::NODE_CUSP;
702         new_path->code = NR_LINETO;
703         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
704     } else {
705         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
706         new_path->code = NR_CURVETO;
707         gdouble s      = 1 - t;
708         for (int dim = 0; dim < 2; dim++) {
709             NR::Coord const f000 = start->pos[dim];
710             NR::Coord const f001 = start->n.pos[dim];
711             NR::Coord const f011 = end->p.pos[dim];
712             NR::Coord const f111 = end->pos[dim];
713             NR::Coord const f00t = s * f000 + t * f001;
714             NR::Coord const f01t = s * f001 + t * f011;
715             NR::Coord const f11t = s * f011 + t * f111;
716             NR::Coord const f0tt = s * f00t + t * f01t;
717             NR::Coord const f1tt = s * f01t + t * f11t;
718             NR::Coord const fttt = s * f0tt + t * f1tt;
719             start->n.pos[dim]    = f00t;
720             new_path->p.pos[dim] = f0tt;
721             new_path->pos[dim]   = fttt;
722             new_path->n.pos[dim] = f1tt;
723             end->p.pos[dim]      = f11t;
724         }
725     }
728 /**
729  * Adds new node on direct line between two nodes, activates handles of all
730  * three nodes.
731  */
732 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
734     g_assert(end);
735     g_assert(end->subpath);
736     g_assert(g_list_find(end->subpath->nodes, end));
738    Inkscape::NodePath::Node *start = end->p.other;
739     g_assert( start->n.other == end );
740    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
741                                                end,
742                                               Inkscape::NodePath::NODE_SMOOTH,
743                                                (NRPathcode)end->code,
744                                                &start->pos, &start->pos, &start->n.pos);
745     sp_nodepath_line_midpoint(newnode, end, t);
747     sp_node_update_handles(start);
748     sp_node_update_handles(newnode);
749     sp_node_update_handles(end);
751     return newnode;
754 /**
755 \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
756 */
757 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
759     g_assert(node);
760     g_assert(node->subpath);
761     g_assert(g_list_find(node->subpath->nodes, node));
763    Inkscape::NodePath::SubPath *sp = node->subpath;
764     Inkscape::NodePath::Path *np    = sp->nodepath;
766     if (sp->closed) {
767         sp_nodepath_subpath_open(sp, node);
768         return sp->first;
769     } else {
770         // no break for end nodes
771         if (node == sp->first) return NULL;
772         if (node == sp->last ) return NULL;
774         // create a new subpath
775        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
777         // duplicate the break node as start of the new subpath
778        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
780         while (node->n.other) { // copy the remaining nodes into the new subpath
781            Inkscape::NodePath::Node *n  = node->n.other;
782            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);
783             if (n->selected) {
784                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
785             }
786             sp_nodepath_node_destroy(n); // remove the point on the original subpath
787         }
789         return newnode;
790     }
793 /**
794  * Duplicate node and connect to neighbours.
795  */
796 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
798     g_assert(node);
799     g_assert(node->subpath);
800     g_assert(g_list_find(node->subpath->nodes, node));
802    Inkscape::NodePath::SubPath *sp = node->subpath;
804     NRPathcode code = (NRPathcode) node->code;
805     if (code == NR_MOVETO) { // if node is the endnode,
806         node->code = NR_LINETO; // new one is inserted before it, so change that to line
807     }
809     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
811     if (!node->n.other || !node->p.other) // if node is an endnode, select it
812         return node;
813     else
814         return newnode; // otherwise select the newly created node
817 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
819     node->p.pos = (node->pos + (node->pos - node->n.pos));
822 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
824     node->n.pos = (node->pos + (node->pos - node->p.pos));
827 /**
828  * Change line type at node, with side effects on neighbours.
829  */
830 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
832     g_assert(end);
833     g_assert(end->subpath);
834     g_assert(end->p.other);
836     if (end->code == static_cast< guint > ( code ) )
837         return;
839    Inkscape::NodePath::Node *start = end->p.other;
841     end->code = code;
843     if (code == NR_LINETO) {
844         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
845         if (end->n.other) {
846             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
847         }
848         sp_node_adjust_handle(start, -1);
849         sp_node_adjust_handle(end, 1);
850     } else {
851         NR::Point delta = end->pos - start->pos;
852         start->n.pos = start->pos + delta / 3;
853         end->p.pos = end->pos - delta / 3;
854         sp_node_adjust_handle(start, 1);
855         sp_node_adjust_handle(end, -1);
856     }
858     sp_node_update_handles(start);
859     sp_node_update_handles(end);
862 /**
863  * Change node type, and its handles accordingly.
864  */
865 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
867     g_assert(node);
868     g_assert(node->subpath);
870     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
871         return node;
873     if ((node->p.other != NULL) && (node->n.other != NULL)) {
874         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
875             type =Inkscape::NodePath::NODE_CUSP;
876         }
877     }
879     node->type = type;
881     if (node->type == Inkscape::NodePath::NODE_CUSP) {
882         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
883         node->knot->setSize (node->selected? 11 : 9);
884         sp_knot_update_ctrl(node->knot);
885     } else {
886         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
887         node->knot->setSize (node->selected? 9 : 7);
888         sp_knot_update_ctrl(node->knot);
889     }
891     // if one of handles is mouseovered, preserve its position
892     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
893         sp_node_adjust_handle(node, 1);
894     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
895         sp_node_adjust_handle(node, -1);
896     } else {
897         sp_node_adjust_handles(node);
898     }
900     sp_node_update_handles(node);
902     sp_nodepath_update_statusbar(node->subpath->nodepath);
904     return node;
907 /**
908  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
909  * adjacent segments from lines to curves.
910 */
911 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
913     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
914         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
915             // convert adjacent segment BEFORE to curve
916             node->code = NR_CURVETO;
917             NR::Point delta;
918             if (node->n.other != NULL)
919                 delta = node->n.other->pos - node->p.other->pos;
920             else
921                 delta = node->pos - node->p.other->pos;
922             node->p.pos = node->pos - delta / 4;
923             sp_node_update_handles(node);
924         }
926         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
927             // convert adjacent segment AFTER to curve
928             node->n.other->code = NR_CURVETO;
929             NR::Point delta;
930             if (node->p.other != NULL)
931                 delta = node->p.other->pos - node->n.other->pos;
932             else
933                 delta = node->pos - node->n.other->pos;
934             node->n.pos = node->pos - delta / 4;
935             sp_node_update_handles(node);
936         }
937     }
939     sp_nodepath_set_node_type (node, type);
942 /**
943  * Move node to point, and adjust its and neighbouring handles.
944  */
945 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
947     NR::Point delta = p - node->pos;
948     node->pos = p;
950     node->p.pos += delta;
951     node->n.pos += delta;
953     if (node->p.other) {
954         if (node->code == NR_LINETO) {
955             sp_node_adjust_handle(node, 1);
956             sp_node_adjust_handle(node->p.other, -1);
957         }
958     }
959     if (node->n.other) {
960         if (node->n.other->code == NR_LINETO) {
961             sp_node_adjust_handle(node, -1);
962             sp_node_adjust_handle(node->n.other, 1);
963         }
964     }
966     // this function is only called from batch movers that will update display at the end
967     // themselves, so here we just move all the knots without emitting move signals, for speed
968     sp_node_update_handles(node, false);
971 /**
972  * Call sp_node_moveto() for node selection and handle possible snapping.
973  */
974 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
975                                             bool const snap = true)
977     NR::Coord best = NR_HUGE;
978     NR::Point delta(dx, dy);
979     NR::Point best_pt = delta;
981     if (snap) {
982         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
983         
984         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
985             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
986             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
987             if (s.getDistance() < best) {
988                 best = s.getDistance();
989                 best_pt = s.getPoint() - n->pos;
990             }
991         }
992     }
994     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
995         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
996         sp_node_moveto(n, n->pos + best_pt);
997     }
999     // do not update repr here so that node dragging is acceptably fast
1000     update_object(nodepath);
1003 /**
1004 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1005 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1006 near x = 0.
1007  */
1008 double
1009 sculpt_profile (double x, double alpha, guint profile)
1011     if (x >= 1)
1012         return 0;
1013     if (x <= 0)
1014         return 1;
1016     switch (profile) {
1017         case SCULPT_PROFILE_LINEAR:
1018         return 1 - x;
1019         case SCULPT_PROFILE_BELL:
1020         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1021         case SCULPT_PROFILE_ELLIPTIC:
1022         return sqrt(1 - x*x);
1023     }
1025     return 1;
1028 double
1029 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1031     // extremely primitive for now, don't have time to look for the real one
1032     double lower = NR::L2(b - a);
1033     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1034     return (lower + upper)/2;
1037 void
1038 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1040     n->pos = n->origin + delta;
1041     n->n.pos = n->n.origin + delta_n;
1042     n->p.pos = n->p.origin + delta_p;
1043     sp_node_adjust_handles(n);
1044     sp_node_update_handles(n, false);
1047 /**
1048  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1049  * on how far they are from the dragged node n.
1050  */
1051 static void 
1052 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1054     g_assert (n);
1055     g_assert (nodepath);
1056     g_assert (n->subpath->nodepath == nodepath);
1058     double pressure = n->knot->pressure;
1059     if (pressure == 0)
1060         pressure = 0.5; // default
1061     pressure = CLAMP (pressure, 0.2, 0.8);
1063     // map pressure to alpha = 1/5 ... 5
1064     double alpha = 1 - 2 * fabs(pressure - 0.5);
1065     if (pressure > 0.5)
1066         alpha = 1/alpha;
1068     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1070     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1071         // Only one subpath has selected nodes:
1072         // use linear mode, where the distance from n to node being dragged is calculated along the path
1074         double n_sel_range = 0, p_sel_range = 0;
1075         guint n_nodes = 0, p_nodes = 0;
1076         guint n_sel_nodes = 0, p_sel_nodes = 0;
1078         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1079         {
1080             double n_range = 0, p_range = 0;
1081             bool n_going = true, p_going = true;
1082             Inkscape::NodePath::Node *n_node = n;
1083             Inkscape::NodePath::Node *p_node = n;
1084             do {
1085                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1086                 if (n_node && n_going)
1087                     n_node = n_node->n.other;
1088                 if (n_node == NULL) {
1089                     n_going = false;
1090                 } else {
1091                     n_nodes ++;
1092                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1093                     if (n_node->selected) {
1094                         n_sel_nodes ++;
1095                         n_sel_range = n_range;
1096                     }
1097                     if (n_node == p_node) {
1098                         n_going = false;
1099                         p_going = false;
1100                     }
1101                 }
1102                 if (p_node && p_going)
1103                     p_node = p_node->p.other;
1104                 if (p_node == NULL) {
1105                     p_going = false;
1106                 } else {
1107                     p_nodes ++;
1108                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1109                     if (p_node->selected) {
1110                         p_sel_nodes ++;
1111                         p_sel_range = p_range;
1112                     }
1113                     if (p_node == n_node) {
1114                         n_going = false;
1115                         p_going = false;
1116                     }
1117                 }
1118             } while (n_going || p_going);
1119         }
1121         // Second pass: actually move nodes in this subpath
1122         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1123         {
1124             double n_range = 0, p_range = 0;
1125             bool n_going = true, p_going = true;
1126             Inkscape::NodePath::Node *n_node = n;
1127             Inkscape::NodePath::Node *p_node = n;
1128             do {
1129                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1130                 if (n_node && n_going)
1131                     n_node = n_node->n.other;
1132                 if (n_node == NULL) {
1133                     n_going = false;
1134                 } else {
1135                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1136                     if (n_node->selected) {
1137                         sp_nodepath_move_node_and_handles (n_node, 
1138                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1139                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1140                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1141                     }
1142                     if (n_node == p_node) {
1143                         n_going = false;
1144                         p_going = false;
1145                     }
1146                 }
1147                 if (p_node && p_going)
1148                     p_node = p_node->p.other;
1149                 if (p_node == NULL) {
1150                     p_going = false;
1151                 } else {
1152                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1153                     if (p_node->selected) {
1154                         sp_nodepath_move_node_and_handles (p_node, 
1155                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1156                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1157                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1158                     }
1159                     if (p_node == n_node) {
1160                         n_going = false;
1161                         p_going = false;
1162                     }
1163                 }
1164             } while (n_going || p_going);
1165         }
1167     } else {
1168         // Multiple subpaths have selected nodes:
1169         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1170         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1171         // fix the pear-like shape when sculpting e.g. a ring
1173         // First pass: calculate range
1174         gdouble direct_range = 0;
1175         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1176             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1177             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1178                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1179                 if (node->selected) {
1180                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1181                 }
1182             }
1183         }
1185         // Second pass: actually move nodes
1186         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1187             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1188             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1189                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1190                 if (node->selected) {
1191                     if (direct_range > 1e-6) {
1192                         sp_nodepath_move_node_and_handles (node,
1193                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1194                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1195                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1196                     } else {
1197                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1198                     }
1200                 }
1201             }
1202         }
1203     }
1205     // do not update repr here so that node dragging is acceptably fast
1206     update_object(nodepath);
1210 /**
1211  * Move node selection to point, adjust its and neighbouring handles,
1212  * handle possible snapping, and commit the change with possible undo.
1213  */
1214 void
1215 sp_node_selected_move(gdouble dx, gdouble dy)
1217     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1218     if (!nodepath) return;
1220     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1222     if (dx == 0) {
1223         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1224     } else if (dy == 0) {
1225         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1226     } else {
1227         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1228     }
1231 /**
1232  * Move node selection off screen and commit the change.
1233  */
1234 void
1235 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1237     // borrowed from sp_selection_move_screen in selection-chemistry.c
1238     // we find out the current zoom factor and divide deltas by it
1239     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1241     gdouble zoom = desktop->current_zoom();
1242     gdouble zdx = dx / zoom;
1243     gdouble zdy = dy / zoom;
1245     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1246     if (!nodepath) return;
1248     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1250     if (dx == 0) {
1251         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1252     } else if (dy == 0) {
1253         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1254     } else {
1255         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1256     }
1259 /** If they don't yet exist, creates knot and line for the given side of the node */
1260 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1262     if (!side->knot) {
1263         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"));
1265         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1266         side->knot->setSize (7);
1267         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1268         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1269         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1270         sp_knot_update_ctrl(side->knot);
1272         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1273         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1274         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1275         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1276         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1277         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1278     }
1280     if (!side->line) {
1281         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1282                                         SP_TYPE_CTRLLINE, NULL);
1283     }
1286 /**
1287  * Ensure the given handle of the node is visible/invisible, update its screen position
1288  */
1289 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1291     g_assert(node != NULL);
1293    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1294     NRPathcode code = sp_node_path_code_from_side(node, side);
1296     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1298     if (show_handle) {
1299         if (!side->knot) { // No handle knot at all
1300             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1301             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1302             side->knot->pos = side->pos;
1303             if (side->knot->item) 
1304                 SP_CTRL(side->knot->item)->moveto(side->pos);
1305             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1306             sp_knot_show(side->knot);
1307         } else {
1308             if (side->knot->pos != side->pos) { // only if it's really moved
1309                 if (fire_move_signals) {
1310                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1311                 } else {
1312                     sp_knot_moveto(side->knot, &side->pos);
1313                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1314                 }
1315             }
1316             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1317                 sp_knot_show(side->knot);
1318             }
1319         }
1320         sp_canvas_item_show(side->line);
1321     } else {
1322         if (side->knot) {
1323             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1324                 sp_knot_hide(side->knot);
1325             }
1326         }
1327         if (side->line) {
1328             sp_canvas_item_hide(side->line);
1329         }
1330     }
1333 /**
1334  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1335  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1336  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1337  * updated; otherwise, just move the knots silently (used in batch moves).
1338  */
1339 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1341     g_assert(node != NULL);
1343     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1344         sp_knot_show(node->knot);
1345     }
1347     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1348         if (fire_move_signals)
1349             sp_knot_set_position(node->knot, &node->pos, 0);
1350         else 
1351             sp_knot_moveto(node->knot, &node->pos);
1352     }
1354     gboolean show_handles = node->selected;
1355     if (node->p.other != NULL) {
1356         if (node->p.other->selected) show_handles = TRUE;
1357     }
1358     if (node->n.other != NULL) {
1359         if (node->n.other->selected) show_handles = TRUE;
1360     }
1362     if (node->subpath->nodepath->show_handles == false)
1363         show_handles = FALSE;
1365     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1366     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1369 /**
1370  * Call sp_node_update_handles() for all nodes on subpath.
1371  */
1372 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1374     g_assert(subpath != NULL);
1376     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1377         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1378     }
1381 /**
1382  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1383  */
1384 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1386     g_assert(nodepath != NULL);
1388     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1389         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1390     }
1393 void
1394 sp_nodepath_show_handles(bool show)
1396     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1397     if (nodepath == NULL) return;
1399     nodepath->show_handles = show;
1400     sp_nodepath_update_handles(nodepath);
1403 /**
1404  * Adds all selected nodes in nodepath to list.
1405  */
1406 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1408     StlConv<Node *>::list(l, selected);
1409 /// \todo this adds a copying, rework when the selection becomes a stl list
1412 /**
1413  * Align selected nodes on the specified axis.
1414  */
1415 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1417     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1418         return;
1419     }
1421     if ( !nodepath->selected->next ) { // only one node selected
1422         return;
1423     }
1424    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1425     NR::Point dest(pNode->pos);
1426     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1427         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1428         if (pNode) {
1429             dest[axis] = pNode->pos[axis];
1430             sp_node_moveto(pNode, dest);
1431         }
1432     }
1434     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1437 /// Helper struct.
1438 struct NodeSort
1440    Inkscape::NodePath::Node *_node;
1441     NR::Coord _coord;
1442     /// \todo use vectorof pointers instead of calling copy ctor
1443     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1444         _node(node), _coord(node->pos[axis])
1445     {}
1447 };
1449 static bool operator<(NodeSort const &a, NodeSort const &b)
1451     return (a._coord < b._coord);
1454 /**
1455  * Distribute selected nodes on the specified axis.
1456  */
1457 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1459     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1460         return;
1461     }
1463     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1464         return;
1465     }
1467    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1468     std::vector<NodeSort> sorted;
1469     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1470         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1471         if (pNode) {
1472             NodeSort n(pNode, axis);
1473             sorted.push_back(n);
1474             //dest[axis] = pNode->pos[axis];
1475             //sp_node_moveto(pNode, dest);
1476         }
1477     }
1478     std::sort(sorted.begin(), sorted.end());
1479     unsigned int len = sorted.size();
1480     //overall bboxes span
1481     float dist = (sorted.back()._coord -
1482                   sorted.front()._coord);
1483     //new distance between each bbox
1484     float step = (dist) / (len - 1);
1485     float pos = sorted.front()._coord;
1486     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1487           it < sorted.end();
1488           it ++ )
1489     {
1490         NR::Point dest((*it)._node->pos);
1491         dest[axis] = pos;
1492         sp_node_moveto((*it)._node, dest);
1493         pos += step;
1494     }
1496     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1500 /**
1501  * Call sp_nodepath_line_add_node() for all selected segments.
1502  */
1503 void
1504 sp_node_selected_add_node(void)
1506     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1507     if (!nodepath) {
1508         return;
1509     }
1511     GList *nl = NULL;
1513     int n_added = 0;
1515     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1516        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1517         g_assert(t->selected);
1518         if (t->p.other && t->p.other->selected) {
1519             nl = g_list_prepend(nl, t);
1520         }
1521     }
1523     while (nl) {
1524        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1525        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1526        sp_nodepath_node_select(n, TRUE, FALSE);
1527        n_added ++;
1528        nl = g_list_remove(nl, t);
1529     }
1531     /** \todo fixme: adjust ? */
1532     sp_nodepath_update_handles(nodepath);
1534     if (n_added > 1) {
1535         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1536     } else if (n_added > 0) {
1537         sp_nodepath_update_repr(nodepath, _("Add node"));
1538     }
1540     sp_nodepath_update_statusbar(nodepath);
1543 /**
1544  * Select segment nearest to point
1545  */
1546 void
1547 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1549     if (!nodepath) {
1550         return;
1551     }
1553     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1555     //find segment to segment
1556     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1558     //fixme: this can return NULL, so check before proceeding.
1559     g_return_if_fail(e != NULL);
1560     
1561     gboolean force = FALSE;
1562     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1563         force = TRUE;
1564     }
1565     sp_nodepath_node_select(e, (gboolean) toggle, force);
1566     if (e->p.other)
1567         sp_nodepath_node_select(e->p.other, TRUE, force);
1569     sp_nodepath_update_handles(nodepath);
1571     sp_nodepath_update_statusbar(nodepath);
1574 /**
1575  * Add a node nearest to point
1576  */
1577 void
1578 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1580     if (!nodepath) {
1581         return;
1582     }
1584     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1586     //find segment to split
1587     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1589     //don't know why but t seems to flip for lines
1590     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1591         position.t = 1.0 - position.t;
1592     }
1593     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1594     sp_nodepath_node_select(n, FALSE, TRUE);
1596     /* fixme: adjust ? */
1597     sp_nodepath_update_handles(nodepath);
1599     sp_nodepath_update_repr(nodepath, _("Add node"));
1601     sp_nodepath_update_statusbar(nodepath);
1604 /*
1605  * Adjusts a segment so that t moves by a certain delta for dragging
1606  * converts lines to curves
1607  *
1608  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1609  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1610  */
1611 void
1612 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1614     //fixme: e and e->p can be NULL, so check for those before proceeding
1615     g_return_if_fail(e != NULL);
1616     g_return_if_fail(&e->p != NULL);
1617     
1618     /* feel good is an arbitrary parameter that distributes the delta between handles
1619      * if t of the drag point is less than 1/6 distance form the endpoint only
1620      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1621      */
1622     double feel_good;
1623     if (t <= 1.0 / 6.0)
1624         feel_good = 0;
1625     else if (t <= 0.5)
1626         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1627     else if (t <= 5.0 / 6.0)
1628         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1629     else
1630         feel_good = 1;
1632     //if we're dragging a line convert it to a curve
1633     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1634         sp_nodepath_set_line_type(e, NR_CURVETO);
1635     }
1637     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1638     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1639     e->p.other->n.pos += offsetcoord0;
1640     e->p.pos += offsetcoord1;
1642     // adjust handles of adjacent nodes where necessary
1643     sp_node_adjust_handle(e,1);
1644     sp_node_adjust_handle(e->p.other,-1);
1646     sp_nodepath_update_handles(e->subpath->nodepath);
1648     update_object(e->subpath->nodepath);
1650     sp_nodepath_update_statusbar(e->subpath->nodepath);
1654 /**
1655  * Call sp_nodepath_break() for all selected segments.
1656  */
1657 void sp_node_selected_break()
1659     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1660     if (!nodepath) return;
1662     GList *temp = NULL;
1663     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1664        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1665        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1666         if (nn == NULL) continue; // no break, no new node
1667         temp = g_list_prepend(temp, nn);
1668     }
1670     if (temp) {
1671         sp_nodepath_deselect(nodepath);
1672     }
1673     for (GList *l = temp; l != NULL; l = l->next) {
1674         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1675     }
1677     sp_nodepath_update_handles(nodepath);
1679     sp_nodepath_update_repr(nodepath, _("Break path"));
1682 /**
1683  * Duplicate the selected node(s).
1684  */
1685 void sp_node_selected_duplicate()
1687     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1688     if (!nodepath) {
1689         return;
1690     }
1692     GList *temp = NULL;
1693     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1694        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1695        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1696         if (nn == NULL) continue; // could not duplicate
1697         temp = g_list_prepend(temp, nn);
1698     }
1700     if (temp) {
1701         sp_nodepath_deselect(nodepath);
1702     }
1703     for (GList *l = temp; l != NULL; l = l->next) {
1704         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1705     }
1707     sp_nodepath_update_handles(nodepath);
1709     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1712 /**
1713  *  Join two nodes by merging them into one.
1714  */
1715 void sp_node_selected_join()
1717     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1718     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1720     if (g_list_length(nodepath->selected) != 2) {
1721         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1722         return;
1723     }
1725    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1726    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1728     g_assert(a != b);
1729     g_assert(a->p.other || a->n.other);
1730     g_assert(b->p.other || b->n.other);
1732     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1733         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1734         return;
1735     }
1737     /* a and b are endpoints */
1739     NR::Point c;
1740     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1741         c = a->pos;
1742     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1743         c = b->pos;
1744     } else {
1745         c = (a->pos + b->pos) / 2;
1746     }
1748     if (a->subpath == b->subpath) {
1749        Inkscape::NodePath::SubPath *sp = a->subpath;
1750         sp_nodepath_subpath_close(sp);
1751         sp_node_moveto (sp->first, c);
1753         sp_nodepath_update_handles(sp->nodepath);
1754         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1755         return;
1756     }
1758     /* a and b are separate subpaths */
1759    Inkscape::NodePath::SubPath *sa = a->subpath;
1760    Inkscape::NodePath::SubPath *sb = b->subpath;
1761     NR::Point p;
1762    Inkscape::NodePath::Node *n;
1763     NRPathcode code;
1764     if (a == sa->first) {
1765         p = sa->first->n.pos;
1766         code = (NRPathcode)sa->first->n.other->code;
1767        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1768         n = sa->last;
1769         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1770         n = n->p.other;
1771         while (n) {
1772             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1773             n = n->p.other;
1774             if (n == sa->first) n = NULL;
1775         }
1776         sp_nodepath_subpath_destroy(sa);
1777         sa = t;
1778     } else if (a == sa->last) {
1779         p = sa->last->p.pos;
1780         code = (NRPathcode)sa->last->code;
1781         sp_nodepath_node_destroy(sa->last);
1782     } else {
1783         code = NR_END;
1784         g_assert_not_reached();
1785     }
1787     if (b == sb->first) {
1788         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1789         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1790             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1791         }
1792     } else if (b == sb->last) {
1793         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1794         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1795             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1796         }
1797     } else {
1798         g_assert_not_reached();
1799     }
1800     /* and now destroy sb */
1802     sp_nodepath_subpath_destroy(sb);
1804     sp_nodepath_update_handles(sa->nodepath);
1806     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1808     sp_nodepath_update_statusbar(nodepath);
1811 /**
1812  *  Join two nodes by adding a segment between them.
1813  */
1814 void sp_node_selected_join_segment()
1816     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1817     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1819     if (g_list_length(nodepath->selected) != 2) {
1820         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1821         return;
1822     }
1824    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1825    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1827     g_assert(a != b);
1828     g_assert(a->p.other || a->n.other);
1829     g_assert(b->p.other || b->n.other);
1831     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1832         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1833         return;
1834     }
1836     if (a->subpath == b->subpath) {
1837        Inkscape::NodePath::SubPath *sp = a->subpath;
1839         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1840         sp->closed = TRUE;
1842         sp->first->p.other = sp->last;
1843         sp->last->n.other  = sp->first;
1845         sp_node_handle_mirror_p_to_n(sp->last);
1846         sp_node_handle_mirror_n_to_p(sp->first);
1848         sp->first->code = sp->last->code;
1849         sp->first       = sp->last;
1851         sp_nodepath_update_handles(sp->nodepath);
1853         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1855         return;
1856     }
1858     /* a and b are separate subpaths */
1859    Inkscape::NodePath::SubPath *sa = a->subpath;
1860    Inkscape::NodePath::SubPath *sb = b->subpath;
1862    Inkscape::NodePath::Node *n;
1863     NR::Point p;
1864     NRPathcode code;
1865     if (a == sa->first) {
1866         code = (NRPathcode) sa->first->n.other->code;
1867        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1868         n = sa->last;
1869         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1870         for (n = n->p.other; n != NULL; n = n->p.other) {
1871             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1872         }
1873         sp_nodepath_subpath_destroy(sa);
1874         sa = t;
1875     } else if (a == sa->last) {
1876         code = (NRPathcode)sa->last->code;
1877     } else {
1878         code = NR_END;
1879         g_assert_not_reached();
1880     }
1882     if (b == sb->first) {
1883         n = sb->first;
1884         sp_node_handle_mirror_p_to_n(sa->last);
1885         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1886         sp_node_handle_mirror_n_to_p(sa->last);
1887         for (n = n->n.other; n != NULL; n = n->n.other) {
1888             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1889         }
1890     } else if (b == sb->last) {
1891         n = sb->last;
1892         sp_node_handle_mirror_p_to_n(sa->last);
1893         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1894         sp_node_handle_mirror_n_to_p(sa->last);
1895         for (n = n->p.other; n != NULL; n = n->p.other) {
1896             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1897         }
1898     } else {
1899         g_assert_not_reached();
1900     }
1901     /* and now destroy sb */
1903     sp_nodepath_subpath_destroy(sb);
1905     sp_nodepath_update_handles(sa->nodepath);
1907     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1910 /**
1911  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1912  */
1913 void sp_node_delete_preserve(GList *nodes_to_delete)
1915     GSList *nodepaths = NULL;
1916     
1917     while (nodes_to_delete) {
1918         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1919         Inkscape::NodePath::SubPath *sp = node->subpath;
1920         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1921         Inkscape::NodePath::Node *sample_cursor = NULL;
1922         Inkscape::NodePath::Node *sample_end = NULL;
1923         Inkscape::NodePath::Node *delete_cursor = node;
1924         bool just_delete = false;
1925         
1926         //find the start of this contiguous selection
1927         //move left to the first node that is not selected
1928         //or the start of the non-closed path
1929         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1930             delete_cursor = curr;
1931         }
1933         //just delete at the beginning of an open path
1934         if (!delete_cursor->p.other) {
1935             sample_cursor = delete_cursor;
1936             just_delete = true;
1937         } else {
1938             sample_cursor = delete_cursor->p.other;
1939         }
1940         
1941         //calculate points for each segment
1942         int rate = 5;
1943         float period = 1.0 / rate;
1944         std::vector<NR::Point> data;
1945         if (!just_delete) {
1946             data.push_back(sample_cursor->pos);
1947             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1948                 //just delete at the end of an open path
1949                 if (!sp->closed && curr == sp->last) {
1950                     just_delete = true;
1951                     break;
1952                 }
1953                 
1954                 //sample points on the contiguous selected segment
1955                 NR::Point *bez;
1956                 bez = new NR::Point [4];
1957                 bez[0] = curr->pos;
1958                 bez[1] = curr->n.pos;
1959                 bez[2] = curr->n.other->p.pos;
1960                 bez[3] = curr->n.other->pos;
1961                 for (int i=1; i<rate; i++) {
1962                     gdouble t = i * period;
1963                     NR::Point p = bezier_pt(3, bez, t);
1964                     data.push_back(p);
1965                 }
1966                 data.push_back(curr->n.other->pos);
1968                 sample_end = curr->n.other;
1969                 //break if we've come full circle or hit the end of the selection
1970                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1971                     break;
1972                 }
1973             }
1974         }
1976         if (!just_delete) {
1977             //calculate the best fitting single segment and adjust the endpoints
1978             NR::Point *adata;
1979             adata = new NR::Point [data.size()];
1980             copy(data.begin(), data.end(), adata);
1981             
1982             NR::Point *bez;
1983             bez = new NR::Point [4];
1984             //would decreasing error create a better fitting approximation?
1985             gdouble error = 1.0;
1986             gint ret;
1987             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1989             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
1990             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
1991             //the resulting nodes behave as expected.
1992             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
1993             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
1994             
1995             //adjust endpoints
1996             sample_cursor->n.pos = bez[1];
1997             sample_end->p.pos = bez[2];
1998         }
1999        
2000         //destroy this contiguous selection
2001         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2002             Inkscape::NodePath::Node *temp = delete_cursor;
2003             if (delete_cursor->n.other == delete_cursor) {
2004                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2005                 delete_cursor = NULL; 
2006             } else {
2007                 delete_cursor = delete_cursor->n.other;
2008             }
2009             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2010             sp_nodepath_node_destroy(temp);
2011         }
2013         sp_nodepath_update_handles(nodepath);
2015         if (!g_slist_find(nodepaths, nodepath))
2016             nodepaths = g_slist_prepend (nodepaths, nodepath);
2017     }
2019     for (GSList *i = nodepaths; i; i = i->next) {
2020         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2021         // different nodepaths will give us one undo event per nodepath
2022         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2024         // if the entire nodepath is removed, delete the selected object.
2025         if (nodepath->subpaths == NULL ||
2026             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2027             //at least 2
2028             sp_nodepath_get_node_count(nodepath) < 2) {
2029             SPDocument *document = sp_desktop_document (nodepath->desktop);
2030             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2031             //delete this nodepath's object, not the entire selection! (though at this time, this
2032             //does not matter)
2033             sp_selection_delete();
2034             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2035                               _("Delete nodes"));
2036         } else {
2037             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2038             sp_nodepath_update_statusbar(nodepath);
2039         }
2040     }
2042     g_slist_free (nodepaths);
2045 /**
2046  * Delete one or more selected nodes.
2047  */
2048 void sp_node_selected_delete()
2050     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2051     if (!nodepath) return;
2052     if (!nodepath->selected) return;
2054     /** \todo fixme: do it the right way */
2055     while (nodepath->selected) {
2056        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2057         sp_nodepath_node_destroy(node);
2058     }
2061     //clean up the nodepath (such as for trivial subpaths)
2062     sp_nodepath_cleanup(nodepath);
2064     sp_nodepath_update_handles(nodepath);
2066     // if the entire nodepath is removed, delete the selected object.
2067     if (nodepath->subpaths == NULL ||
2068         sp_nodepath_get_node_count(nodepath) < 2) {
2069         SPDocument *document = sp_desktop_document (nodepath->desktop);
2070         sp_selection_delete();
2071         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2072                           _("Delete nodes"));
2073         return;
2074     }
2076     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2078     sp_nodepath_update_statusbar(nodepath);
2081 /**
2082  * Delete one or more segments between two selected nodes.
2083  * This is the code for 'split'.
2084  */
2085 void
2086 sp_node_selected_delete_segment(void)
2088    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2089    Inkscape::NodePath::Node *curr, *next;     //Iterators
2091     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2092     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2094     if (g_list_length(nodepath->selected) != 2) {
2095         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2096                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2097         return;
2098     }
2100     //Selected nodes, not inclusive
2101    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2102    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2104     if ( ( a==b)                       ||  //same node
2105          (a->subpath  != b->subpath )  ||  //not the same path
2106          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2107          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2108     {
2109         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2110                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2111         return;
2112     }
2114     //###########################################
2115     //# BEGIN EDITS
2116     //###########################################
2117     //##################################
2118     //# CLOSED PATH
2119     //##################################
2120     if (a->subpath->closed) {
2123         gboolean reversed = FALSE;
2125         //Since we can go in a circle, we need to find the shorter distance.
2126         //  a->b or b->a
2127         start = end = NULL;
2128         int distance    = 0;
2129         int minDistance = 0;
2130         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2131             if (curr==b) {
2132                 //printf("a to b:%d\n", distance);
2133                 start = a;//go from a to b
2134                 end   = b;
2135                 minDistance = distance;
2136                 //printf("A to B :\n");
2137                 break;
2138             }
2139             distance++;
2140         }
2142         //try again, the other direction
2143         distance = 0;
2144         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2145             if (curr==a) {
2146                 //printf("b to a:%d\n", distance);
2147                 if (distance < minDistance) {
2148                     start    = b;  //we go from b to a
2149                     end      = a;
2150                     reversed = TRUE;
2151                     //printf("B to A\n");
2152                 }
2153                 break;
2154             }
2155             distance++;
2156         }
2159         //Copy everything from 'end' to 'start' to a new subpath
2160        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2161         for (curr=end ; curr ; curr=curr->n.other) {
2162             NRPathcode code = (NRPathcode) curr->code;
2163             if (curr == end)
2164                 code = NR_MOVETO;
2165             sp_nodepath_node_new(t, NULL,
2166                                  (Inkscape::NodePath::NodeType)curr->type, code,
2167                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2168             if (curr == start)
2169                 break;
2170         }
2171         sp_nodepath_subpath_destroy(a->subpath);
2174     }
2178     //##################################
2179     //# OPEN PATH
2180     //##################################
2181     else {
2183         //We need to get the direction of the list between A and B
2184         //Can we walk from a to b?
2185         start = end = NULL;
2186         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2187             if (curr==b) {
2188                 start = a;  //did it!  we go from a to b
2189                 end   = b;
2190                 //printf("A to B\n");
2191                 break;
2192             }
2193         }
2194         if (!start) {//didn't work?  let's try the other direction
2195             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2196                 if (curr==a) {
2197                     start = b;  //did it!  we go from b to a
2198                     end   = a;
2199                     //printf("B to A\n");
2200                     break;
2201                 }
2202             }
2203         }
2204         if (!start) {
2205             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2206                                                      _("Cannot find path between nodes."));
2207             return;
2208         }
2212         //Copy everything after 'end' to a new subpath
2213        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2214         for (curr=end ; curr ; curr=curr->n.other) {
2215             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2216                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2217         }
2219         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2220         for (curr = start->n.other ; curr  ; curr=next) {
2221             next = curr->n.other;
2222             sp_nodepath_node_destroy(curr);
2223         }
2225     }
2226     //###########################################
2227     //# END EDITS
2228     //###########################################
2230     //clean up the nodepath (such as for trivial subpaths)
2231     sp_nodepath_cleanup(nodepath);
2233     sp_nodepath_update_handles(nodepath);
2235     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2237     sp_nodepath_update_statusbar(nodepath);
2240 /**
2241  * Call sp_nodepath_set_line() for all selected segments.
2242  */
2243 void
2244 sp_node_selected_set_line_type(NRPathcode code)
2246     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2247     if (nodepath == NULL) return;
2249     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2250        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2251         g_assert(n->selected);
2252         if (n->p.other && n->p.other->selected) {
2253             sp_nodepath_set_line_type(n, code);
2254         }
2255     }
2257     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2260 /**
2261  * Call sp_nodepath_convert_node_type() for all selected nodes.
2262  */
2263 void
2264 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2266     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2267     if (nodepath == NULL) return;
2269     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2270         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2271     }
2273     sp_nodepath_update_repr(nodepath, _("Change node type"));
2276 /**
2277  * Change select status of node, update its own and neighbour handles.
2278  */
2279 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2281     node->selected = selected;
2283     if (selected) {
2284         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2285         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2286         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2287         sp_knot_update_ctrl(node->knot);
2288     } else {
2289         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2290         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2291         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2292         sp_knot_update_ctrl(node->knot);
2293     }
2295     sp_node_update_handles(node);
2296     if (node->n.other) sp_node_update_handles(node->n.other);
2297     if (node->p.other) sp_node_update_handles(node->p.other);
2300 /**
2301 \brief Select a node
2302 \param node     The node to select
2303 \param incremental   If true, add to selection, otherwise deselect others
2304 \param override   If true, always select this node, otherwise toggle selected status
2305 */
2306 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2308     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2310     if (incremental) {
2311         if (override) {
2312             if (!g_list_find(nodepath->selected, node)) {
2313                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2314             }
2315             sp_node_set_selected(node, TRUE);
2316         } else { // toggle
2317             if (node->selected) {
2318                 g_assert(g_list_find(nodepath->selected, node));
2319                 nodepath->selected = g_list_remove(nodepath->selected, node);
2320             } else {
2321                 g_assert(!g_list_find(nodepath->selected, node));
2322                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2323             }
2324             sp_node_set_selected(node, !node->selected);
2325         }
2326     } else {
2327         sp_nodepath_deselect(nodepath);
2328         nodepath->selected = g_list_prepend(nodepath->selected, node);
2329         sp_node_set_selected(node, TRUE);
2330     }
2332     sp_nodepath_update_statusbar(nodepath);
2336 /**
2337 \brief Deselect all nodes in the nodepath
2338 */
2339 void
2340 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2342     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2344     while (nodepath->selected) {
2345         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2346         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2347     }
2348     sp_nodepath_update_statusbar(nodepath);
2351 /**
2352 \brief Select or invert selection of all nodes in the nodepath
2353 */
2354 void
2355 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2357     if (!nodepath) return;
2359     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2360        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2361         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2362            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2363            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2364         }
2365     }
2368 /**
2369  * If nothing selected, does the same as sp_nodepath_select_all();
2370  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2371  * (i.e., similar to "select all in layer", with the "selected" subpaths
2372  * being treated as "layers" in the path).
2373  */
2374 void
2375 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2377     if (!nodepath) return;
2379     if (g_list_length (nodepath->selected) == 0) {
2380         sp_nodepath_select_all (nodepath, invert);
2381         return;
2382     }
2384     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2385     GSList *subpaths = NULL;
2387     for (GList *l = copy; l != NULL; l = l->next) {
2388         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2389         Inkscape::NodePath::SubPath *subpath = n->subpath;
2390         if (!g_slist_find (subpaths, subpath))
2391             subpaths = g_slist_prepend (subpaths, subpath);
2392     }
2394     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2395         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2396         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2397             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2398             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2399         }
2400     }
2402     g_slist_free (subpaths);
2403     g_list_free (copy);
2406 /**
2407  * \brief Select the node after the last selected; if none is selected,
2408  * select the first within path.
2409  */
2410 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2412     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2414    Inkscape::NodePath::Node *last = NULL;
2415     if (nodepath->selected) {
2416         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2417            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2418             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2419             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2420                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2421                 if (node->selected) {
2422                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2423                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2424                             if (spl->next) { // there's a next subpath
2425                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2426                                 last = subpath_next->first;
2427                             } else if (spl->prev) { // there's a previous subpath
2428                                 last = NULL; // to be set later to the first node of first subpath
2429                             } else {
2430                                 last = node->n.other;
2431                             }
2432                         } else {
2433                             last = node->n.other;
2434                         }
2435                     } else {
2436                         if (node->n.other) {
2437                             last = node->n.other;
2438                         } else {
2439                             if (spl->next) { // there's a next subpath
2440                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2441                                 last = subpath_next->first;
2442                             } else if (spl->prev) { // there's a previous subpath
2443                                 last = NULL; // to be set later to the first node of first subpath
2444                             } else {
2445                                 last = (Inkscape::NodePath::Node *) subpath->first;
2446                             }
2447                         }
2448                     }
2449                 }
2450             }
2451         }
2452         sp_nodepath_deselect(nodepath);
2453     }
2455     if (last) { // there's at least one more node after selected
2456         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2457     } else { // no more nodes, select the first one in first subpath
2458        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2459         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2460     }
2463 /**
2464  * \brief Select the node before the first selected; if none is selected,
2465  * select the last within path
2466  */
2467 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2469     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2471    Inkscape::NodePath::Node *last = NULL;
2472     if (nodepath->selected) {
2473         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2474            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2475             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2476                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2477                 if (node->selected) {
2478                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2479                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2480                             if (spl->prev) { // there's a prev subpath
2481                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2482                                 last = subpath_prev->last;
2483                             } else if (spl->next) { // there's a next subpath
2484                                 last = NULL; // to be set later to the last node of last subpath
2485                             } else {
2486                                 last = node->p.other;
2487                             }
2488                         } else {
2489                             last = node->p.other;
2490                         }
2491                     } else {
2492                         if (node->p.other) {
2493                             last = node->p.other;
2494                         } else {
2495                             if (spl->prev) { // there's a prev subpath
2496                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2497                                 last = subpath_prev->last;
2498                             } else if (spl->next) { // there's a next subpath
2499                                 last = NULL; // to be set later to the last node of last subpath
2500                             } else {
2501                                 last = (Inkscape::NodePath::Node *) subpath->last;
2502                             }
2503                         }
2504                     }
2505                 }
2506             }
2507         }
2508         sp_nodepath_deselect(nodepath);
2509     }
2511     if (last) { // there's at least one more node before selected
2512         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2513     } else { // no more nodes, select the last one in last subpath
2514         GList *spl = g_list_last(nodepath->subpaths);
2515        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2516         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2517     }
2520 /**
2521  * \brief Select all nodes that are within the rectangle.
2522  */
2523 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2525     if (!incremental) {
2526         sp_nodepath_deselect(nodepath);
2527     }
2529     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2530        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2531         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2532            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2534             if (b.contains(node->pos)) {
2535                 sp_nodepath_node_select(node, TRUE, TRUE);
2536             }
2537         }
2538     }
2542 void
2543 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2545     g_assert (n);
2546     g_assert (nodepath);
2547     g_assert (n->subpath->nodepath == nodepath);
2549     if (g_list_length (nodepath->selected) == 0) {
2550         if (grow > 0) {
2551             sp_nodepath_node_select(n, TRUE, TRUE);
2552         }
2553         return;
2554     }
2556     if (g_list_length (nodepath->selected) == 1) {
2557         if (grow < 0) {
2558             sp_nodepath_deselect (nodepath);
2559             return;
2560         }
2561     }
2563         double n_sel_range = 0, p_sel_range = 0;
2564             Inkscape::NodePath::Node *farthest_n_node = n;
2565             Inkscape::NodePath::Node *farthest_p_node = n;
2567         // Calculate ranges
2568         {
2569             double n_range = 0, p_range = 0;
2570             bool n_going = true, p_going = true;
2571             Inkscape::NodePath::Node *n_node = n;
2572             Inkscape::NodePath::Node *p_node = n;
2573             do {
2574                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2575                 if (n_node && n_going)
2576                     n_node = n_node->n.other;
2577                 if (n_node == NULL) {
2578                     n_going = false;
2579                 } else {
2580                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2581                     if (n_node->selected) {
2582                         n_sel_range = n_range;
2583                         farthest_n_node = n_node;
2584                     }
2585                     if (n_node == p_node) {
2586                         n_going = false;
2587                         p_going = false;
2588                     }
2589                 }
2590                 if (p_node && p_going)
2591                     p_node = p_node->p.other;
2592                 if (p_node == NULL) {
2593                     p_going = false;
2594                 } else {
2595                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2596                     if (p_node->selected) {
2597                         p_sel_range = p_range;
2598                         farthest_p_node = p_node;
2599                     }
2600                     if (p_node == n_node) {
2601                         n_going = false;
2602                         p_going = false;
2603                     }
2604                 }
2605             } while (n_going || p_going);
2606         }
2608     if (grow > 0) {
2609         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2610                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2611         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2612                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2613         }
2614     } else {
2615         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2616                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2617         } else if (farthest_p_node && farthest_p_node->selected) {
2618                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2619         }
2620     }
2623 void
2624 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2626     g_assert (n);
2627     g_assert (nodepath);
2628     g_assert (n->subpath->nodepath == nodepath);
2630     if (g_list_length (nodepath->selected) == 0) {
2631         if (grow > 0) {
2632             sp_nodepath_node_select(n, TRUE, TRUE);
2633         }
2634         return;
2635     }
2637     if (g_list_length (nodepath->selected) == 1) {
2638         if (grow < 0) {
2639             sp_nodepath_deselect (nodepath);
2640             return;
2641         }
2642     }
2644     Inkscape::NodePath::Node *farthest_selected = NULL;
2645     double farthest_dist = 0;
2647     Inkscape::NodePath::Node *closest_unselected = NULL;
2648     double closest_dist = NR_HUGE;
2650     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2651        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2652         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2653            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2654            if (node == n)
2655                continue;
2656            if (node->selected) {
2657                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2658                    farthest_dist = NR::L2(node->pos - n->pos);
2659                    farthest_selected = node;
2660                }
2661            } else {
2662                if (NR::L2(node->pos - n->pos) < closest_dist) {
2663                    closest_dist = NR::L2(node->pos - n->pos);
2664                    closest_unselected = node;
2665                }
2666            }
2667         }
2668     }
2670     if (grow > 0) {
2671         if (closest_unselected) {
2672             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2673         }
2674     } else {
2675         if (farthest_selected) {
2676             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2677         }
2678     }
2682 /**
2683 \brief  Saves all nodes' and handles' current positions in their origin members
2684 */
2685 void
2686 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2688     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2689        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2690         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2691            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2692            n->origin = n->pos;
2693            n->p.origin = n->p.pos;
2694            n->n.origin = n->n.pos;
2695         }
2696     }
2697
2699 /**
2700 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2701 */
2702 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2704     if (!nodepath->selected) {
2705         return NULL;
2706     }
2708     GList *r = NULL;
2709     guint i = 0;
2710     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2711        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2712         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2713            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2714             i++;
2715             if (node->selected) {
2716                 r = g_list_append(r, GINT_TO_POINTER(i));
2717             }
2718         }
2719     }
2720     return r;
2723 /**
2724 \brief  Restores selection by selecting nodes whose positions are in the list
2725 */
2726 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2728     sp_nodepath_deselect(nodepath);
2730     guint i = 0;
2731     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2732        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2733         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2734            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2735             i++;
2736             if (g_list_find(r, GINT_TO_POINTER(i))) {
2737                 sp_nodepath_node_select(node, TRUE, TRUE);
2738             }
2739         }
2740     }
2744 /**
2745 \brief Adjusts handle according to node type and line code.
2746 */
2747 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2749     double len, otherlen, linelen;
2751     g_assert(node);
2753    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2754    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2756     /** \todo fixme: */
2757     if (me->other == NULL) return;
2758     if (other->other == NULL) return;
2760     /* I have line */
2762     NRPathcode mecode, ocode;
2763     if (which_adjust == 1) {
2764         mecode = (NRPathcode)me->other->code;
2765         ocode = (NRPathcode)node->code;
2766     } else {
2767         mecode = (NRPathcode)node->code;
2768         ocode = (NRPathcode)other->other->code;
2769     }
2771     if (mecode == NR_LINETO) return;
2773     /* I am curve */
2775     if (other->other == NULL) return;
2777     /* Other has line */
2779     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2781     NR::Point delta;
2782     if (ocode == NR_LINETO) {
2783         /* other is lineto, we are either smooth or symm */
2784        Inkscape::NodePath::Node *othernode = other->other;
2785         len = NR::L2(me->pos - node->pos);
2786         delta = node->pos - othernode->pos;
2787         linelen = NR::L2(delta);
2788         if (linelen < 1e-18) 
2789             return;
2790         me->pos = node->pos + (len / linelen)*delta;
2791         return;
2792     }
2794     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2796         me->pos = 2 * node->pos - other->pos;
2797         return;
2798     }
2800     /* We are smooth */
2802     len = NR::L2(me->pos - node->pos);
2803     delta = other->pos - node->pos;
2804     otherlen = NR::L2(delta);
2805     if (otherlen < 1e-18) return;
2807     me->pos = node->pos - (len / otherlen) * delta;
2810 /**
2811  \brief Adjusts both handles according to node type and line code
2812  */
2813 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2815     g_assert(node);
2817     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2819     /* we are either smooth or symm */
2821     if (node->p.other == NULL) return;
2823     if (node->n.other == NULL) return;
2825     if (node->code == NR_LINETO) {
2826         if (node->n.other->code == NR_LINETO) return;
2827         sp_node_adjust_handle(node, 1);
2828         return;
2829     }
2831     if (node->n.other->code == NR_LINETO) {
2832         if (node->code == NR_LINETO) return;
2833         sp_node_adjust_handle(node, -1);
2834         return;
2835     }
2837     /* both are curves */
2838     NR::Point const delta( node->n.pos - node->p.pos );
2840     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2841         node->p.pos = node->pos - delta / 2;
2842         node->n.pos = node->pos + delta / 2;
2843         return;
2844     }
2846     /* We are smooth */
2847     double plen = NR::L2(node->p.pos - node->pos);
2848     if (plen < 1e-18) return;
2849     double nlen = NR::L2(node->n.pos - node->pos);
2850     if (nlen < 1e-18) return;
2851     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2852     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2855 /**
2856  * Node event callback.
2857  */
2858 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2860     gboolean ret = FALSE;
2861     switch (event->type) {
2862         case GDK_ENTER_NOTIFY:
2863             active_node = n;
2864             break;
2865         case GDK_LEAVE_NOTIFY:
2866             active_node = NULL;
2867             break;
2868         case GDK_KEY_PRESS:
2869             switch (get_group0_keyval (&event->key)) {
2870                 case GDK_space:
2871                     if (event->key.state & GDK_BUTTON1_MASK) {
2872                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2873                         stamp_repr(nodepath);
2874                         ret = TRUE;
2875                     }
2876                     break;
2877                 case GDK_Page_Up:
2878                     if (event->key.state & GDK_CONTROL_MASK) {
2879                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2880                     } else {
2881                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2882                     }
2883                     break;
2884                 case GDK_Page_Down:
2885                     if (event->key.state & GDK_CONTROL_MASK) {
2886                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2887                     } else {
2888                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2889                     }
2890                     break;
2891                 default:
2892                     break;
2893             }
2894             break;
2895         default:
2896             break;
2897     }
2899     return ret;
2902 /**
2903  * Handle keypress on node; directly called.
2904  */
2905 gboolean node_key(GdkEvent *event)
2907     Inkscape::NodePath::Path *np;
2909     // there is no way to verify nodes so set active_node to nil when deleting!!
2910     if (active_node == NULL) return FALSE;
2912     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2913         gint ret = FALSE;
2914         switch (get_group0_keyval (&event->key)) {
2915             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2916             case GDK_BackSpace:
2917                 np = active_node->subpath->nodepath;
2918                 sp_nodepath_node_destroy(active_node);
2919                 sp_nodepath_update_repr(np, _("Delete node"));
2920                 active_node = NULL;
2921                 ret = TRUE;
2922                 break;
2923             case GDK_c:
2924                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2925                 ret = TRUE;
2926                 break;
2927             case GDK_s:
2928                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2929                 ret = TRUE;
2930                 break;
2931             case GDK_y:
2932                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2933                 ret = TRUE;
2934                 break;
2935             case GDK_b:
2936                 sp_nodepath_node_break(active_node);
2937                 ret = TRUE;
2938                 break;
2939         }
2940         return ret;
2941     }
2942     return FALSE;
2945 /**
2946  * Mouseclick on node callback.
2947  */
2948 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2950    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2952     if (state & GDK_CONTROL_MASK) {
2953         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2955         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2956             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2957                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2958             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2959                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2960             } else {
2961                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2962             }
2963             sp_nodepath_update_repr(nodepath, _("Change node type"));
2964             sp_nodepath_update_statusbar(nodepath);
2966         } else { //ctrl+alt+click: delete node
2967             GList *node_to_delete = NULL;
2968             node_to_delete = g_list_append(node_to_delete, n);
2969             sp_node_delete_preserve(node_to_delete);
2970         }
2972     } else {
2973         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2974     }
2977 /**
2978  * Mouse grabbed node callback.
2979  */
2980 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2982    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2984     if (!n->selected) {
2985         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2986     }
2988     n->is_dragging = true;
2990     sp_nodepath_remember_origins (n->subpath->nodepath);
2993 /**
2994  * Mouse ungrabbed node callback.
2995  */
2996 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2998    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3000    n->dragging_out = NULL;
3001    n->is_dragging = false;
3003    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3006 /**
3007  * The point on a line, given by its angle, closest to the given point.
3008  * \param p  A point.
3009  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3010  * \param closest  Pointer to the point struct where the result is stored.
3011  * \todo FIXME: use dot product perhaps?
3012  */
3013 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3015     if (a == HUGE_VAL) { // vertical
3016         *closest = NR::Point(0, (*p)[NR::Y]);
3017     } else {
3018         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3019         (*closest)[NR::Y] = a * (*closest)[NR::X];
3020     }
3023 /**
3024  * Distance from the point to a line given by its angle.
3025  * \param p  A point.
3026  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3027  */
3028 static double point_line_distance(NR::Point *p, double a)
3030     NR::Point c;
3031     point_line_closest(p, a, &c);
3032     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]));
3035 /**
3036  * Callback for node "request" signal.
3037  * \todo fixme: This goes to "moved" event? (lauris)
3038  */
3039 static gboolean
3040 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3042     double yn, xn, yp, xp;
3043     double an, ap, na, pa;
3044     double d_an, d_ap, d_na, d_pa;
3045     gboolean collinear = FALSE;
3046     NR::Point c;
3047     NR::Point pr;
3049    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3051    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3052    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3054        NR::Point mouse = (*p);
3056        if (!n->dragging_out) {
3057            // This is the first drag-out event; find out which handle to drag out
3058            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3059            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3061            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3062                return FALSE;
3064            Inkscape::NodePath::NodeSide *opposite;
3065            if (appr_p > appr_n) { // closer to p
3066                n->dragging_out = &n->p;
3067                opposite = &n->n;
3068                n->code = NR_CURVETO;
3069            } else if (appr_p < appr_n) { // closer to n
3070                n->dragging_out = &n->n;
3071                opposite = &n->p;
3072                n->n.other->code = NR_CURVETO;
3073            } else { // p and n nodes are the same
3074                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3075                    n->dragging_out = &n->p;
3076                    opposite = &n->n;
3077                    n->code = NR_CURVETO;
3078                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3079                    n->dragging_out = &n->n;
3080                    opposite = &n->p;
3081                    n->n.other->code = NR_CURVETO;
3082                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3083                    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);
3084                    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);
3085                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3086                        n->dragging_out = &n->n;
3087                        opposite = &n->p;
3088                        n->n.other->code = NR_CURVETO;
3089                    } else { // closer to other's n handle
3090                        n->dragging_out = &n->p;
3091                        opposite = &n->n;
3092                        n->code = NR_CURVETO;
3093                    }
3094                }
3095            }
3097            // if there's another handle, make sure the one we drag out starts parallel to it
3098            if (opposite->pos != n->pos) {
3099                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3100            }
3102            // knots might not be created yet!
3103            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3104            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3105        }
3107        // pass this on to the handle-moved callback
3108        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3109        sp_node_update_handles(n);
3110        return TRUE;
3111    }
3113     if (state & GDK_CONTROL_MASK) { // constrained motion
3115         // calculate relative distances of handles
3116         // n handle:
3117         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3118         xn = n->n.pos[NR::X] - n->pos[NR::X];
3119         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3120         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3121             if (n->n.other) { // if there is the next point
3122                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3123                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3124                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3125             }
3126         }
3127         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3128         if (yn < 0) { xn = -xn; yn = -yn; }
3130         // p handle:
3131         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3132         xp = n->p.pos[NR::X] - n->pos[NR::X];
3133         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3134         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3135             if (n->p.other) {
3136                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3137                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3138                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3139             }
3140         }
3141         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3142         if (yp < 0) { xp = -xp; yp = -yp; }
3144         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3145             // sliding on handles, only if at least one of the handles is non-vertical
3146             // (otherwise it's the same as ctrl+drag anyway)
3148             // calculate angles of the handles
3149             if (xn == 0) {
3150                 if (yn == 0) { // no handle, consider it the continuation of the other one
3151                     an = 0;
3152                     collinear = TRUE;
3153                 }
3154                 else an = 0; // vertical; set the angle to horizontal
3155             } else an = yn/xn;
3157             if (xp == 0) {
3158                 if (yp == 0) { // no handle, consider it the continuation of the other one
3159                     ap = an;
3160                 }
3161                 else ap = 0; // vertical; set the angle to horizontal
3162             } else  ap = yp/xp;
3164             if (collinear) an = ap;
3166             // angles of the perpendiculars; HUGE_VAL means vertical
3167             if (an == 0) na = HUGE_VAL; else na = -1/an;
3168             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3170             // mouse point relative to the node's original pos
3171             pr = (*p) - n->origin;
3173             // distances to the four lines (two handles and two perpendiculars)
3174             d_an = point_line_distance(&pr, an);
3175             d_na = point_line_distance(&pr, na);
3176             d_ap = point_line_distance(&pr, ap);
3177             d_pa = point_line_distance(&pr, pa);
3179             // find out which line is the closest, save its closest point in c
3180             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3181                 point_line_closest(&pr, an, &c);
3182             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3183                 point_line_closest(&pr, ap, &c);
3184             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3185                 point_line_closest(&pr, na, &c);
3186             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3187                 point_line_closest(&pr, pa, &c);
3188             }
3190             // move the node to the closest point
3191             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3192                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3193                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3195         } else {  // constraining to hor/vert
3197             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3198                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3199             } else { // snap to vert
3200                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3201             }
3202         }
3203     } else { // move freely
3204         if (n->is_dragging) {
3205             if (state & GDK_MOD1_MASK) { // sculpt
3206                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3207             } else {
3208                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3209                                             (*p)[NR::X] - n->pos[NR::X],
3210                                             (*p)[NR::Y] - n->pos[NR::Y],
3211                                             (state & GDK_SHIFT_MASK) == 0);
3212             }
3213         }
3214     }
3216     n->subpath->nodepath->desktop->scroll_to_point(p);
3218     return TRUE;
3221 /**
3222  * Node handle clicked callback.
3223  */
3224 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3226    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3228     if (state & GDK_CONTROL_MASK) { // "delete" handle
3229         if (n->p.knot == knot) {
3230             n->p.pos = n->pos;
3231         } else if (n->n.knot == knot) {
3232             n->n.pos = n->pos;
3233         }
3234         sp_node_update_handles(n);
3235         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3236         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3237         sp_nodepath_update_statusbar(nodepath);
3239     } else { // just select or add to selection, depending in Shift
3240         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3241     }
3244 /**
3245  * Node handle grabbed callback.
3246  */
3247 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3249    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3251     if (!n->selected) {
3252         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3253     }
3255     // remember the origin point of the handle
3256     if (n->p.knot == knot) {
3257         n->p.origin_radial = n->p.pos - n->pos;
3258     } else if (n->n.knot == knot) {
3259         n->n.origin_radial = n->n.pos - n->pos;
3260     } else {
3261         g_assert_not_reached();
3262     }
3266 /**
3267  * Node handle ungrabbed callback.
3268  */
3269 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3271    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3273     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3274     if (n->p.knot == knot) {
3275         n->p.origin_radial.a = 0;
3276         sp_knot_set_position(knot, &n->p.pos, state);
3277     } else if (n->n.knot == knot) {
3278         n->n.origin_radial.a = 0;
3279         sp_knot_set_position(knot, &n->n.pos, state);
3280     } else {
3281         g_assert_not_reached();
3282     }
3284     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3287 /**
3288  * Node handle "request" signal callback.
3289  */
3290 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3292     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3294     Inkscape::NodePath::NodeSide *me, *opposite;
3295     gint which;
3296     if (n->p.knot == knot) {
3297         me = &n->p;
3298         opposite = &n->n;
3299         which = -1;
3300     } else if (n->n.knot == knot) {
3301         me = &n->n;
3302         opposite = &n->p;
3303         which = 1;
3304     } else {
3305         me = opposite = NULL;
3306         which = 0;
3307         g_assert_not_reached();
3308     }
3310     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3312     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3314     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3315         /* We are smooth node adjacent with line */
3316         NR::Point const delta = *p - n->pos;
3317         NR::Coord const len = NR::L2(delta);
3318         Inkscape::NodePath::Node *othernode = opposite->other;
3319         NR::Point const ndelta = n->pos - othernode->pos;
3320         NR::Coord const linelen = NR::L2(ndelta);
3321         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3322             NR::Coord const scal = dot(delta, ndelta) / linelen;
3323             (*p) = n->pos + (scal / linelen) * ndelta;
3324         }
3325         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3326     } else {
3327         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3328     }
3330     sp_node_adjust_handle(n, -which);
3332     return FALSE;
3335 /**
3336  * Node handle moved callback.
3337  */
3338 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3340    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3342    Inkscape::NodePath::NodeSide *me;
3343    Inkscape::NodePath::NodeSide *other;
3344     if (n->p.knot == knot) {
3345         me = &n->p;
3346         other = &n->n;
3347     } else if (n->n.knot == knot) {
3348         me = &n->n;
3349         other = &n->p;
3350     } else {
3351         me = NULL;
3352         other = NULL;
3353         g_assert_not_reached();
3354     }
3356     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3357     Radial rme(me->pos - n->pos);
3358     Radial rother(other->pos - n->pos);
3359     Radial rnew(*p - n->pos);
3361     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3362         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3363         /* 0 interpreted as "no snapping". */
3365         // The closest PI/snaps angle, starting from zero.
3366         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3367         if (me->origin_radial.a == HUGE_VAL) {
3368             // ortho doesn't exist: original handle was zero length.
3369             rnew.a = a_snapped;
3370         } else {
3371             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3372              * its opposite and perpendiculars). */
3373             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3375             // Snap to the closest.
3376             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3377                        ? a_snapped
3378                        : a_ortho );
3379         }
3380     }
3382     if (state & GDK_MOD1_MASK) {
3383         // lock handle length
3384         rnew.r = me->origin_radial.r;
3385     }
3387     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3388         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3389         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3390         rother.a += rnew.a - rme.a;
3391         other->pos = NR::Point(rother) + n->pos;
3392         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3393         sp_knot_set_position(other->knot, &other->pos, 0);
3394     }
3396     me->pos = NR::Point(rnew) + n->pos;
3397     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3399     // this is what sp_knot_set_position does, but without emitting the signal:
3400     // we cannot emit a "moved" signal because we're now processing it
3401     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3403     knot->desktop->set_coordinate_status(me->pos);
3405     update_object(n->subpath->nodepath);
3407     /* status text */
3408     SPDesktop *desktop = n->subpath->nodepath->desktop;
3409     if (!desktop) return;
3410     SPEventContext *ec = desktop->event_context;
3411     if (!ec) return;
3412     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3413     if (!mc) return;
3415     double degrees = 180 / M_PI * rnew.a;
3416     if (degrees > 180) degrees -= 360;
3417     if (degrees < -180) degrees += 360;
3418     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3419         degrees = angle_to_compass (degrees);
3421     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3423     mc->setF(Inkscape::NORMAL_MESSAGE,
3424          _("<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);
3426     g_string_free(length, TRUE);
3429 /**
3430  * Node handle event callback.
3431  */
3432 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3434     gboolean ret = FALSE;
3435     switch (event->type) {
3436         case GDK_KEY_PRESS:
3437             switch (get_group0_keyval (&event->key)) {
3438                 case GDK_space:
3439                     if (event->key.state & GDK_BUTTON1_MASK) {
3440                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3441                         stamp_repr(nodepath);
3442                         ret = TRUE;
3443                     }
3444                     break;
3445                 default:
3446                     break;
3447             }
3448             break;
3449         default:
3450             break;
3451     }
3453     return ret;
3456 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3457                                  Radial &rme, Radial &rother, gboolean const both)
3459     rme.a += angle;
3460     if ( both
3461          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3462          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3463     {
3464         rother.a += angle;
3465     }
3468 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3469                                         Radial &rme, Radial &rother, gboolean const both)
3471     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3473     gdouble r;
3474     if ( both
3475          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3476          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3477     {
3478         r = MAX(rme.r, rother.r);
3479     } else {
3480         r = rme.r;
3481     }
3483     gdouble const weird_angle = atan2(norm_angle, r);
3484 /* Bulia says norm_angle is just the visible distance that the
3485  * object's end must travel on the screen.  Left as 'angle' for want of
3486  * a better name.*/
3488     rme.a += weird_angle;
3489     if ( both
3490          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3491          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3492     {
3493         rother.a += weird_angle;
3494     }
3497 /**
3498  * Rotate one node.
3499  */
3500 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3502     Inkscape::NodePath::NodeSide *me, *other;
3503     bool both = false;
3505     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3506     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3508     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3509         me = &(n->p);
3510         other = &(n->n);
3511     } else if (!n->p.other) {
3512         me = &(n->n);
3513         other = &(n->p);
3514     } else {
3515         if (which > 0) { // right handle
3516             if (xn > xp) {
3517                 me = &(n->n);
3518                 other = &(n->p);
3519             } else {
3520                 me = &(n->p);
3521                 other = &(n->n);
3522             }
3523         } else if (which < 0){ // left handle
3524             if (xn <= xp) {
3525                 me = &(n->n);
3526                 other = &(n->p);
3527             } else {
3528                 me = &(n->p);
3529                 other = &(n->n);
3530             }
3531         } else { // both handles
3532             me = &(n->n);
3533             other = &(n->p);
3534             both = true;
3535         }
3536     }
3538     Radial rme(me->pos - n->pos);
3539     Radial rother(other->pos - n->pos);
3541     if (screen) {
3542         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3543     } else {
3544         node_rotate_one_internal (*n, angle, rme, rother, both);
3545     }
3547     me->pos = n->pos + NR::Point(rme);
3549     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3550         other->pos =  n->pos + NR::Point(rother);
3551     }
3553     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3554     // so here we just move all the knots without emitting move signals, for speed
3555     sp_node_update_handles(n, false);
3558 /**
3559  * Rotate selected nodes.
3560  */
3561 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3563     if (!nodepath || !nodepath->selected) return;
3565     if (g_list_length(nodepath->selected) == 1) {
3566        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3567         node_rotate_one (n, angle, which, screen);
3568     } else {
3569        // rotate as an object:
3571         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3572         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3573         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3574             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3575             box.expandTo (n->pos); // contain all selected nodes
3576         }
3578         gdouble rot;
3579         if (screen) {
3580             gdouble const zoom = nodepath->desktop->current_zoom();
3581             gdouble const zmove = angle / zoom;
3582             gdouble const r = NR::L2(box.max() - box.midpoint());
3583             rot = atan2(zmove, r);
3584         } else {
3585             rot = angle;
3586         }
3588         NR::Matrix t =
3589             NR::Matrix (NR::translate(-box.midpoint())) *
3590             NR::Matrix (NR::rotate(rot)) *
3591             NR::Matrix (NR::translate(box.midpoint()));
3593         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3594             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3595             n->pos *= t;
3596             n->n.pos *= t;
3597             n->p.pos *= t;
3598             sp_node_update_handles(n, false);
3599         }
3600     }
3602     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3605 /**
3606  * Scale one node.
3607  */
3608 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3610     bool both = false;
3611     Inkscape::NodePath::NodeSide *me, *other;
3613     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3614     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3616     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3617         me = &(n->p);
3618         other = &(n->n);
3619         n->code = NR_CURVETO;
3620     } else if (!n->p.other) {
3621         me = &(n->n);
3622         other = &(n->p);
3623         if (n->n.other)
3624             n->n.other->code = NR_CURVETO;
3625     } else {
3626         if (which > 0) { // right handle
3627             if (xn > xp) {
3628                 me = &(n->n);
3629                 other = &(n->p);
3630                 if (n->n.other)
3631                     n->n.other->code = NR_CURVETO;
3632             } else {
3633                 me = &(n->p);
3634                 other = &(n->n);
3635                 n->code = NR_CURVETO;
3636             }
3637         } else if (which < 0){ // left handle
3638             if (xn <= xp) {
3639                 me = &(n->n);
3640                 other = &(n->p);
3641                 if (n->n.other)
3642                     n->n.other->code = NR_CURVETO;
3643             } else {
3644                 me = &(n->p);
3645                 other = &(n->n);
3646                 n->code = NR_CURVETO;
3647             }
3648         } else { // both handles
3649             me = &(n->n);
3650             other = &(n->p);
3651             both = true;
3652             n->code = NR_CURVETO;
3653             if (n->n.other)
3654                 n->n.other->code = NR_CURVETO;
3655         }
3656     }
3658     Radial rme(me->pos - n->pos);
3659     Radial rother(other->pos - n->pos);
3661     rme.r += grow;
3662     if (rme.r < 0) rme.r = 0;
3663     if (rme.a == HUGE_VAL) {
3664         if (me->other) { // if direction is unknown, initialize it towards the next node
3665             Radial rme_next(me->other->pos - n->pos);
3666             rme.a = rme_next.a;
3667         } else { // if there's no next, initialize to 0
3668             rme.a = 0;
3669         }
3670     }
3671     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3672         rother.r += grow;
3673         if (rother.r < 0) rother.r = 0;
3674         if (rother.a == HUGE_VAL) {
3675             rother.a = rme.a + M_PI;
3676         }
3677     }
3679     me->pos = n->pos + NR::Point(rme);
3681     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3682         other->pos = n->pos + NR::Point(rother);
3683     }
3685     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3686     // so here we just move all the knots without emitting move signals, for speed
3687     sp_node_update_handles(n, false);
3690 /**
3691  * Scale selected nodes.
3692  */
3693 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3695     if (!nodepath || !nodepath->selected) return;
3697     if (g_list_length(nodepath->selected) == 1) {
3698         // scale handles of the single selected node
3699         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3700         node_scale_one (n, grow, which);
3701     } else {
3702         // scale nodes as an "object":
3704         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3705         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3706         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3707             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3708             box.expandTo (n->pos); // contain all selected nodes
3709         }
3711         double scale = (box.maxExtent() + grow)/box.maxExtent();
3713         NR::Matrix t =
3714             NR::Matrix (NR::translate(-box.midpoint())) *
3715             NR::Matrix (NR::scale(scale, scale)) *
3716             NR::Matrix (NR::translate(box.midpoint()));
3718         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3719             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3720             n->pos *= t;
3721             n->n.pos *= t;
3722             n->p.pos *= t;
3723             sp_node_update_handles(n, false);
3724         }
3725     }
3727     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3730 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3732     if (!nodepath) return;
3733     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3736 /**
3737  * Flip selected nodes horizontally/vertically.
3738  */
3739 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3741     if (!nodepath || !nodepath->selected) return;
3743     if (g_list_length(nodepath->selected) == 1) {
3744         // flip handles of the single selected node
3745         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3746         double temp = n->p.pos[axis];
3747         n->p.pos[axis] = n->n.pos[axis];
3748         n->n.pos[axis] = temp;
3749         sp_node_update_handles(n, false);
3750     } else {
3751         // scale nodes as an "object":
3753         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3754         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3755         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3756             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3757             box.expandTo (n->pos); // contain all selected nodes
3758         }
3760         NR::Matrix t =
3761             NR::Matrix (NR::translate(-box.midpoint())) *
3762             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3763             NR::Matrix (NR::translate(box.midpoint()));
3765         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3766             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3767             n->pos *= t;
3768             n->n.pos *= t;
3769             n->p.pos *= t;
3770             sp_node_update_handles(n, false);
3771         }
3772     }
3774     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3777 //-----------------------------------------------
3778 /**
3779  * Return new subpath under given nodepath.
3780  */
3781 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3783     g_assert(nodepath);
3784     g_assert(nodepath->desktop);
3786    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3788     s->nodepath = nodepath;
3789     s->closed = FALSE;
3790     s->nodes = NULL;
3791     s->first = NULL;
3792     s->last = NULL;
3794     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3795     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3796     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3798     return s;
3801 /**
3802  * Destroy nodes in subpath, then subpath itself.
3803  */
3804 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3806     g_assert(subpath);
3807     g_assert(subpath->nodepath);
3808     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3810     while (subpath->nodes) {
3811         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3812     }
3814     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3816     g_free(subpath);
3819 /**
3820  * Link head to tail in subpath.
3821  */
3822 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3824     g_assert(!sp->closed);
3825     g_assert(sp->last != sp->first);
3826     g_assert(sp->first->code == NR_MOVETO);
3828     sp->closed = TRUE;
3830     //Link the head to the tail
3831     sp->first->p.other = sp->last;
3832     sp->last->n.other  = sp->first;
3833     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3834     sp->first          = sp->last;
3836     //Remove the extra end node
3837     sp_nodepath_node_destroy(sp->last->n.other);
3840 /**
3841  * Open closed (loopy) subpath at node.
3842  */
3843 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3845     g_assert(sp->closed);
3846     g_assert(n->subpath == sp);
3847     g_assert(sp->first == sp->last);
3849     /* We create new startpoint, current node will become last one */
3851    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3852                                                 &n->pos, &n->pos, &n->n.pos);
3855     sp->closed        = FALSE;
3857     //Unlink to make a head and tail
3858     sp->first         = new_path;
3859     sp->last          = n;
3860     n->n.other        = NULL;
3861     new_path->p.other = NULL;
3864 /**
3865  * Returns area in triangle given by points; may be negative.
3866  */
3867 inline double
3868 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3870     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]);
3873 /**
3874  * Return new node in subpath with given properties.
3875  * \param pos Position of node.
3876  * \param ppos Handle position in previous direction
3877  * \param npos Handle position in previous direction
3878  */
3879 Inkscape::NodePath::Node *
3880 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)
3882     g_assert(sp);
3883     g_assert(sp->nodepath);
3884     g_assert(sp->nodepath->desktop);
3886     if (nodechunk == NULL)
3887         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3889     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3891     n->subpath  = sp;
3893     if (type != Inkscape::NodePath::NODE_NONE) {
3894         // use the type from sodipodi:nodetypes
3895         n->type = type;
3896     } else {
3897         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3898             // points are (almost) collinear
3899             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3900                 // endnode, or a node with a retracted handle
3901                 n->type = Inkscape::NodePath::NODE_CUSP;
3902             } else {
3903                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3904             }
3905         } else {
3906             n->type = Inkscape::NodePath::NODE_CUSP;
3907         }
3908     }
3910     n->code     = code;
3911     n->selected = FALSE;
3912     n->pos      = *pos;
3913     n->p.pos    = *ppos;
3914     n->n.pos    = *npos;
3916     n->dragging_out = NULL;
3918     Inkscape::NodePath::Node *prev;
3919     if (next) {
3920         //g_assert(g_list_find(sp->nodes, next));
3921         prev = next->p.other;
3922     } else {
3923         prev = sp->last;
3924     }
3926     if (prev)
3927         prev->n.other = n;
3928     else
3929         sp->first = n;
3931     if (next)
3932         next->p.other = n;
3933     else
3934         sp->last = n;
3936     n->p.other = prev;
3937     n->n.other = next;
3939     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"));
3940     sp_knot_set_position(n->knot, pos, 0);
3942     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3943     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3944     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3945     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3946     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3947     sp_knot_update_ctrl(n->knot);
3949     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3950     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3951     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3952     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3953     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3954     sp_knot_show(n->knot);
3956     // We only create handle knots and lines on demand
3957     n->p.knot = NULL;
3958     n->p.line = NULL;
3959     n->n.knot = NULL;
3960     n->n.line = NULL;
3962     sp->nodes = g_list_prepend(sp->nodes, n);
3964     return n;
3967 /**
3968  * Destroy node and its knots, link neighbors in subpath.
3969  */
3970 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3972     g_assert(node);
3973     g_assert(node->subpath);
3974     g_assert(SP_IS_KNOT(node->knot));
3976    Inkscape::NodePath::SubPath *sp = node->subpath;
3978     if (node->selected) { // first, deselect
3979         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3980         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3981     }
3983     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3985     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
3986     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
3987     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
3988     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
3989     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
3990     g_object_unref(G_OBJECT(node->knot));
3992     if (node->p.knot) {
3993         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
3994         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
3995         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
3996         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
3997         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
3998         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
3999         g_object_unref(G_OBJECT(node->p.knot));
4000     }
4002     if (node->n.knot) {
4003         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4004         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4005         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4006         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4007         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4008         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4009         g_object_unref(G_OBJECT(node->n.knot));
4010     }
4012     if (node->p.line)
4013         gtk_object_destroy(GTK_OBJECT(node->p.line));
4014     if (node->n.line)
4015         gtk_object_destroy(GTK_OBJECT(node->n.line));
4017     if (sp->nodes) { // there are others nodes on the subpath
4018         if (sp->closed) {
4019             if (sp->first == node) {
4020                 g_assert(sp->last == node);
4021                 sp->first = node->n.other;
4022                 sp->last = sp->first;
4023             }
4024             node->p.other->n.other = node->n.other;
4025             node->n.other->p.other = node->p.other;
4026         } else {
4027             if (sp->first == node) {
4028                 sp->first = node->n.other;
4029                 sp->first->code = NR_MOVETO;
4030             }
4031             if (sp->last == node) sp->last = node->p.other;
4032             if (node->p.other) node->p.other->n.other = node->n.other;
4033             if (node->n.other) node->n.other->p.other = node->p.other;
4034         }
4035     } else { // this was the last node on subpath
4036         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4037     }
4039     g_mem_chunk_free(nodechunk, node);
4042 /**
4043  * Returns one of the node's two sides.
4044  * \param which Indicates which side.
4045  * \return Pointer to previous node side if which==-1, next if which==1.
4046  */
4047 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4049     g_assert(node);
4051     switch (which) {
4052         case -1:
4053             return &node->p;
4054         case 1:
4055             return &node->n;
4056         default:
4057             break;
4058     }
4060     g_assert_not_reached();
4062     return NULL;
4065 /**
4066  * Return the other side of the node, given one of its sides.
4067  */
4068 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4070     g_assert(node);
4072     if (me == &node->p) return &node->n;
4073     if (me == &node->n) return &node->p;
4075     g_assert_not_reached();
4077     return NULL;
4080 /**
4081  * Return NRPathcode on the given side of the node.
4082  */
4083 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4085     g_assert(node);
4087     if (me == &node->p) {
4088         if (node->p.other) return (NRPathcode)node->code;
4089         return NR_MOVETO;
4090     }
4092     if (me == &node->n) {
4093         if (node->n.other) return (NRPathcode)node->n.other->code;
4094         return NR_MOVETO;
4095     }
4097     g_assert_not_reached();
4099     return NR_END;
4102 /**
4103  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4104  */
4105 Inkscape::NodePath::Node *
4106 sp_nodepath_get_node_by_index(int index)
4108     Inkscape::NodePath::Node *e = NULL;
4110     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4111     if (!nodepath) {
4112         return e;
4113     }
4115     //find segment
4116     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4118         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4119         int n = g_list_length(sp->nodes);
4120         if (sp->closed) {
4121             n++;
4122         }
4124         //if the piece belongs to this subpath grab it
4125         //otherwise move onto the next subpath
4126         if (index < n) {
4127             e = sp->first;
4128             for (int i = 0; i < index; ++i) {
4129                 e = e->n.other;
4130             }
4131             break;
4132         } else {
4133             if (sp->closed) {
4134                 index -= (n+1);
4135             } else {
4136                 index -= n;
4137             }
4138         }
4139     }
4141     return e;
4144 /**
4145  * Returns plain text meaning of node type.
4146  */
4147 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4149     unsigned retracted = 0;
4150     bool endnode = false;
4152     for (int which = -1; which <= 1; which += 2) {
4153         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4154         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4155             retracted ++;
4156         if (!side->other)
4157             endnode = true;
4158     }
4160     if (retracted == 0) {
4161         if (endnode) {
4162                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4163                 return _("end node");
4164         } else {
4165             switch (node->type) {
4166                 case Inkscape::NodePath::NODE_CUSP:
4167                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4168                     return _("cusp");
4169                 case Inkscape::NodePath::NODE_SMOOTH:
4170                     // TRANSLATORS: "smooth" is an adjective here
4171                     return _("smooth");
4172                 case Inkscape::NodePath::NODE_SYMM:
4173                     return _("symmetric");
4174             }
4175         }
4176     } else if (retracted == 1) {
4177         if (endnode) {
4178             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4179             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4180         } else {
4181             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4182         }
4183     } else {
4184         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4185     }
4187     return NULL;
4190 /**
4191  * Handles content of statusbar as long as node tool is active.
4192  */
4193 void
4194 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
4196     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>Alt+drag</b> nodes to sculpt; <b>arrow</b> keys to move nodes, <b>&lt; &gt;</b> to scale, <b>[ ]</b> to rotate");
4197     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4199     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4200     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4201     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4202     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4204     SPDesktop *desktop = NULL;
4205     if (nodepath) {
4206         desktop = nodepath->desktop;
4207     } else {
4208         desktop = SP_ACTIVE_DESKTOP;
4209     }
4211     SPEventContext *ec = desktop->event_context;
4212     if (!ec) return;
4213     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4214     if (!mc) return;
4216     if (selected_nodes == 0) {
4217         Inkscape::Selection *sel = desktop->selection;
4218         if (!sel || sel->isEmpty()) {
4219             mc->setF(Inkscape::NORMAL_MESSAGE,
4220                      _("Select a single object to edit its nodes or handles."));
4221         } else {
4222             if (nodepath) {
4223             mc->setF(Inkscape::NORMAL_MESSAGE,
4224                      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.",
4225                               "<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.",
4226                               total_nodes),
4227                      total_nodes);
4228             } else {
4229                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4230                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4231                 } else {
4232                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4233                 }
4234             }
4235         }
4236     } else if (nodepath && selected_nodes == 1) {
4237         mc->setF(Inkscape::NORMAL_MESSAGE,
4238                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4239                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4240                           total_nodes),
4241                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4242     } else {
4243         if (selected_subpaths > 1) {
4244             mc->setF(Inkscape::NORMAL_MESSAGE,
4245                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4246                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4247                               total_nodes),
4248                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4249         } else {
4250             mc->setF(Inkscape::NORMAL_MESSAGE,
4251                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4252                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4253                               total_nodes),
4254                      selected_nodes, total_nodes, when_selected);
4255         }
4256     }
4260 /*
4261   Local Variables:
4262   mode:c++
4263   c-file-style:"stroustrup"
4264   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4265   indent-tabs-mode:nil
4266   fill-column:99
4267   End:
4268 */
4269 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :