Code

factor out creation of livarot_path, and make it on-demand so that update_repr does...
[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     sp_nodepath_ensure_livarot_path(np);
216     return np;
219 /**
220  * Destroys nodepath's subpaths, then itself, also tell context about it.
221  */
222 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
224     if (!np)  //soft fail, like delete
225         return;
227     while (np->subpaths) {
228         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
229     }
231     //Inform the context that made me, if any, that I am gone.
232     if (np->nodeContext)
233         np->nodeContext->nodepath = NULL;
235     g_assert(!np->selected);
237     if (np->livarot_path) {
238         delete np->livarot_path;
239         np->livarot_path = NULL;
240     }
242     np->desktop = NULL;
244     g_free(np);
248 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
250     if (np && np->livarot_path == NULL && np->path && SP_IS_ITEM(np->path)) {
251         np->livarot_path = Path_for_item (np->path, true, true);
252         if (np->livarot_path)
253             np->livarot_path->ConvertWithBackData(0.01);
254     }
258 /**
259  *  Return the node count of a given NodeSubPath.
260  */
261 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
263     if (!subpath)
264         return 0;
265     gint nodeCount = g_list_length(subpath->nodes);
266     return nodeCount;
269 /**
270  *  Return the node count of a given NodePath.
271  */
272 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
274     if (!np)
275         return 0;
276     gint nodeCount = 0;
277     for (GList *item = np->subpaths ; item ; item=item->next) {
278        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
279         nodeCount += g_list_length(subpath->nodes);
280     }
281     return nodeCount;
284 /**
285  *  Return the subpath count of a given NodePath.
286  */
287 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
289     if (!np)
290         return 0;
291     return g_list_length (np->subpaths);
294 /**
295  *  Return the selected node count of a given NodePath.
296  */
297 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
299     if (!np)
300         return 0;
301     return g_list_length (np->selected);
304 /**
305  *  Return the number of subpaths where nodes are selected in a given NodePath.
306  */
307 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
309     if (!np)
310         return 0;
311     if (!np->selected)
312         return 0;
313     if (!np->selected->next)
314         return 1;
315     gint count = 0;
316     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
317         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
318         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
319             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
320             if (node->selected) {
321                 count ++;
322                 break;
323             }
324         }
325     }
326     return count;
328  
329 /**
330  * Clean up a nodepath after editing.
331  *
332  * Currently we are deleting trivial subpaths.
333  */
334 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
336     GList *badSubPaths = NULL;
338     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
339     for (GList *l = nodepath->subpaths; l ; l=l->next) {
340        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
341        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
342             badSubPaths = g_list_append(badSubPaths, sp);
343     }
345     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
346     //also removes the subpath from nodepath->subpaths
347     for (GList *l = badSubPaths; l ; l=l->next) {
348        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
349         sp_nodepath_subpath_destroy(sp);
350     }
352     g_list_free(badSubPaths);
355 /**
356  * Create new nodepath from b, make it subpath of np.
357  * \param t The node type.
358  * \todo Fixme: t should be a proper type, rather than gchar
359  */
360 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
362     NR::Point ppos, pos, npos;
364     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
366     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
367     bool const closed = (b->code == NR_MOVETO);
369     pos = NR::Point(b->x3, b->y3) * np->i2d;
370     if (b[1].code == NR_CURVETO) {
371         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
372     } else {
373         npos = pos;
374     }
375     Inkscape::NodePath::Node *n;
376     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
377     g_assert(sp->first == n);
378     g_assert(sp->last  == n);
380     b++;
381     t++;
382     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
383         pos = NR::Point(b->x3, b->y3) * np->i2d;
384         if (b->code == NR_CURVETO) {
385             ppos = NR::Point(b->x2, b->y2) * np->i2d;
386         } else {
387             ppos = pos;
388         }
389         if (b[1].code == NR_CURVETO) {
390             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
391         } else {
392             npos = pos;
393         }
394         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
395         b++;
396         t++;
397     }
399     if (closed) sp_nodepath_subpath_close(sp);
401     return b;
404 /**
405  * Convert from sodipodi:nodetypes to new style type string.
406  */
407 static gchar *parse_nodetypes(gchar const *types, gint length)
409     g_assert(length > 0);
411     gchar *typestr = g_new(gchar, length + 1);
413     gint pos = 0;
415     if (types) {
416         for (gint i = 0; types[i] && ( i < length ); i++) {
417             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
418             if (types[i] != '\0') {
419                 switch (types[i]) {
420                     case 's':
421                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
422                         break;
423                     case 'z':
424                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
425                         break;
426                     case 'c':
427                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
428                         break;
429                     default:
430                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
431                         break;
432                 }
433             }
434         }
435     }
437     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
439     return typestr;
442 /**
443  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
444  * updated but repr is not (for speed). Used during curve and node drag.
445  */
446 static void update_object(Inkscape::NodePath::Path *np)
448     g_assert(np);
450     SPCurve *curve = create_curve(np);
452     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
454     sp_curve_unref(curve);
457 /**
458  * Update XML path node with data from path object.
459  */
460 static void update_repr_internal(Inkscape::NodePath::Path *np)
462     g_assert(np);
464     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
466     SPCurve *curve = create_curve(np);
467     gchar *typestr = create_typestr(np);
468     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
470     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
471         np->local_change++;
472         repr->setAttribute("d", svgpath);
473     }
475     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
476         np->local_change++;
477         repr->setAttribute("sodipodi:nodetypes", typestr);
478     }
480     g_free(svgpath);
481     g_free(typestr);
482     sp_curve_unref(curve);
485 /**
486  * Update XML path node with data from path object, commit changes forever.
487  */
488 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
490     //fixme: np can be NULL, so check before proceeding
491     g_return_if_fail(np != NULL);
493     if (np->livarot_path) {
494         delete np->livarot_path;
495         np->livarot_path = NULL;
496     }
498     update_repr_internal(np);
499     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
500     
501     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
502                      annotation);
505 /**
506  * Update XML path node with data from path object, commit changes with undo.
507  */
508 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
510     if (np->livarot_path) {
511         delete np->livarot_path;
512         np->livarot_path = NULL;
513     }
515     update_repr_internal(np);
516     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
517                            annotation);
520 /**
521  * Make duplicate of path, replace corresponding XML node in tree, commit.
522  */
523 static void stamp_repr(Inkscape::NodePath::Path *np)
525     g_assert(np);
527     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
528     Inkscape::XML::Node *new_repr = old_repr->duplicate();
530     // remember the position of the item
531     gint pos = old_repr->position();
532     // remember parent
533     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
535     SPCurve *curve = create_curve(np);
536     gchar *typestr = create_typestr(np);
538     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
540     new_repr->setAttribute("d", svgpath);
541     new_repr->setAttribute("sodipodi:nodetypes", typestr);
543     // add the new repr to the parent
544     parent->appendChild(new_repr);
545     // move to the saved position
546     new_repr->setPosition(pos > 0 ? pos : 0);
548     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
549                      _("Stamp"));
551     Inkscape::GC::release(new_repr);
552     g_free(svgpath);
553     g_free(typestr);
554     sp_curve_unref(curve);
557 /**
558  * Create curve from path.
559  */
560 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
562     SPCurve *curve = sp_curve_new();
564     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
565        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
566         sp_curve_moveto(curve,
567                         sp->first->pos * np->d2i);
568        Inkscape::NodePath::Node *n = sp->first->n.other;
569         while (n) {
570             NR::Point const end_pt = n->pos * np->d2i;
571             switch (n->code) {
572                 case NR_LINETO:
573                     sp_curve_lineto(curve, end_pt);
574                     break;
575                 case NR_CURVETO:
576                     sp_curve_curveto(curve,
577                                      n->p.other->n.pos * np->d2i,
578                                      n->p.pos * np->d2i,
579                                      end_pt);
580                     break;
581                 default:
582                     g_assert_not_reached();
583                     break;
584             }
585             if (n != sp->last) {
586                 n = n->n.other;
587             } else {
588                 n = NULL;
589             }
590         }
591         if (sp->closed) {
592             sp_curve_closepath(curve);
593         }
594     }
596     return curve;
599 /**
600  * Convert path type string to sodipodi:nodetypes style.
601  */
602 static gchar *create_typestr(Inkscape::NodePath::Path *np)
604     gchar *typestr = g_new(gchar, 32);
605     gint len = 32;
606     gint pos = 0;
608     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
609        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
611         if (pos >= len) {
612             typestr = g_renew(gchar, typestr, len + 32);
613             len += 32;
614         }
616         typestr[pos++] = 'c';
618        Inkscape::NodePath::Node *n;
619         n = sp->first->n.other;
620         while (n) {
621             gchar code;
623             switch (n->type) {
624                 case Inkscape::NodePath::NODE_CUSP:
625                     code = 'c';
626                     break;
627                 case Inkscape::NodePath::NODE_SMOOTH:
628                     code = 's';
629                     break;
630                 case Inkscape::NodePath::NODE_SYMM:
631                     code = 'z';
632                     break;
633                 default:
634                     g_assert_not_reached();
635                     code = '\0';
636                     break;
637             }
639             if (pos >= len) {
640                 typestr = g_renew(gchar, typestr, len + 32);
641                 len += 32;
642             }
644             typestr[pos++] = code;
646             if (n != sp->last) {
647                 n = n->n.other;
648             } else {
649                 n = NULL;
650             }
651         }
652     }
654     if (pos >= len) {
655         typestr = g_renew(gchar, typestr, len + 1);
656         len += 1;
657     }
659     typestr[pos++] = '\0';
661     return typestr;
664 /**
665  * Returns current path in context.
666  */
667 static Inkscape::NodePath::Path *sp_nodepath_current()
669     if (!SP_ACTIVE_DESKTOP) {
670         return NULL;
671     }
673     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
675     if (!SP_IS_NODE_CONTEXT(event_context)) {
676         return NULL;
677     }
679     return SP_NODE_CONTEXT(event_context)->nodepath;
684 /**
685  \brief Fills node and handle positions for three nodes, splitting line
686   marked by end at distance t.
687  */
688 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
690     g_assert(new_path != NULL);
691     g_assert(end      != NULL);
693     g_assert(end->p.other == new_path);
694    Inkscape::NodePath::Node *start = new_path->p.other;
695     g_assert(start);
697     if (end->code == NR_LINETO) {
698         new_path->type =Inkscape::NodePath::NODE_CUSP;
699         new_path->code = NR_LINETO;
700         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
701     } else {
702         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
703         new_path->code = NR_CURVETO;
704         gdouble s      = 1 - t;
705         for (int dim = 0; dim < 2; dim++) {
706             NR::Coord const f000 = start->pos[dim];
707             NR::Coord const f001 = start->n.pos[dim];
708             NR::Coord const f011 = end->p.pos[dim];
709             NR::Coord const f111 = end->pos[dim];
710             NR::Coord const f00t = s * f000 + t * f001;
711             NR::Coord const f01t = s * f001 + t * f011;
712             NR::Coord const f11t = s * f011 + t * f111;
713             NR::Coord const f0tt = s * f00t + t * f01t;
714             NR::Coord const f1tt = s * f01t + t * f11t;
715             NR::Coord const fttt = s * f0tt + t * f1tt;
716             start->n.pos[dim]    = f00t;
717             new_path->p.pos[dim] = f0tt;
718             new_path->pos[dim]   = fttt;
719             new_path->n.pos[dim] = f1tt;
720             end->p.pos[dim]      = f11t;
721         }
722     }
725 /**
726  * Adds new node on direct line between two nodes, activates handles of all
727  * three nodes.
728  */
729 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
731     g_assert(end);
732     g_assert(end->subpath);
733     g_assert(g_list_find(end->subpath->nodes, end));
735    Inkscape::NodePath::Node *start = end->p.other;
736     g_assert( start->n.other == end );
737    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
738                                                end,
739                                               Inkscape::NodePath::NODE_SMOOTH,
740                                                (NRPathcode)end->code,
741                                                &start->pos, &start->pos, &start->n.pos);
742     sp_nodepath_line_midpoint(newnode, end, t);
744     sp_node_update_handles(start);
745     sp_node_update_handles(newnode);
746     sp_node_update_handles(end);
748     return newnode;
751 /**
752 \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
753 */
754 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
756     g_assert(node);
757     g_assert(node->subpath);
758     g_assert(g_list_find(node->subpath->nodes, node));
760    Inkscape::NodePath::SubPath *sp = node->subpath;
761     Inkscape::NodePath::Path *np    = sp->nodepath;
763     if (sp->closed) {
764         sp_nodepath_subpath_open(sp, node);
765         return sp->first;
766     } else {
767         // no break for end nodes
768         if (node == sp->first) return NULL;
769         if (node == sp->last ) return NULL;
771         // create a new subpath
772        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
774         // duplicate the break node as start of the new subpath
775        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
777         while (node->n.other) { // copy the remaining nodes into the new subpath
778            Inkscape::NodePath::Node *n  = node->n.other;
779            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);
780             if (n->selected) {
781                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
782             }
783             sp_nodepath_node_destroy(n); // remove the point on the original subpath
784         }
786         return newnode;
787     }
790 /**
791  * Duplicate node and connect to neighbours.
792  */
793 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
795     g_assert(node);
796     g_assert(node->subpath);
797     g_assert(g_list_find(node->subpath->nodes, node));
799    Inkscape::NodePath::SubPath *sp = node->subpath;
801     NRPathcode code = (NRPathcode) node->code;
802     if (code == NR_MOVETO) { // if node is the endnode,
803         node->code = NR_LINETO; // new one is inserted before it, so change that to line
804     }
806     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
808     if (!node->n.other || !node->p.other) // if node is an endnode, select it
809         return node;
810     else
811         return newnode; // otherwise select the newly created node
814 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
816     node->p.pos = (node->pos + (node->pos - node->n.pos));
819 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
821     node->n.pos = (node->pos + (node->pos - node->p.pos));
824 /**
825  * Change line type at node, with side effects on neighbours.
826  */
827 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
829     g_assert(end);
830     g_assert(end->subpath);
831     g_assert(end->p.other);
833     if (end->code == static_cast< guint > ( code ) )
834         return;
836    Inkscape::NodePath::Node *start = end->p.other;
838     end->code = code;
840     if (code == NR_LINETO) {
841         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
842         if (end->n.other) {
843             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
844         }
845         sp_node_adjust_handle(start, -1);
846         sp_node_adjust_handle(end, 1);
847     } else {
848         NR::Point delta = end->pos - start->pos;
849         start->n.pos = start->pos + delta / 3;
850         end->p.pos = end->pos - delta / 3;
851         sp_node_adjust_handle(start, 1);
852         sp_node_adjust_handle(end, -1);
853     }
855     sp_node_update_handles(start);
856     sp_node_update_handles(end);
859 /**
860  * Change node type, and its handles accordingly.
861  */
862 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
864     g_assert(node);
865     g_assert(node->subpath);
867     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
868         return node;
870     if ((node->p.other != NULL) && (node->n.other != NULL)) {
871         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
872             type =Inkscape::NodePath::NODE_CUSP;
873         }
874     }
876     node->type = type;
878     if (node->type == Inkscape::NodePath::NODE_CUSP) {
879         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
880         node->knot->setSize (node->selected? 11 : 9);
881         sp_knot_update_ctrl(node->knot);
882     } else {
883         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
884         node->knot->setSize (node->selected? 9 : 7);
885         sp_knot_update_ctrl(node->knot);
886     }
888     // if one of handles is mouseovered, preserve its position
889     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
890         sp_node_adjust_handle(node, 1);
891     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
892         sp_node_adjust_handle(node, -1);
893     } else {
894         sp_node_adjust_handles(node);
895     }
897     sp_node_update_handles(node);
899     sp_nodepath_update_statusbar(node->subpath->nodepath);
901     return node;
904 /**
905  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
906  * adjacent segments from lines to curves.
907 */
908 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
910     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
911         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
912             // convert adjacent segment BEFORE to curve
913             node->code = NR_CURVETO;
914             NR::Point delta;
915             if (node->n.other != NULL)
916                 delta = node->n.other->pos - node->p.other->pos;
917             else
918                 delta = node->pos - node->p.other->pos;
919             node->p.pos = node->pos - delta / 4;
920             sp_node_update_handles(node);
921         }
923         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
924             // convert adjacent segment AFTER to curve
925             node->n.other->code = NR_CURVETO;
926             NR::Point delta;
927             if (node->p.other != NULL)
928                 delta = node->p.other->pos - node->n.other->pos;
929             else
930                 delta = node->pos - node->n.other->pos;
931             node->n.pos = node->pos - delta / 4;
932             sp_node_update_handles(node);
933         }
934     }
936     sp_nodepath_set_node_type (node, type);
939 /**
940  * Move node to point, and adjust its and neighbouring handles.
941  */
942 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
944     NR::Point delta = p - node->pos;
945     node->pos = p;
947     node->p.pos += delta;
948     node->n.pos += delta;
950     if (node->p.other) {
951         if (node->code == NR_LINETO) {
952             sp_node_adjust_handle(node, 1);
953             sp_node_adjust_handle(node->p.other, -1);
954         }
955     }
956     if (node->n.other) {
957         if (node->n.other->code == NR_LINETO) {
958             sp_node_adjust_handle(node, -1);
959             sp_node_adjust_handle(node->n.other, 1);
960         }
961     }
963     // this function is only called from batch movers that will update display at the end
964     // themselves, so here we just move all the knots without emitting move signals, for speed
965     sp_node_update_handles(node, false);
968 /**
969  * Call sp_node_moveto() for node selection and handle possible snapping.
970  */
971 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
972                                             bool const snap = true)
974     NR::Coord best = NR_HUGE;
975     NR::Point delta(dx, dy);
976     NR::Point best_pt = delta;
978     if (snap) {
979         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
980         
981         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
982             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
983             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
984             if (s.getDistance() < best) {
985                 best = s.getDistance();
986                 best_pt = s.getPoint() - n->pos;
987             }
988         }
989     }
991     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
992         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
993         sp_node_moveto(n, n->pos + best_pt);
994     }
996     // do not update repr here so that node dragging is acceptably fast
997     update_object(nodepath);
1000 /**
1001 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1002 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1003 near x = 0.
1004  */
1005 double
1006 sculpt_profile (double x, double alpha, guint profile)
1008     if (x >= 1)
1009         return 0;
1010     if (x <= 0)
1011         return 1;
1013     switch (profile) {
1014         case SCULPT_PROFILE_LINEAR:
1015         return 1 - x;
1016         case SCULPT_PROFILE_BELL:
1017         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1018         case SCULPT_PROFILE_ELLIPTIC:
1019         return sqrt(1 - x*x);
1020     }
1022     return 1;
1025 double
1026 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1028     // extremely primitive for now, don't have time to look for the real one
1029     double lower = NR::L2(b - a);
1030     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1031     return (lower + upper)/2;
1034 void
1035 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1037     n->pos = n->origin + delta;
1038     n->n.pos = n->n.origin + delta_n;
1039     n->p.pos = n->p.origin + delta_p;
1040     sp_node_adjust_handles(n);
1041     sp_node_update_handles(n, false);
1044 /**
1045  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1046  * on how far they are from the dragged node n.
1047  */
1048 static void 
1049 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1051     g_assert (n);
1052     g_assert (nodepath);
1053     g_assert (n->subpath->nodepath == nodepath);
1055     double pressure = n->knot->pressure;
1056     if (pressure == 0)
1057         pressure = 0.5; // default
1058     pressure = CLAMP (pressure, 0.2, 0.8);
1060     // map pressure to alpha = 1/5 ... 5
1061     double alpha = 1 - 2 * fabs(pressure - 0.5);
1062     if (pressure > 0.5)
1063         alpha = 1/alpha;
1065     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1067     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1068         // Only one subpath has selected nodes:
1069         // use linear mode, where the distance from n to node being dragged is calculated along the path
1071         double n_sel_range = 0, p_sel_range = 0;
1072         guint n_nodes = 0, p_nodes = 0;
1073         guint n_sel_nodes = 0, p_sel_nodes = 0;
1075         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1076         {
1077             double n_range = 0, p_range = 0;
1078             bool n_going = true, p_going = true;
1079             Inkscape::NodePath::Node *n_node = n;
1080             Inkscape::NodePath::Node *p_node = n;
1081             do {
1082                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1083                 if (n_node && n_going)
1084                     n_node = n_node->n.other;
1085                 if (n_node == NULL) {
1086                     n_going = false;
1087                 } else {
1088                     n_nodes ++;
1089                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1090                     if (n_node->selected) {
1091                         n_sel_nodes ++;
1092                         n_sel_range = n_range;
1093                     }
1094                     if (n_node == p_node) {
1095                         n_going = false;
1096                         p_going = false;
1097                     }
1098                 }
1099                 if (p_node && p_going)
1100                     p_node = p_node->p.other;
1101                 if (p_node == NULL) {
1102                     p_going = false;
1103                 } else {
1104                     p_nodes ++;
1105                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1106                     if (p_node->selected) {
1107                         p_sel_nodes ++;
1108                         p_sel_range = p_range;
1109                     }
1110                     if (p_node == n_node) {
1111                         n_going = false;
1112                         p_going = false;
1113                     }
1114                 }
1115             } while (n_going || p_going);
1116         }
1118         // Second pass: actually move nodes in this subpath
1119         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1120         {
1121             double n_range = 0, p_range = 0;
1122             bool n_going = true, p_going = true;
1123             Inkscape::NodePath::Node *n_node = n;
1124             Inkscape::NodePath::Node *p_node = n;
1125             do {
1126                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1127                 if (n_node && n_going)
1128                     n_node = n_node->n.other;
1129                 if (n_node == NULL) {
1130                     n_going = false;
1131                 } else {
1132                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1133                     if (n_node->selected) {
1134                         sp_nodepath_move_node_and_handles (n_node, 
1135                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1136                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1137                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1138                     }
1139                     if (n_node == p_node) {
1140                         n_going = false;
1141                         p_going = false;
1142                     }
1143                 }
1144                 if (p_node && p_going)
1145                     p_node = p_node->p.other;
1146                 if (p_node == NULL) {
1147                     p_going = false;
1148                 } else {
1149                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1150                     if (p_node->selected) {
1151                         sp_nodepath_move_node_and_handles (p_node, 
1152                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1153                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1154                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1155                     }
1156                     if (p_node == n_node) {
1157                         n_going = false;
1158                         p_going = false;
1159                     }
1160                 }
1161             } while (n_going || p_going);
1162         }
1164     } else {
1165         // Multiple subpaths have selected nodes:
1166         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1167         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1168         // fix the pear-like shape when sculpting e.g. a ring
1170         // First pass: calculate range
1171         gdouble direct_range = 0;
1172         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1173             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1174             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1175                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1176                 if (node->selected) {
1177                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1178                 }
1179             }
1180         }
1182         // Second pass: actually move nodes
1183         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1184             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1185             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1186                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1187                 if (node->selected) {
1188                     if (direct_range > 1e-6) {
1189                         sp_nodepath_move_node_and_handles (node,
1190                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1191                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1192                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1193                     } else {
1194                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1195                     }
1197                 }
1198             }
1199         }
1200     }
1202     // do not update repr here so that node dragging is acceptably fast
1203     update_object(nodepath);
1207 /**
1208  * Move node selection to point, adjust its and neighbouring handles,
1209  * handle possible snapping, and commit the change with possible undo.
1210  */
1211 void
1212 sp_node_selected_move(gdouble dx, gdouble dy)
1214     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1215     if (!nodepath) return;
1217     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1219     if (dx == 0) {
1220         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1221     } else if (dy == 0) {
1222         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1223     } else {
1224         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1225     }
1228 /**
1229  * Move node selection off screen and commit the change.
1230  */
1231 void
1232 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1234     // borrowed from sp_selection_move_screen in selection-chemistry.c
1235     // we find out the current zoom factor and divide deltas by it
1236     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1238     gdouble zoom = desktop->current_zoom();
1239     gdouble zdx = dx / zoom;
1240     gdouble zdy = dy / zoom;
1242     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1243     if (!nodepath) return;
1245     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1247     if (dx == 0) {
1248         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1249     } else if (dy == 0) {
1250         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1251     } else {
1252         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1253     }
1256 /** If they don't yet exist, creates knot and line for the given side of the node */
1257 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1259     if (!side->knot) {
1260         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"));
1262         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1263         side->knot->setSize (7);
1264         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1265         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1266         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1267         sp_knot_update_ctrl(side->knot);
1269         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1270         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1271         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1272         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1273         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1274         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1275     }
1277     if (!side->line) {
1278         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1279                                         SP_TYPE_CTRLLINE, NULL);
1280     }
1283 /**
1284  * Ensure the given handle of the node is visible/invisible, update its screen position
1285  */
1286 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1288     g_assert(node != NULL);
1290    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1291     NRPathcode code = sp_node_path_code_from_side(node, side);
1293     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1295     if (show_handle) {
1296         if (!side->knot) { // No handle knot at all
1297             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1298             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1299             side->knot->pos = side->pos;
1300             if (side->knot->item) 
1301                 SP_CTRL(side->knot->item)->moveto(side->pos);
1302             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1303             sp_knot_show(side->knot);
1304         } else {
1305             if (side->knot->pos != side->pos) { // only if it's really moved
1306                 if (fire_move_signals) {
1307                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1308                 } else {
1309                     sp_knot_moveto(side->knot, &side->pos);
1310                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1311                 }
1312             }
1313             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1314                 sp_knot_show(side->knot);
1315             }
1316         }
1317         sp_canvas_item_show(side->line);
1318     } else {
1319         if (side->knot) {
1320             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1321                 sp_knot_hide(side->knot);
1322             }
1323         }
1324         if (side->line) {
1325             sp_canvas_item_hide(side->line);
1326         }
1327     }
1330 /**
1331  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1332  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1333  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1334  * updated; otherwise, just move the knots silently (used in batch moves).
1335  */
1336 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1338     g_assert(node != NULL);
1340     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1341         sp_knot_show(node->knot);
1342     }
1344     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1345         if (fire_move_signals)
1346             sp_knot_set_position(node->knot, &node->pos, 0);
1347         else 
1348             sp_knot_moveto(node->knot, &node->pos);
1349     }
1351     gboolean show_handles = node->selected;
1352     if (node->p.other != NULL) {
1353         if (node->p.other->selected) show_handles = TRUE;
1354     }
1355     if (node->n.other != NULL) {
1356         if (node->n.other->selected) show_handles = TRUE;
1357     }
1359     if (node->subpath->nodepath->show_handles == false)
1360         show_handles = FALSE;
1362     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1363     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1366 /**
1367  * Call sp_node_update_handles() for all nodes on subpath.
1368  */
1369 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1371     g_assert(subpath != NULL);
1373     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1374         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1375     }
1378 /**
1379  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1380  */
1381 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1383     g_assert(nodepath != NULL);
1385     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1386         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1387     }
1390 void
1391 sp_nodepath_show_handles(bool show)
1393     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1394     if (nodepath == NULL) return;
1396     nodepath->show_handles = show;
1397     sp_nodepath_update_handles(nodepath);
1400 /**
1401  * Adds all selected nodes in nodepath to list.
1402  */
1403 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1405     StlConv<Node *>::list(l, selected);
1406 /// \todo this adds a copying, rework when the selection becomes a stl list
1409 /**
1410  * Align selected nodes on the specified axis.
1411  */
1412 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1414     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1415         return;
1416     }
1418     if ( !nodepath->selected->next ) { // only one node selected
1419         return;
1420     }
1421    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1422     NR::Point dest(pNode->pos);
1423     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1424         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1425         if (pNode) {
1426             dest[axis] = pNode->pos[axis];
1427             sp_node_moveto(pNode, dest);
1428         }
1429     }
1431     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1434 /// Helper struct.
1435 struct NodeSort
1437    Inkscape::NodePath::Node *_node;
1438     NR::Coord _coord;
1439     /// \todo use vectorof pointers instead of calling copy ctor
1440     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1441         _node(node), _coord(node->pos[axis])
1442     {}
1444 };
1446 static bool operator<(NodeSort const &a, NodeSort const &b)
1448     return (a._coord < b._coord);
1451 /**
1452  * Distribute selected nodes on the specified axis.
1453  */
1454 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1456     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1457         return;
1458     }
1460     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1461         return;
1462     }
1464    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1465     std::vector<NodeSort> sorted;
1466     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1467         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1468         if (pNode) {
1469             NodeSort n(pNode, axis);
1470             sorted.push_back(n);
1471             //dest[axis] = pNode->pos[axis];
1472             //sp_node_moveto(pNode, dest);
1473         }
1474     }
1475     std::sort(sorted.begin(), sorted.end());
1476     unsigned int len = sorted.size();
1477     //overall bboxes span
1478     float dist = (sorted.back()._coord -
1479                   sorted.front()._coord);
1480     //new distance between each bbox
1481     float step = (dist) / (len - 1);
1482     float pos = sorted.front()._coord;
1483     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1484           it < sorted.end();
1485           it ++ )
1486     {
1487         NR::Point dest((*it)._node->pos);
1488         dest[axis] = pos;
1489         sp_node_moveto((*it)._node, dest);
1490         pos += step;
1491     }
1493     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1497 /**
1498  * Call sp_nodepath_line_add_node() for all selected segments.
1499  */
1500 void
1501 sp_node_selected_add_node(void)
1503     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1504     if (!nodepath) {
1505         return;
1506     }
1508     GList *nl = NULL;
1510     int n_added = 0;
1512     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1513        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1514         g_assert(t->selected);
1515         if (t->p.other && t->p.other->selected) {
1516             nl = g_list_prepend(nl, t);
1517         }
1518     }
1520     while (nl) {
1521        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1522        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1523        sp_nodepath_node_select(n, TRUE, FALSE);
1524        n_added ++;
1525        nl = g_list_remove(nl, t);
1526     }
1528     /** \todo fixme: adjust ? */
1529     sp_nodepath_update_handles(nodepath);
1531     if (n_added > 1) {
1532         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1533     } else if (n_added > 0) {
1534         sp_nodepath_update_repr(nodepath, _("Add node"));
1535     }
1537     sp_nodepath_update_statusbar(nodepath);
1540 /**
1541  * Select segment nearest to point
1542  */
1543 void
1544 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1546     if (!nodepath) {
1547         return;
1548     }
1550     sp_nodepath_ensure_livarot_path(nodepath);
1551     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1553     //find segment to segment
1554     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1556     //fixme: this can return NULL, so check before proceeding.
1557     g_return_if_fail(e != NULL);
1558     
1559     gboolean force = FALSE;
1560     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1561         force = TRUE;
1562     }
1563     sp_nodepath_node_select(e, (gboolean) toggle, force);
1564     if (e->p.other)
1565         sp_nodepath_node_select(e->p.other, TRUE, force);
1567     sp_nodepath_update_handles(nodepath);
1569     sp_nodepath_update_statusbar(nodepath);
1572 /**
1573  * Add a node nearest to point
1574  */
1575 void
1576 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1578     if (!nodepath) {
1579         return;
1580     }
1582     sp_nodepath_ensure_livarot_path(nodepath);
1583     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1585     //find segment to split
1586     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1588     //don't know why but t seems to flip for lines
1589     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1590         position.t = 1.0 - position.t;
1591     }
1592     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1593     sp_nodepath_node_select(n, FALSE, TRUE);
1595     /* fixme: adjust ? */
1596     sp_nodepath_update_handles(nodepath);
1598     sp_nodepath_update_repr(nodepath, _("Add node"));
1600     sp_nodepath_update_statusbar(nodepath);
1603 /*
1604  * Adjusts a segment so that t moves by a certain delta for dragging
1605  * converts lines to curves
1606  *
1607  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1608  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1609  */
1610 void
1611 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta)
1613     //fixme: e and e->p can be NULL, so check for those before proceeding
1614     g_return_if_fail(e != NULL);
1615     g_return_if_fail(&e->p != NULL);
1616     
1617     /* feel good is an arbitrary parameter that distributes the delta between handles
1618      * if t of the drag point is less than 1/6 distance form the endpoint only
1619      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1620      */
1621     double feel_good;
1622     if (t <= 1.0 / 6.0)
1623         feel_good = 0;
1624     else if (t <= 0.5)
1625         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1626     else if (t <= 5.0 / 6.0)
1627         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1628     else
1629         feel_good = 1;
1631     //if we're dragging a line convert it to a curve
1632     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1633         sp_nodepath_set_line_type(e, NR_CURVETO);
1634     }
1636     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1637     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1638     e->p.other->n.pos += offsetcoord0;
1639     e->p.pos += offsetcoord1;
1641     // adjust handles of adjacent nodes where necessary
1642     sp_node_adjust_handle(e,1);
1643     sp_node_adjust_handle(e->p.other,-1);
1645     sp_nodepath_update_handles(e->subpath->nodepath);
1647     update_object(e->subpath->nodepath);
1649     sp_nodepath_update_statusbar(e->subpath->nodepath);
1653 /**
1654  * Call sp_nodepath_break() for all selected segments.
1655  */
1656 void sp_node_selected_break()
1658     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1659     if (!nodepath) return;
1661     GList *temp = NULL;
1662     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1663        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1664        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1665         if (nn == NULL) continue; // no break, no new node
1666         temp = g_list_prepend(temp, nn);
1667     }
1669     if (temp) {
1670         sp_nodepath_deselect(nodepath);
1671     }
1672     for (GList *l = temp; l != NULL; l = l->next) {
1673         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1674     }
1676     sp_nodepath_update_handles(nodepath);
1678     sp_nodepath_update_repr(nodepath, _("Break path"));
1681 /**
1682  * Duplicate the selected node(s).
1683  */
1684 void sp_node_selected_duplicate()
1686     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1687     if (!nodepath) {
1688         return;
1689     }
1691     GList *temp = NULL;
1692     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1693        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1694        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1695         if (nn == NULL) continue; // could not duplicate
1696         temp = g_list_prepend(temp, nn);
1697     }
1699     if (temp) {
1700         sp_nodepath_deselect(nodepath);
1701     }
1702     for (GList *l = temp; l != NULL; l = l->next) {
1703         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1704     }
1706     sp_nodepath_update_handles(nodepath);
1708     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1711 /**
1712  *  Join two nodes by merging them into one.
1713  */
1714 void sp_node_selected_join()
1716     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1717     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1719     if (g_list_length(nodepath->selected) != 2) {
1720         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1721         return;
1722     }
1724    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1725    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1727     g_assert(a != b);
1728     g_assert(a->p.other || a->n.other);
1729     g_assert(b->p.other || b->n.other);
1731     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1732         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1733         return;
1734     }
1736     /* a and b are endpoints */
1738     NR::Point c;
1739     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1740         c = a->pos;
1741     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1742         c = b->pos;
1743     } else {
1744         c = (a->pos + b->pos) / 2;
1745     }
1747     if (a->subpath == b->subpath) {
1748        Inkscape::NodePath::SubPath *sp = a->subpath;
1749         sp_nodepath_subpath_close(sp);
1750         sp_node_moveto (sp->first, c);
1752         sp_nodepath_update_handles(sp->nodepath);
1753         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1754         return;
1755     }
1757     /* a and b are separate subpaths */
1758    Inkscape::NodePath::SubPath *sa = a->subpath;
1759    Inkscape::NodePath::SubPath *sb = b->subpath;
1760     NR::Point p;
1761    Inkscape::NodePath::Node *n;
1762     NRPathcode code;
1763     if (a == sa->first) {
1764         p = sa->first->n.pos;
1765         code = (NRPathcode)sa->first->n.other->code;
1766        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1767         n = sa->last;
1768         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1769         n = n->p.other;
1770         while (n) {
1771             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1772             n = n->p.other;
1773             if (n == sa->first) n = NULL;
1774         }
1775         sp_nodepath_subpath_destroy(sa);
1776         sa = t;
1777     } else if (a == sa->last) {
1778         p = sa->last->p.pos;
1779         code = (NRPathcode)sa->last->code;
1780         sp_nodepath_node_destroy(sa->last);
1781     } else {
1782         code = NR_END;
1783         g_assert_not_reached();
1784     }
1786     if (b == sb->first) {
1787         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1788         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1789             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1790         }
1791     } else if (b == sb->last) {
1792         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1793         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1794             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1795         }
1796     } else {
1797         g_assert_not_reached();
1798     }
1799     /* and now destroy sb */
1801     sp_nodepath_subpath_destroy(sb);
1803     sp_nodepath_update_handles(sa->nodepath);
1805     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1807     sp_nodepath_update_statusbar(nodepath);
1810 /**
1811  *  Join two nodes by adding a segment between them.
1812  */
1813 void sp_node_selected_join_segment()
1815     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1816     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1818     if (g_list_length(nodepath->selected) != 2) {
1819         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1820         return;
1821     }
1823    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1824    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1826     g_assert(a != b);
1827     g_assert(a->p.other || a->n.other);
1828     g_assert(b->p.other || b->n.other);
1830     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1831         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1832         return;
1833     }
1835     if (a->subpath == b->subpath) {
1836        Inkscape::NodePath::SubPath *sp = a->subpath;
1838         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1839         sp->closed = TRUE;
1841         sp->first->p.other = sp->last;
1842         sp->last->n.other  = sp->first;
1844         sp_node_handle_mirror_p_to_n(sp->last);
1845         sp_node_handle_mirror_n_to_p(sp->first);
1847         sp->first->code = sp->last->code;
1848         sp->first       = sp->last;
1850         sp_nodepath_update_handles(sp->nodepath);
1852         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1854         return;
1855     }
1857     /* a and b are separate subpaths */
1858    Inkscape::NodePath::SubPath *sa = a->subpath;
1859    Inkscape::NodePath::SubPath *sb = b->subpath;
1861    Inkscape::NodePath::Node *n;
1862     NR::Point p;
1863     NRPathcode code;
1864     if (a == sa->first) {
1865         code = (NRPathcode) sa->first->n.other->code;
1866        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1867         n = sa->last;
1868         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1869         for (n = n->p.other; n != NULL; n = n->p.other) {
1870             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1871         }
1872         sp_nodepath_subpath_destroy(sa);
1873         sa = t;
1874     } else if (a == sa->last) {
1875         code = (NRPathcode)sa->last->code;
1876     } else {
1877         code = NR_END;
1878         g_assert_not_reached();
1879     }
1881     if (b == sb->first) {
1882         n = sb->first;
1883         sp_node_handle_mirror_p_to_n(sa->last);
1884         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1885         sp_node_handle_mirror_n_to_p(sa->last);
1886         for (n = n->n.other; n != NULL; n = n->n.other) {
1887             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1888         }
1889     } else if (b == sb->last) {
1890         n = sb->last;
1891         sp_node_handle_mirror_p_to_n(sa->last);
1892         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1893         sp_node_handle_mirror_n_to_p(sa->last);
1894         for (n = n->p.other; n != NULL; n = n->p.other) {
1895             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1896         }
1897     } else {
1898         g_assert_not_reached();
1899     }
1900     /* and now destroy sb */
1902     sp_nodepath_subpath_destroy(sb);
1904     sp_nodepath_update_handles(sa->nodepath);
1906     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1909 /**
1910  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1911  */
1912 void sp_node_delete_preserve(GList *nodes_to_delete)
1914     GSList *nodepaths = NULL;
1915     
1916     while (nodes_to_delete) {
1917         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1918         Inkscape::NodePath::SubPath *sp = node->subpath;
1919         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1920         Inkscape::NodePath::Node *sample_cursor = NULL;
1921         Inkscape::NodePath::Node *sample_end = NULL;
1922         Inkscape::NodePath::Node *delete_cursor = node;
1923         bool just_delete = false;
1924         
1925         //find the start of this contiguous selection
1926         //move left to the first node that is not selected
1927         //or the start of the non-closed path
1928         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1929             delete_cursor = curr;
1930         }
1932         //just delete at the beginning of an open path
1933         if (!delete_cursor->p.other) {
1934             sample_cursor = delete_cursor;
1935             just_delete = true;
1936         } else {
1937             sample_cursor = delete_cursor->p.other;
1938         }
1939         
1940         //calculate points for each segment
1941         int rate = 5;
1942         float period = 1.0 / rate;
1943         std::vector<NR::Point> data;
1944         if (!just_delete) {
1945             data.push_back(sample_cursor->pos);
1946             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1947                 //just delete at the end of an open path
1948                 if (!sp->closed && curr == sp->last) {
1949                     just_delete = true;
1950                     break;
1951                 }
1952                 
1953                 //sample points on the contiguous selected segment
1954                 NR::Point *bez;
1955                 bez = new NR::Point [4];
1956                 bez[0] = curr->pos;
1957                 bez[1] = curr->n.pos;
1958                 bez[2] = curr->n.other->p.pos;
1959                 bez[3] = curr->n.other->pos;
1960                 for (int i=1; i<rate; i++) {
1961                     gdouble t = i * period;
1962                     NR::Point p = bezier_pt(3, bez, t);
1963                     data.push_back(p);
1964                 }
1965                 data.push_back(curr->n.other->pos);
1967                 sample_end = curr->n.other;
1968                 //break if we've come full circle or hit the end of the selection
1969                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1970                     break;
1971                 }
1972             }
1973         }
1975         if (!just_delete) {
1976             //calculate the best fitting single segment and adjust the endpoints
1977             NR::Point *adata;
1978             adata = new NR::Point [data.size()];
1979             copy(data.begin(), data.end(), adata);
1980             
1981             NR::Point *bez;
1982             bez = new NR::Point [4];
1983             //would decreasing error create a better fitting approximation?
1984             gdouble error = 1.0;
1985             gint ret;
1986             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1988             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
1989             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
1990             //the resulting nodes behave as expected.
1991             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
1992             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
1993             
1994             //adjust endpoints
1995             sample_cursor->n.pos = bez[1];
1996             sample_end->p.pos = bez[2];
1997         }
1998        
1999         //destroy this contiguous selection
2000         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2001             Inkscape::NodePath::Node *temp = delete_cursor;
2002             if (delete_cursor->n.other == delete_cursor) {
2003                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2004                 delete_cursor = NULL; 
2005             } else {
2006                 delete_cursor = delete_cursor->n.other;
2007             }
2008             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2009             sp_nodepath_node_destroy(temp);
2010         }
2012         sp_nodepath_update_handles(nodepath);
2014         if (!g_slist_find(nodepaths, nodepath))
2015             nodepaths = g_slist_prepend (nodepaths, nodepath);
2016     }
2018     for (GSList *i = nodepaths; i; i = i->next) {
2019         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2020         // different nodepaths will give us one undo event per nodepath
2021         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2023         // if the entire nodepath is removed, delete the selected object.
2024         if (nodepath->subpaths == NULL ||
2025             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2026             //at least 2
2027             sp_nodepath_get_node_count(nodepath) < 2) {
2028             SPDocument *document = sp_desktop_document (nodepath->desktop);
2029             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2030             //delete this nodepath's object, not the entire selection! (though at this time, this
2031             //does not matter)
2032             sp_selection_delete();
2033             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2034                               _("Delete nodes"));
2035         } else {
2036             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2037             sp_nodepath_update_statusbar(nodepath);
2038         }
2039     }
2041     g_slist_free (nodepaths);
2044 /**
2045  * Delete one or more selected nodes.
2046  */
2047 void sp_node_selected_delete()
2049     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2050     if (!nodepath) return;
2051     if (!nodepath->selected) return;
2053     /** \todo fixme: do it the right way */
2054     while (nodepath->selected) {
2055        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2056         sp_nodepath_node_destroy(node);
2057     }
2060     //clean up the nodepath (such as for trivial subpaths)
2061     sp_nodepath_cleanup(nodepath);
2063     sp_nodepath_update_handles(nodepath);
2065     // if the entire nodepath is removed, delete the selected object.
2066     if (nodepath->subpaths == NULL ||
2067         sp_nodepath_get_node_count(nodepath) < 2) {
2068         SPDocument *document = sp_desktop_document (nodepath->desktop);
2069         sp_selection_delete();
2070         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2071                           _("Delete nodes"));
2072         return;
2073     }
2075     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2077     sp_nodepath_update_statusbar(nodepath);
2080 /**
2081  * Delete one or more segments between two selected nodes.
2082  * This is the code for 'split'.
2083  */
2084 void
2085 sp_node_selected_delete_segment(void)
2087    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2088    Inkscape::NodePath::Node *curr, *next;     //Iterators
2090     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2091     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2093     if (g_list_length(nodepath->selected) != 2) {
2094         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2095                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2096         return;
2097     }
2099     //Selected nodes, not inclusive
2100    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2101    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2103     if ( ( a==b)                       ||  //same node
2104          (a->subpath  != b->subpath )  ||  //not the same path
2105          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2106          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2107     {
2108         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2109                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2110         return;
2111     }
2113     //###########################################
2114     //# BEGIN EDITS
2115     //###########################################
2116     //##################################
2117     //# CLOSED PATH
2118     //##################################
2119     if (a->subpath->closed) {
2122         gboolean reversed = FALSE;
2124         //Since we can go in a circle, we need to find the shorter distance.
2125         //  a->b or b->a
2126         start = end = NULL;
2127         int distance    = 0;
2128         int minDistance = 0;
2129         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2130             if (curr==b) {
2131                 //printf("a to b:%d\n", distance);
2132                 start = a;//go from a to b
2133                 end   = b;
2134                 minDistance = distance;
2135                 //printf("A to B :\n");
2136                 break;
2137             }
2138             distance++;
2139         }
2141         //try again, the other direction
2142         distance = 0;
2143         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2144             if (curr==a) {
2145                 //printf("b to a:%d\n", distance);
2146                 if (distance < minDistance) {
2147                     start    = b;  //we go from b to a
2148                     end      = a;
2149                     reversed = TRUE;
2150                     //printf("B to A\n");
2151                 }
2152                 break;
2153             }
2154             distance++;
2155         }
2158         //Copy everything from 'end' to 'start' to a new subpath
2159        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2160         for (curr=end ; curr ; curr=curr->n.other) {
2161             NRPathcode code = (NRPathcode) curr->code;
2162             if (curr == end)
2163                 code = NR_MOVETO;
2164             sp_nodepath_node_new(t, NULL,
2165                                  (Inkscape::NodePath::NodeType)curr->type, code,
2166                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2167             if (curr == start)
2168                 break;
2169         }
2170         sp_nodepath_subpath_destroy(a->subpath);
2173     }
2177     //##################################
2178     //# OPEN PATH
2179     //##################################
2180     else {
2182         //We need to get the direction of the list between A and B
2183         //Can we walk from a to b?
2184         start = end = NULL;
2185         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2186             if (curr==b) {
2187                 start = a;  //did it!  we go from a to b
2188                 end   = b;
2189                 //printf("A to B\n");
2190                 break;
2191             }
2192         }
2193         if (!start) {//didn't work?  let's try the other direction
2194             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2195                 if (curr==a) {
2196                     start = b;  //did it!  we go from b to a
2197                     end   = a;
2198                     //printf("B to A\n");
2199                     break;
2200                 }
2201             }
2202         }
2203         if (!start) {
2204             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2205                                                      _("Cannot find path between nodes."));
2206             return;
2207         }
2211         //Copy everything after 'end' to a new subpath
2212        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2213         for (curr=end ; curr ; curr=curr->n.other) {
2214             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2215                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2216         }
2218         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2219         for (curr = start->n.other ; curr  ; curr=next) {
2220             next = curr->n.other;
2221             sp_nodepath_node_destroy(curr);
2222         }
2224     }
2225     //###########################################
2226     //# END EDITS
2227     //###########################################
2229     //clean up the nodepath (such as for trivial subpaths)
2230     sp_nodepath_cleanup(nodepath);
2232     sp_nodepath_update_handles(nodepath);
2234     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2236     sp_nodepath_update_statusbar(nodepath);
2239 /**
2240  * Call sp_nodepath_set_line() for all selected segments.
2241  */
2242 void
2243 sp_node_selected_set_line_type(NRPathcode code)
2245     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2246     if (nodepath == NULL) return;
2248     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2249        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2250         g_assert(n->selected);
2251         if (n->p.other && n->p.other->selected) {
2252             sp_nodepath_set_line_type(n, code);
2253         }
2254     }
2256     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2259 /**
2260  * Call sp_nodepath_convert_node_type() for all selected nodes.
2261  */
2262 void
2263 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2265     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2266     if (nodepath == NULL) return;
2268     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2269         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2270     }
2272     sp_nodepath_update_repr(nodepath, _("Change node type"));
2275 /**
2276  * Change select status of node, update its own and neighbour handles.
2277  */
2278 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2280     node->selected = selected;
2282     if (selected) {
2283         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2284         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2285         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2286         sp_knot_update_ctrl(node->knot);
2287     } else {
2288         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2289         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2290         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2291         sp_knot_update_ctrl(node->knot);
2292     }
2294     sp_node_update_handles(node);
2295     if (node->n.other) sp_node_update_handles(node->n.other);
2296     if (node->p.other) sp_node_update_handles(node->p.other);
2299 /**
2300 \brief Select a node
2301 \param node     The node to select
2302 \param incremental   If true, add to selection, otherwise deselect others
2303 \param override   If true, always select this node, otherwise toggle selected status
2304 */
2305 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2307     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2309     if (incremental) {
2310         if (override) {
2311             if (!g_list_find(nodepath->selected, node)) {
2312                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2313             }
2314             sp_node_set_selected(node, TRUE);
2315         } else { // toggle
2316             if (node->selected) {
2317                 g_assert(g_list_find(nodepath->selected, node));
2318                 nodepath->selected = g_list_remove(nodepath->selected, node);
2319             } else {
2320                 g_assert(!g_list_find(nodepath->selected, node));
2321                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2322             }
2323             sp_node_set_selected(node, !node->selected);
2324         }
2325     } else {
2326         sp_nodepath_deselect(nodepath);
2327         nodepath->selected = g_list_prepend(nodepath->selected, node);
2328         sp_node_set_selected(node, TRUE);
2329     }
2331     sp_nodepath_update_statusbar(nodepath);
2335 /**
2336 \brief Deselect all nodes in the nodepath
2337 */
2338 void
2339 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2341     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2343     while (nodepath->selected) {
2344         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2345         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2346     }
2347     sp_nodepath_update_statusbar(nodepath);
2350 /**
2351 \brief Select or invert selection of all nodes in the nodepath
2352 */
2353 void
2354 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2356     if (!nodepath) return;
2358     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2359        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2360         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2361            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2362            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2363         }
2364     }
2367 /**
2368  * If nothing selected, does the same as sp_nodepath_select_all();
2369  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2370  * (i.e., similar to "select all in layer", with the "selected" subpaths
2371  * being treated as "layers" in the path).
2372  */
2373 void
2374 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2376     if (!nodepath) return;
2378     if (g_list_length (nodepath->selected) == 0) {
2379         sp_nodepath_select_all (nodepath, invert);
2380         return;
2381     }
2383     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2384     GSList *subpaths = NULL;
2386     for (GList *l = copy; l != NULL; l = l->next) {
2387         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2388         Inkscape::NodePath::SubPath *subpath = n->subpath;
2389         if (!g_slist_find (subpaths, subpath))
2390             subpaths = g_slist_prepend (subpaths, subpath);
2391     }
2393     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2394         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2395         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2396             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2397             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2398         }
2399     }
2401     g_slist_free (subpaths);
2402     g_list_free (copy);
2405 /**
2406  * \brief Select the node after the last selected; if none is selected,
2407  * select the first within path.
2408  */
2409 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2411     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2413    Inkscape::NodePath::Node *last = NULL;
2414     if (nodepath->selected) {
2415         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2416            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2417             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2418             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2419                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2420                 if (node->selected) {
2421                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2422                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2423                             if (spl->next) { // there's a next subpath
2424                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2425                                 last = subpath_next->first;
2426                             } else if (spl->prev) { // there's a previous subpath
2427                                 last = NULL; // to be set later to the first node of first subpath
2428                             } else {
2429                                 last = node->n.other;
2430                             }
2431                         } else {
2432                             last = node->n.other;
2433                         }
2434                     } else {
2435                         if (node->n.other) {
2436                             last = node->n.other;
2437                         } else {
2438                             if (spl->next) { // there's a next subpath
2439                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2440                                 last = subpath_next->first;
2441                             } else if (spl->prev) { // there's a previous subpath
2442                                 last = NULL; // to be set later to the first node of first subpath
2443                             } else {
2444                                 last = (Inkscape::NodePath::Node *) subpath->first;
2445                             }
2446                         }
2447                     }
2448                 }
2449             }
2450         }
2451         sp_nodepath_deselect(nodepath);
2452     }
2454     if (last) { // there's at least one more node after selected
2455         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2456     } else { // no more nodes, select the first one in first subpath
2457        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2458         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2459     }
2462 /**
2463  * \brief Select the node before the first selected; if none is selected,
2464  * select the last within path
2465  */
2466 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2468     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2470    Inkscape::NodePath::Node *last = NULL;
2471     if (nodepath->selected) {
2472         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2473            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2474             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2475                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2476                 if (node->selected) {
2477                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2478                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2479                             if (spl->prev) { // there's a prev subpath
2480                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2481                                 last = subpath_prev->last;
2482                             } else if (spl->next) { // there's a next subpath
2483                                 last = NULL; // to be set later to the last node of last subpath
2484                             } else {
2485                                 last = node->p.other;
2486                             }
2487                         } else {
2488                             last = node->p.other;
2489                         }
2490                     } else {
2491                         if (node->p.other) {
2492                             last = node->p.other;
2493                         } else {
2494                             if (spl->prev) { // there's a prev subpath
2495                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2496                                 last = subpath_prev->last;
2497                             } else if (spl->next) { // there's a next subpath
2498                                 last = NULL; // to be set later to the last node of last subpath
2499                             } else {
2500                                 last = (Inkscape::NodePath::Node *) subpath->last;
2501                             }
2502                         }
2503                     }
2504                 }
2505             }
2506         }
2507         sp_nodepath_deselect(nodepath);
2508     }
2510     if (last) { // there's at least one more node before selected
2511         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2512     } else { // no more nodes, select the last one in last subpath
2513         GList *spl = g_list_last(nodepath->subpaths);
2514        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2515         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2516     }
2519 /**
2520  * \brief Select all nodes that are within the rectangle.
2521  */
2522 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2524     if (!incremental) {
2525         sp_nodepath_deselect(nodepath);
2526     }
2528     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2529        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2530         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2531            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2533             if (b.contains(node->pos)) {
2534                 sp_nodepath_node_select(node, TRUE, TRUE);
2535             }
2536         }
2537     }
2541 void
2542 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2544     g_assert (n);
2545     g_assert (nodepath);
2546     g_assert (n->subpath->nodepath == nodepath);
2548     if (g_list_length (nodepath->selected) == 0) {
2549         if (grow > 0) {
2550             sp_nodepath_node_select(n, TRUE, TRUE);
2551         }
2552         return;
2553     }
2555     if (g_list_length (nodepath->selected) == 1) {
2556         if (grow < 0) {
2557             sp_nodepath_deselect (nodepath);
2558             return;
2559         }
2560     }
2562         double n_sel_range = 0, p_sel_range = 0;
2563             Inkscape::NodePath::Node *farthest_n_node = n;
2564             Inkscape::NodePath::Node *farthest_p_node = n;
2566         // Calculate ranges
2567         {
2568             double n_range = 0, p_range = 0;
2569             bool n_going = true, p_going = true;
2570             Inkscape::NodePath::Node *n_node = n;
2571             Inkscape::NodePath::Node *p_node = n;
2572             do {
2573                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2574                 if (n_node && n_going)
2575                     n_node = n_node->n.other;
2576                 if (n_node == NULL) {
2577                     n_going = false;
2578                 } else {
2579                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2580                     if (n_node->selected) {
2581                         n_sel_range = n_range;
2582                         farthest_n_node = n_node;
2583                     }
2584                     if (n_node == p_node) {
2585                         n_going = false;
2586                         p_going = false;
2587                     }
2588                 }
2589                 if (p_node && p_going)
2590                     p_node = p_node->p.other;
2591                 if (p_node == NULL) {
2592                     p_going = false;
2593                 } else {
2594                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2595                     if (p_node->selected) {
2596                         p_sel_range = p_range;
2597                         farthest_p_node = p_node;
2598                     }
2599                     if (p_node == n_node) {
2600                         n_going = false;
2601                         p_going = false;
2602                     }
2603                 }
2604             } while (n_going || p_going);
2605         }
2607     if (grow > 0) {
2608         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2609                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2610         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2611                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2612         }
2613     } else {
2614         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2615                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2616         } else if (farthest_p_node && farthest_p_node->selected) {
2617                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2618         }
2619     }
2622 void
2623 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2625     g_assert (n);
2626     g_assert (nodepath);
2627     g_assert (n->subpath->nodepath == nodepath);
2629     if (g_list_length (nodepath->selected) == 0) {
2630         if (grow > 0) {
2631             sp_nodepath_node_select(n, TRUE, TRUE);
2632         }
2633         return;
2634     }
2636     if (g_list_length (nodepath->selected) == 1) {
2637         if (grow < 0) {
2638             sp_nodepath_deselect (nodepath);
2639             return;
2640         }
2641     }
2643     Inkscape::NodePath::Node *farthest_selected = NULL;
2644     double farthest_dist = 0;
2646     Inkscape::NodePath::Node *closest_unselected = NULL;
2647     double closest_dist = NR_HUGE;
2649     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2650        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2651         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2652            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2653            if (node == n)
2654                continue;
2655            if (node->selected) {
2656                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2657                    farthest_dist = NR::L2(node->pos - n->pos);
2658                    farthest_selected = node;
2659                }
2660            } else {
2661                if (NR::L2(node->pos - n->pos) < closest_dist) {
2662                    closest_dist = NR::L2(node->pos - n->pos);
2663                    closest_unselected = node;
2664                }
2665            }
2666         }
2667     }
2669     if (grow > 0) {
2670         if (closest_unselected) {
2671             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2672         }
2673     } else {
2674         if (farthest_selected) {
2675             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2676         }
2677     }
2681 /**
2682 \brief  Saves all nodes' and handles' current positions in their origin members
2683 */
2684 void
2685 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2687     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2688        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2689         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2690            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2691            n->origin = n->pos;
2692            n->p.origin = n->p.pos;
2693            n->n.origin = n->n.pos;
2694         }
2695     }
2696
2698 /**
2699 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2700 */
2701 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2703     if (!nodepath->selected) {
2704         return NULL;
2705     }
2707     GList *r = NULL;
2708     guint i = 0;
2709     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2710        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2711         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2712            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2713             i++;
2714             if (node->selected) {
2715                 r = g_list_append(r, GINT_TO_POINTER(i));
2716             }
2717         }
2718     }
2719     return r;
2722 /**
2723 \brief  Restores selection by selecting nodes whose positions are in the list
2724 */
2725 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2727     sp_nodepath_deselect(nodepath);
2729     guint i = 0;
2730     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2731        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2732         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2733            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2734             i++;
2735             if (g_list_find(r, GINT_TO_POINTER(i))) {
2736                 sp_nodepath_node_select(node, TRUE, TRUE);
2737             }
2738         }
2739     }
2743 /**
2744 \brief Adjusts handle according to node type and line code.
2745 */
2746 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2748     double len, otherlen, linelen;
2750     g_assert(node);
2752    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2753    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2755     /** \todo fixme: */
2756     if (me->other == NULL) return;
2757     if (other->other == NULL) return;
2759     /* I have line */
2761     NRPathcode mecode, ocode;
2762     if (which_adjust == 1) {
2763         mecode = (NRPathcode)me->other->code;
2764         ocode = (NRPathcode)node->code;
2765     } else {
2766         mecode = (NRPathcode)node->code;
2767         ocode = (NRPathcode)other->other->code;
2768     }
2770     if (mecode == NR_LINETO) return;
2772     /* I am curve */
2774     if (other->other == NULL) return;
2776     /* Other has line */
2778     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2780     NR::Point delta;
2781     if (ocode == NR_LINETO) {
2782         /* other is lineto, we are either smooth or symm */
2783        Inkscape::NodePath::Node *othernode = other->other;
2784         len = NR::L2(me->pos - node->pos);
2785         delta = node->pos - othernode->pos;
2786         linelen = NR::L2(delta);
2787         if (linelen < 1e-18) 
2788             return;
2789         me->pos = node->pos + (len / linelen)*delta;
2790         return;
2791     }
2793     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2795         me->pos = 2 * node->pos - other->pos;
2796         return;
2797     }
2799     /* We are smooth */
2801     len = NR::L2(me->pos - node->pos);
2802     delta = other->pos - node->pos;
2803     otherlen = NR::L2(delta);
2804     if (otherlen < 1e-18) return;
2806     me->pos = node->pos - (len / otherlen) * delta;
2809 /**
2810  \brief Adjusts both handles according to node type and line code
2811  */
2812 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2814     g_assert(node);
2816     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2818     /* we are either smooth or symm */
2820     if (node->p.other == NULL) return;
2822     if (node->n.other == NULL) return;
2824     if (node->code == NR_LINETO) {
2825         if (node->n.other->code == NR_LINETO) return;
2826         sp_node_adjust_handle(node, 1);
2827         return;
2828     }
2830     if (node->n.other->code == NR_LINETO) {
2831         if (node->code == NR_LINETO) return;
2832         sp_node_adjust_handle(node, -1);
2833         return;
2834     }
2836     /* both are curves */
2837     NR::Point const delta( node->n.pos - node->p.pos );
2839     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2840         node->p.pos = node->pos - delta / 2;
2841         node->n.pos = node->pos + delta / 2;
2842         return;
2843     }
2845     /* We are smooth */
2846     double plen = NR::L2(node->p.pos - node->pos);
2847     if (plen < 1e-18) return;
2848     double nlen = NR::L2(node->n.pos - node->pos);
2849     if (nlen < 1e-18) return;
2850     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2851     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2854 /**
2855  * Node event callback.
2856  */
2857 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2859     gboolean ret = FALSE;
2860     switch (event->type) {
2861         case GDK_ENTER_NOTIFY:
2862             active_node = n;
2863             break;
2864         case GDK_LEAVE_NOTIFY:
2865             active_node = NULL;
2866             break;
2867         case GDK_KEY_PRESS:
2868             switch (get_group0_keyval (&event->key)) {
2869                 case GDK_space:
2870                     if (event->key.state & GDK_BUTTON1_MASK) {
2871                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2872                         stamp_repr(nodepath);
2873                         ret = TRUE;
2874                     }
2875                     break;
2876                 case GDK_Page_Up:
2877                     if (event->key.state & GDK_CONTROL_MASK) {
2878                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2879                     } else {
2880                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2881                     }
2882                     break;
2883                 case GDK_Page_Down:
2884                     if (event->key.state & GDK_CONTROL_MASK) {
2885                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2886                     } else {
2887                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2888                     }
2889                     break;
2890                 default:
2891                     break;
2892             }
2893             break;
2894         default:
2895             break;
2896     }
2898     return ret;
2901 /**
2902  * Handle keypress on node; directly called.
2903  */
2904 gboolean node_key(GdkEvent *event)
2906     Inkscape::NodePath::Path *np;
2908     // there is no way to verify nodes so set active_node to nil when deleting!!
2909     if (active_node == NULL) return FALSE;
2911     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2912         gint ret = FALSE;
2913         switch (get_group0_keyval (&event->key)) {
2914             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2915             case GDK_BackSpace:
2916                 np = active_node->subpath->nodepath;
2917                 sp_nodepath_node_destroy(active_node);
2918                 sp_nodepath_update_repr(np, _("Delete node"));
2919                 active_node = NULL;
2920                 ret = TRUE;
2921                 break;
2922             case GDK_c:
2923                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2924                 ret = TRUE;
2925                 break;
2926             case GDK_s:
2927                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2928                 ret = TRUE;
2929                 break;
2930             case GDK_y:
2931                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2932                 ret = TRUE;
2933                 break;
2934             case GDK_b:
2935                 sp_nodepath_node_break(active_node);
2936                 ret = TRUE;
2937                 break;
2938         }
2939         return ret;
2940     }
2941     return FALSE;
2944 /**
2945  * Mouseclick on node callback.
2946  */
2947 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2949    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2951     if (state & GDK_CONTROL_MASK) {
2952         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2954         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2955             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2956                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2957             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2958                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2959             } else {
2960                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2961             }
2962             sp_nodepath_update_repr(nodepath, _("Change node type"));
2963             sp_nodepath_update_statusbar(nodepath);
2965         } else { //ctrl+alt+click: delete node
2966             GList *node_to_delete = NULL;
2967             node_to_delete = g_list_append(node_to_delete, n);
2968             sp_node_delete_preserve(node_to_delete);
2969         }
2971     } else {
2972         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2973     }
2976 /**
2977  * Mouse grabbed node callback.
2978  */
2979 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2981    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2983     if (!n->selected) {
2984         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2985     }
2987     n->is_dragging = true;
2988     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
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;
3002    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3004    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3007 /**
3008  * The point on a line, given by its angle, closest to the given point.
3009  * \param p  A point.
3010  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3011  * \param closest  Pointer to the point struct where the result is stored.
3012  * \todo FIXME: use dot product perhaps?
3013  */
3014 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3016     if (a == HUGE_VAL) { // vertical
3017         *closest = NR::Point(0, (*p)[NR::Y]);
3018     } else {
3019         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3020         (*closest)[NR::Y] = a * (*closest)[NR::X];
3021     }
3024 /**
3025  * Distance from the point to a line given by its angle.
3026  * \param p  A point.
3027  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3028  */
3029 static double point_line_distance(NR::Point *p, double a)
3031     NR::Point c;
3032     point_line_closest(p, a, &c);
3033     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]));
3036 /**
3037  * Callback for node "request" signal.
3038  * \todo fixme: This goes to "moved" event? (lauris)
3039  */
3040 static gboolean
3041 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3043     double yn, xn, yp, xp;
3044     double an, ap, na, pa;
3045     double d_an, d_ap, d_na, d_pa;
3046     gboolean collinear = FALSE;
3047     NR::Point c;
3048     NR::Point pr;
3050    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3052    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3053    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3055        NR::Point mouse = (*p);
3057        if (!n->dragging_out) {
3058            // This is the first drag-out event; find out which handle to drag out
3059            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3060            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3062            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3063                return FALSE;
3065            Inkscape::NodePath::NodeSide *opposite;
3066            if (appr_p > appr_n) { // closer to p
3067                n->dragging_out = &n->p;
3068                opposite = &n->n;
3069                n->code = NR_CURVETO;
3070            } else if (appr_p < appr_n) { // closer to n
3071                n->dragging_out = &n->n;
3072                opposite = &n->p;
3073                n->n.other->code = NR_CURVETO;
3074            } else { // p and n nodes are the same
3075                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3076                    n->dragging_out = &n->p;
3077                    opposite = &n->n;
3078                    n->code = NR_CURVETO;
3079                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3080                    n->dragging_out = &n->n;
3081                    opposite = &n->p;
3082                    n->n.other->code = NR_CURVETO;
3083                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3084                    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);
3085                    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);
3086                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3087                        n->dragging_out = &n->n;
3088                        opposite = &n->p;
3089                        n->n.other->code = NR_CURVETO;
3090                    } else { // closer to other's n handle
3091                        n->dragging_out = &n->p;
3092                        opposite = &n->n;
3093                        n->code = NR_CURVETO;
3094                    }
3095                }
3096            }
3098            // if there's another handle, make sure the one we drag out starts parallel to it
3099            if (opposite->pos != n->pos) {
3100                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3101            }
3103            // knots might not be created yet!
3104            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3105            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3106        }
3108        // pass this on to the handle-moved callback
3109        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3110        sp_node_update_handles(n);
3111        return TRUE;
3112    }
3114     if (state & GDK_CONTROL_MASK) { // constrained motion
3116         // calculate relative distances of handles
3117         // n handle:
3118         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3119         xn = n->n.pos[NR::X] - n->pos[NR::X];
3120         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3121         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3122             if (n->n.other) { // if there is the next point
3123                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3124                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3125                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3126             }
3127         }
3128         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3129         if (yn < 0) { xn = -xn; yn = -yn; }
3131         // p handle:
3132         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3133         xp = n->p.pos[NR::X] - n->pos[NR::X];
3134         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3135         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3136             if (n->p.other) {
3137                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3138                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3139                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3140             }
3141         }
3142         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3143         if (yp < 0) { xp = -xp; yp = -yp; }
3145         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3146             // sliding on handles, only if at least one of the handles is non-vertical
3147             // (otherwise it's the same as ctrl+drag anyway)
3149             // calculate angles of the handles
3150             if (xn == 0) {
3151                 if (yn == 0) { // no handle, consider it the continuation of the other one
3152                     an = 0;
3153                     collinear = TRUE;
3154                 }
3155                 else an = 0; // vertical; set the angle to horizontal
3156             } else an = yn/xn;
3158             if (xp == 0) {
3159                 if (yp == 0) { // no handle, consider it the continuation of the other one
3160                     ap = an;
3161                 }
3162                 else ap = 0; // vertical; set the angle to horizontal
3163             } else  ap = yp/xp;
3165             if (collinear) an = ap;
3167             // angles of the perpendiculars; HUGE_VAL means vertical
3168             if (an == 0) na = HUGE_VAL; else na = -1/an;
3169             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3171             // mouse point relative to the node's original pos
3172             pr = (*p) - n->origin;
3174             // distances to the four lines (two handles and two perpendiculars)
3175             d_an = point_line_distance(&pr, an);
3176             d_na = point_line_distance(&pr, na);
3177             d_ap = point_line_distance(&pr, ap);
3178             d_pa = point_line_distance(&pr, pa);
3180             // find out which line is the closest, save its closest point in c
3181             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3182                 point_line_closest(&pr, an, &c);
3183             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3184                 point_line_closest(&pr, ap, &c);
3185             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3186                 point_line_closest(&pr, na, &c);
3187             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3188                 point_line_closest(&pr, pa, &c);
3189             }
3191             // move the node to the closest point
3192             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3193                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3194                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3196         } else {  // constraining to hor/vert
3198             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3199                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3200             } else { // snap to vert
3201                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3202             }
3203         }
3204     } else { // move freely
3205         if (n->is_dragging) {
3206             if (state & GDK_MOD1_MASK) { // sculpt
3207                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3208             } else {
3209                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3210                                             (*p)[NR::X] - n->pos[NR::X],
3211                                             (*p)[NR::Y] - n->pos[NR::Y],
3212                                             (state & GDK_SHIFT_MASK) == 0);
3213             }
3214         }
3215     }
3217     n->subpath->nodepath->desktop->scroll_to_point(p);
3219     return TRUE;
3222 /**
3223  * Node handle clicked callback.
3224  */
3225 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3227    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3229     if (state & GDK_CONTROL_MASK) { // "delete" handle
3230         if (n->p.knot == knot) {
3231             n->p.pos = n->pos;
3232         } else if (n->n.knot == knot) {
3233             n->n.pos = n->pos;
3234         }
3235         sp_node_update_handles(n);
3236         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3237         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3238         sp_nodepath_update_statusbar(nodepath);
3240     } else { // just select or add to selection, depending in Shift
3241         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3242     }
3245 /**
3246  * Node handle grabbed callback.
3247  */
3248 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3250    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3252     if (!n->selected) {
3253         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3254     }
3256     // remember the origin point of the handle
3257     if (n->p.knot == knot) {
3258         n->p.origin_radial = n->p.pos - n->pos;
3259     } else if (n->n.knot == knot) {
3260         n->n.origin_radial = n->n.pos - n->pos;
3261     } else {
3262         g_assert_not_reached();
3263     }
3265     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3268 /**
3269  * Node handle ungrabbed callback.
3270  */
3271 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3273    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3275     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3276     if (n->p.knot == knot) {
3277         n->p.origin_radial.a = 0;
3278         sp_knot_set_position(knot, &n->p.pos, state);
3279     } else if (n->n.knot == knot) {
3280         n->n.origin_radial.a = 0;
3281         sp_knot_set_position(knot, &n->n.pos, state);
3282     } else {
3283         g_assert_not_reached();
3284     }
3286     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3287     sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3290 /**
3291  * Node handle "request" signal callback.
3292  */
3293 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3295     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3297     Inkscape::NodePath::NodeSide *me, *opposite;
3298     gint which;
3299     if (n->p.knot == knot) {
3300         me = &n->p;
3301         opposite = &n->n;
3302         which = -1;
3303     } else if (n->n.knot == knot) {
3304         me = &n->n;
3305         opposite = &n->p;
3306         which = 1;
3307     } else {
3308         me = opposite = NULL;
3309         which = 0;
3310         g_assert_not_reached();
3311     }
3313     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3315     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3317     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3318         /* We are smooth node adjacent with line */
3319         NR::Point const delta = *p - n->pos;
3320         NR::Coord const len = NR::L2(delta);
3321         Inkscape::NodePath::Node *othernode = opposite->other;
3322         NR::Point const ndelta = n->pos - othernode->pos;
3323         NR::Coord const linelen = NR::L2(ndelta);
3324         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3325             NR::Coord const scal = dot(delta, ndelta) / linelen;
3326             (*p) = n->pos + (scal / linelen) * ndelta;
3327         }
3328         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3329     } else {
3330         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3331     }
3333     sp_node_adjust_handle(n, -which);
3335     return FALSE;
3338 /**
3339  * Node handle moved callback.
3340  */
3341 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3343    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3345    Inkscape::NodePath::NodeSide *me;
3346    Inkscape::NodePath::NodeSide *other;
3347     if (n->p.knot == knot) {
3348         me = &n->p;
3349         other = &n->n;
3350     } else if (n->n.knot == knot) {
3351         me = &n->n;
3352         other = &n->p;
3353     } else {
3354         me = NULL;
3355         other = NULL;
3356         g_assert_not_reached();
3357     }
3359     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3360     Radial rme(me->pos - n->pos);
3361     Radial rother(other->pos - n->pos);
3362     Radial rnew(*p - n->pos);
3364     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3365         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3366         /* 0 interpreted as "no snapping". */
3368         // The closest PI/snaps angle, starting from zero.
3369         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3370         if (me->origin_radial.a == HUGE_VAL) {
3371             // ortho doesn't exist: original handle was zero length.
3372             rnew.a = a_snapped;
3373         } else {
3374             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3375              * its opposite and perpendiculars). */
3376             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3378             // Snap to the closest.
3379             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3380                        ? a_snapped
3381                        : a_ortho );
3382         }
3383     }
3385     if (state & GDK_MOD1_MASK) {
3386         // lock handle length
3387         rnew.r = me->origin_radial.r;
3388     }
3390     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3391         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3392         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3393         rother.a += rnew.a - rme.a;
3394         other->pos = NR::Point(rother) + n->pos;
3395         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3396         sp_knot_set_position(other->knot, &other->pos, 0);
3397     }
3399     me->pos = NR::Point(rnew) + n->pos;
3400     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3402     // this is what sp_knot_set_position does, but without emitting the signal:
3403     // we cannot emit a "moved" signal because we're now processing it
3404     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3406     knot->desktop->set_coordinate_status(me->pos);
3408     update_object(n->subpath->nodepath);
3410     /* status text */
3411     SPDesktop *desktop = n->subpath->nodepath->desktop;
3412     if (!desktop) return;
3413     SPEventContext *ec = desktop->event_context;
3414     if (!ec) return;
3415     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3416     if (!mc) return;
3418     double degrees = 180 / M_PI * rnew.a;
3419     if (degrees > 180) degrees -= 360;
3420     if (degrees < -180) degrees += 360;
3421     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3422         degrees = angle_to_compass (degrees);
3424     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3426     mc->setF(Inkscape::NORMAL_MESSAGE,
3427          _("<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);
3429     g_string_free(length, TRUE);
3432 /**
3433  * Node handle event callback.
3434  */
3435 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3437     gboolean ret = FALSE;
3438     switch (event->type) {
3439         case GDK_KEY_PRESS:
3440             switch (get_group0_keyval (&event->key)) {
3441                 case GDK_space:
3442                     if (event->key.state & GDK_BUTTON1_MASK) {
3443                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3444                         stamp_repr(nodepath);
3445                         ret = TRUE;
3446                     }
3447                     break;
3448                 default:
3449                     break;
3450             }
3451             break;
3452         default:
3453             break;
3454     }
3456     return ret;
3459 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3460                                  Radial &rme, Radial &rother, gboolean const both)
3462     rme.a += angle;
3463     if ( both
3464          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3465          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3466     {
3467         rother.a += angle;
3468     }
3471 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3472                                         Radial &rme, Radial &rother, gboolean const both)
3474     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3476     gdouble r;
3477     if ( both
3478          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3479          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3480     {
3481         r = MAX(rme.r, rother.r);
3482     } else {
3483         r = rme.r;
3484     }
3486     gdouble const weird_angle = atan2(norm_angle, r);
3487 /* Bulia says norm_angle is just the visible distance that the
3488  * object's end must travel on the screen.  Left as 'angle' for want of
3489  * a better name.*/
3491     rme.a += weird_angle;
3492     if ( both
3493          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3494          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3495     {
3496         rother.a += weird_angle;
3497     }
3500 /**
3501  * Rotate one node.
3502  */
3503 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3505     Inkscape::NodePath::NodeSide *me, *other;
3506     bool both = false;
3508     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3509     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3511     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3512         me = &(n->p);
3513         other = &(n->n);
3514     } else if (!n->p.other) {
3515         me = &(n->n);
3516         other = &(n->p);
3517     } else {
3518         if (which > 0) { // right handle
3519             if (xn > xp) {
3520                 me = &(n->n);
3521                 other = &(n->p);
3522             } else {
3523                 me = &(n->p);
3524                 other = &(n->n);
3525             }
3526         } else if (which < 0){ // left handle
3527             if (xn <= xp) {
3528                 me = &(n->n);
3529                 other = &(n->p);
3530             } else {
3531                 me = &(n->p);
3532                 other = &(n->n);
3533             }
3534         } else { // both handles
3535             me = &(n->n);
3536             other = &(n->p);
3537             both = true;
3538         }
3539     }
3541     Radial rme(me->pos - n->pos);
3542     Radial rother(other->pos - n->pos);
3544     if (screen) {
3545         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3546     } else {
3547         node_rotate_one_internal (*n, angle, rme, rother, both);
3548     }
3550     me->pos = n->pos + NR::Point(rme);
3552     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3553         other->pos =  n->pos + NR::Point(rother);
3554     }
3556     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3557     // so here we just move all the knots without emitting move signals, for speed
3558     sp_node_update_handles(n, false);
3561 /**
3562  * Rotate selected nodes.
3563  */
3564 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3566     if (!nodepath || !nodepath->selected) return;
3568     if (g_list_length(nodepath->selected) == 1) {
3569        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3570         node_rotate_one (n, angle, which, screen);
3571     } else {
3572        // rotate as an object:
3574         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3575         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3576         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3577             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3578             box.expandTo (n->pos); // contain all selected nodes
3579         }
3581         gdouble rot;
3582         if (screen) {
3583             gdouble const zoom = nodepath->desktop->current_zoom();
3584             gdouble const zmove = angle / zoom;
3585             gdouble const r = NR::L2(box.max() - box.midpoint());
3586             rot = atan2(zmove, r);
3587         } else {
3588             rot = angle;
3589         }
3591         NR::Matrix t =
3592             NR::Matrix (NR::translate(-box.midpoint())) *
3593             NR::Matrix (NR::rotate(rot)) *
3594             NR::Matrix (NR::translate(box.midpoint()));
3596         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3597             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3598             n->pos *= t;
3599             n->n.pos *= t;
3600             n->p.pos *= t;
3601             sp_node_update_handles(n, false);
3602         }
3603     }
3605     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3608 /**
3609  * Scale one node.
3610  */
3611 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3613     bool both = false;
3614     Inkscape::NodePath::NodeSide *me, *other;
3616     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3617     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3619     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3620         me = &(n->p);
3621         other = &(n->n);
3622         n->code = NR_CURVETO;
3623     } else if (!n->p.other) {
3624         me = &(n->n);
3625         other = &(n->p);
3626         if (n->n.other)
3627             n->n.other->code = NR_CURVETO;
3628     } else {
3629         if (which > 0) { // right handle
3630             if (xn > xp) {
3631                 me = &(n->n);
3632                 other = &(n->p);
3633                 if (n->n.other)
3634                     n->n.other->code = NR_CURVETO;
3635             } else {
3636                 me = &(n->p);
3637                 other = &(n->n);
3638                 n->code = NR_CURVETO;
3639             }
3640         } else if (which < 0){ // left handle
3641             if (xn <= xp) {
3642                 me = &(n->n);
3643                 other = &(n->p);
3644                 if (n->n.other)
3645                     n->n.other->code = NR_CURVETO;
3646             } else {
3647                 me = &(n->p);
3648                 other = &(n->n);
3649                 n->code = NR_CURVETO;
3650             }
3651         } else { // both handles
3652             me = &(n->n);
3653             other = &(n->p);
3654             both = true;
3655             n->code = NR_CURVETO;
3656             if (n->n.other)
3657                 n->n.other->code = NR_CURVETO;
3658         }
3659     }
3661     Radial rme(me->pos - n->pos);
3662     Radial rother(other->pos - n->pos);
3664     rme.r += grow;
3665     if (rme.r < 0) rme.r = 0;
3666     if (rme.a == HUGE_VAL) {
3667         if (me->other) { // if direction is unknown, initialize it towards the next node
3668             Radial rme_next(me->other->pos - n->pos);
3669             rme.a = rme_next.a;
3670         } else { // if there's no next, initialize to 0
3671             rme.a = 0;
3672         }
3673     }
3674     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3675         rother.r += grow;
3676         if (rother.r < 0) rother.r = 0;
3677         if (rother.a == HUGE_VAL) {
3678             rother.a = rme.a + M_PI;
3679         }
3680     }
3682     me->pos = n->pos + NR::Point(rme);
3684     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3685         other->pos = n->pos + NR::Point(rother);
3686     }
3688     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3689     // so here we just move all the knots without emitting move signals, for speed
3690     sp_node_update_handles(n, false);
3693 /**
3694  * Scale selected nodes.
3695  */
3696 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3698     if (!nodepath || !nodepath->selected) return;
3700     if (g_list_length(nodepath->selected) == 1) {
3701         // scale handles of the single selected node
3702         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3703         node_scale_one (n, grow, which);
3704     } else {
3705         // scale nodes as an "object":
3707         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3708         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3709         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3710             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3711             box.expandTo (n->pos); // contain all selected nodes
3712         }
3714         double scale = (box.maxExtent() + grow)/box.maxExtent();
3716         NR::Matrix t =
3717             NR::Matrix (NR::translate(-box.midpoint())) *
3718             NR::Matrix (NR::scale(scale, scale)) *
3719             NR::Matrix (NR::translate(box.midpoint()));
3721         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3722             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3723             n->pos *= t;
3724             n->n.pos *= t;
3725             n->p.pos *= t;
3726             sp_node_update_handles(n, false);
3727         }
3728     }
3730     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3733 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3735     if (!nodepath) return;
3736     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3739 /**
3740  * Flip selected nodes horizontally/vertically.
3741  */
3742 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3744     if (!nodepath || !nodepath->selected) return;
3746     if (g_list_length(nodepath->selected) == 1) {
3747         // flip handles of the single selected node
3748         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3749         double temp = n->p.pos[axis];
3750         n->p.pos[axis] = n->n.pos[axis];
3751         n->n.pos[axis] = temp;
3752         sp_node_update_handles(n, false);
3753     } else {
3754         // scale nodes as an "object":
3756         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3757         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3758         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3759             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3760             box.expandTo (n->pos); // contain all selected nodes
3761         }
3763         NR::Matrix t =
3764             NR::Matrix (NR::translate(-box.midpoint())) *
3765             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3766             NR::Matrix (NR::translate(box.midpoint()));
3768         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3769             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3770             n->pos *= t;
3771             n->n.pos *= t;
3772             n->p.pos *= t;
3773             sp_node_update_handles(n, false);
3774         }
3775     }
3777     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3780 //-----------------------------------------------
3781 /**
3782  * Return new subpath under given nodepath.
3783  */
3784 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3786     g_assert(nodepath);
3787     g_assert(nodepath->desktop);
3789    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3791     s->nodepath = nodepath;
3792     s->closed = FALSE;
3793     s->nodes = NULL;
3794     s->first = NULL;
3795     s->last = NULL;
3797     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3798     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3799     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3801     return s;
3804 /**
3805  * Destroy nodes in subpath, then subpath itself.
3806  */
3807 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3809     g_assert(subpath);
3810     g_assert(subpath->nodepath);
3811     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3813     while (subpath->nodes) {
3814         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3815     }
3817     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3819     g_free(subpath);
3822 /**
3823  * Link head to tail in subpath.
3824  */
3825 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3827     g_assert(!sp->closed);
3828     g_assert(sp->last != sp->first);
3829     g_assert(sp->first->code == NR_MOVETO);
3831     sp->closed = TRUE;
3833     //Link the head to the tail
3834     sp->first->p.other = sp->last;
3835     sp->last->n.other  = sp->first;
3836     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3837     sp->first          = sp->last;
3839     //Remove the extra end node
3840     sp_nodepath_node_destroy(sp->last->n.other);
3843 /**
3844  * Open closed (loopy) subpath at node.
3845  */
3846 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3848     g_assert(sp->closed);
3849     g_assert(n->subpath == sp);
3850     g_assert(sp->first == sp->last);
3852     /* We create new startpoint, current node will become last one */
3854    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3855                                                 &n->pos, &n->pos, &n->n.pos);
3858     sp->closed        = FALSE;
3860     //Unlink to make a head and tail
3861     sp->first         = new_path;
3862     sp->last          = n;
3863     n->n.other        = NULL;
3864     new_path->p.other = NULL;
3867 /**
3868  * Returns area in triangle given by points; may be negative.
3869  */
3870 inline double
3871 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3873     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]);
3876 /**
3877  * Return new node in subpath with given properties.
3878  * \param pos Position of node.
3879  * \param ppos Handle position in previous direction
3880  * \param npos Handle position in previous direction
3881  */
3882 Inkscape::NodePath::Node *
3883 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)
3885     g_assert(sp);
3886     g_assert(sp->nodepath);
3887     g_assert(sp->nodepath->desktop);
3889     if (nodechunk == NULL)
3890         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3892     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3894     n->subpath  = sp;
3896     if (type != Inkscape::NodePath::NODE_NONE) {
3897         // use the type from sodipodi:nodetypes
3898         n->type = type;
3899     } else {
3900         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3901             // points are (almost) collinear
3902             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3903                 // endnode, or a node with a retracted handle
3904                 n->type = Inkscape::NodePath::NODE_CUSP;
3905             } else {
3906                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3907             }
3908         } else {
3909             n->type = Inkscape::NodePath::NODE_CUSP;
3910         }
3911     }
3913     n->code     = code;
3914     n->selected = FALSE;
3915     n->pos      = *pos;
3916     n->p.pos    = *ppos;
3917     n->n.pos    = *npos;
3919     n->dragging_out = NULL;
3921     Inkscape::NodePath::Node *prev;
3922     if (next) {
3923         //g_assert(g_list_find(sp->nodes, next));
3924         prev = next->p.other;
3925     } else {
3926         prev = sp->last;
3927     }
3929     if (prev)
3930         prev->n.other = n;
3931     else
3932         sp->first = n;
3934     if (next)
3935         next->p.other = n;
3936     else
3937         sp->last = n;
3939     n->p.other = prev;
3940     n->n.other = next;
3942     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"));
3943     sp_knot_set_position(n->knot, pos, 0);
3945     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3946     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3947     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3948     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3949     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3950     sp_knot_update_ctrl(n->knot);
3952     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3953     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3954     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3955     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3956     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3957     sp_knot_show(n->knot);
3959     // We only create handle knots and lines on demand
3960     n->p.knot = NULL;
3961     n->p.line = NULL;
3962     n->n.knot = NULL;
3963     n->n.line = NULL;
3965     sp->nodes = g_list_prepend(sp->nodes, n);
3967     return n;
3970 /**
3971  * Destroy node and its knots, link neighbors in subpath.
3972  */
3973 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3975     g_assert(node);
3976     g_assert(node->subpath);
3977     g_assert(SP_IS_KNOT(node->knot));
3979    Inkscape::NodePath::SubPath *sp = node->subpath;
3981     if (node->selected) { // first, deselect
3982         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3983         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3984     }
3986     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3988     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
3989     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
3990     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
3991     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
3992     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
3993     g_object_unref(G_OBJECT(node->knot));
3995     if (node->p.knot) {
3996         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
3997         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
3998         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
3999         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4000         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4001         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4002         g_object_unref(G_OBJECT(node->p.knot));
4003     }
4005     if (node->n.knot) {
4006         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4007         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4008         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4009         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4010         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4011         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4012         g_object_unref(G_OBJECT(node->n.knot));
4013     }
4015     if (node->p.line)
4016         gtk_object_destroy(GTK_OBJECT(node->p.line));
4017     if (node->n.line)
4018         gtk_object_destroy(GTK_OBJECT(node->n.line));
4020     if (sp->nodes) { // there are others nodes on the subpath
4021         if (sp->closed) {
4022             if (sp->first == node) {
4023                 g_assert(sp->last == node);
4024                 sp->first = node->n.other;
4025                 sp->last = sp->first;
4026             }
4027             node->p.other->n.other = node->n.other;
4028             node->n.other->p.other = node->p.other;
4029         } else {
4030             if (sp->first == node) {
4031                 sp->first = node->n.other;
4032                 sp->first->code = NR_MOVETO;
4033             }
4034             if (sp->last == node) sp->last = node->p.other;
4035             if (node->p.other) node->p.other->n.other = node->n.other;
4036             if (node->n.other) node->n.other->p.other = node->p.other;
4037         }
4038     } else { // this was the last node on subpath
4039         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4040     }
4042     g_mem_chunk_free(nodechunk, node);
4045 /**
4046  * Returns one of the node's two sides.
4047  * \param which Indicates which side.
4048  * \return Pointer to previous node side if which==-1, next if which==1.
4049  */
4050 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4052     g_assert(node);
4054     switch (which) {
4055         case -1:
4056             return &node->p;
4057         case 1:
4058             return &node->n;
4059         default:
4060             break;
4061     }
4063     g_assert_not_reached();
4065     return NULL;
4068 /**
4069  * Return the other side of the node, given one of its sides.
4070  */
4071 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4073     g_assert(node);
4075     if (me == &node->p) return &node->n;
4076     if (me == &node->n) return &node->p;
4078     g_assert_not_reached();
4080     return NULL;
4083 /**
4084  * Return NRPathcode on the given side of the node.
4085  */
4086 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4088     g_assert(node);
4090     if (me == &node->p) {
4091         if (node->p.other) return (NRPathcode)node->code;
4092         return NR_MOVETO;
4093     }
4095     if (me == &node->n) {
4096         if (node->n.other) return (NRPathcode)node->n.other->code;
4097         return NR_MOVETO;
4098     }
4100     g_assert_not_reached();
4102     return NR_END;
4105 /**
4106  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4107  */
4108 Inkscape::NodePath::Node *
4109 sp_nodepath_get_node_by_index(int index)
4111     Inkscape::NodePath::Node *e = NULL;
4113     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4114     if (!nodepath) {
4115         return e;
4116     }
4118     //find segment
4119     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4121         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4122         int n = g_list_length(sp->nodes);
4123         if (sp->closed) {
4124             n++;
4125         }
4127         //if the piece belongs to this subpath grab it
4128         //otherwise move onto the next subpath
4129         if (index < n) {
4130             e = sp->first;
4131             for (int i = 0; i < index; ++i) {
4132                 e = e->n.other;
4133             }
4134             break;
4135         } else {
4136             if (sp->closed) {
4137                 index -= (n+1);
4138             } else {
4139                 index -= n;
4140             }
4141         }
4142     }
4144     return e;
4147 /**
4148  * Returns plain text meaning of node type.
4149  */
4150 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4152     unsigned retracted = 0;
4153     bool endnode = false;
4155     for (int which = -1; which <= 1; which += 2) {
4156         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4157         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4158             retracted ++;
4159         if (!side->other)
4160             endnode = true;
4161     }
4163     if (retracted == 0) {
4164         if (endnode) {
4165                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4166                 return _("end node");
4167         } else {
4168             switch (node->type) {
4169                 case Inkscape::NodePath::NODE_CUSP:
4170                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4171                     return _("cusp");
4172                 case Inkscape::NodePath::NODE_SMOOTH:
4173                     // TRANSLATORS: "smooth" is an adjective here
4174                     return _("smooth");
4175                 case Inkscape::NodePath::NODE_SYMM:
4176                     return _("symmetric");
4177             }
4178         }
4179     } else if (retracted == 1) {
4180         if (endnode) {
4181             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4182             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4183         } else {
4184             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4185         }
4186     } else {
4187         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4188     }
4190     return NULL;
4193 /**
4194  * Handles content of statusbar as long as node tool is active.
4195  */
4196 void
4197 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
4199     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");
4200     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4202     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4203     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4204     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4205     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4207     SPDesktop *desktop = NULL;
4208     if (nodepath) {
4209         desktop = nodepath->desktop;
4210     } else {
4211         desktop = SP_ACTIVE_DESKTOP;
4212     }
4214     SPEventContext *ec = desktop->event_context;
4215     if (!ec) return;
4216     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4217     if (!mc) return;
4219     if (selected_nodes == 0) {
4220         Inkscape::Selection *sel = desktop->selection;
4221         if (!sel || sel->isEmpty()) {
4222             mc->setF(Inkscape::NORMAL_MESSAGE,
4223                      _("Select a single object to edit its nodes or handles."));
4224         } else {
4225             if (nodepath) {
4226             mc->setF(Inkscape::NORMAL_MESSAGE,
4227                      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.",
4228                               "<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.",
4229                               total_nodes),
4230                      total_nodes);
4231             } else {
4232                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4233                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4234                 } else {
4235                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4236                 }
4237             }
4238         }
4239     } else if (nodepath && selected_nodes == 1) {
4240         mc->setF(Inkscape::NORMAL_MESSAGE,
4241                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4242                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4243                           total_nodes),
4244                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4245     } else {
4246         if (selected_subpaths > 1) {
4247             mc->setF(Inkscape::NORMAL_MESSAGE,
4248                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4249                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4250                               total_nodes),
4251                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4252         } else {
4253             mc->setF(Inkscape::NORMAL_MESSAGE,
4254                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4255                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4256                               total_nodes),
4257                      selected_nodes, total_nodes, when_selected);
4258         }
4259     }
4263 /*
4264   Local Variables:
4265   mode:c++
4266   c-file-style:"stroustrup"
4267   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4268   indent-tabs-mode:nil
4269   fill-column:99
4270   End:
4271 */
4272 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :