Code

8371c8241825d00a153c1305b2de858b44c75a0a
[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(int node, double t, NR::Point delta)
1613     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1615     //fixme: e and e->p can be NULL, so check for those before proceeding
1616     g_return_if_fail(e != NULL);
1617     g_return_if_fail(&e->p != NULL);
1618     
1619     /* feel good is an arbitrary parameter that distributes the delta between handles
1620      * if t of the drag point is less than 1/6 distance form the endpoint only
1621      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1622      */
1623     double feel_good;
1624     if (t <= 1.0 / 6.0)
1625         feel_good = 0;
1626     else if (t <= 0.5)
1627         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1628     else if (t <= 5.0 / 6.0)
1629         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1630     else
1631         feel_good = 1;
1633     //if we're dragging a line convert it to a curve
1634     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1635         sp_nodepath_set_line_type(e, NR_CURVETO);
1636     }
1638     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1639     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1640     e->p.other->n.pos += offsetcoord0;
1641     e->p.pos += offsetcoord1;
1643     // adjust handles of adjacent nodes where necessary
1644     sp_node_adjust_handle(e,1);
1645     sp_node_adjust_handle(e->p.other,-1);
1647     sp_nodepath_update_handles(e->subpath->nodepath);
1649     update_object(e->subpath->nodepath);
1651     sp_nodepath_update_statusbar(e->subpath->nodepath);
1655 /**
1656  * Call sp_nodepath_break() for all selected segments.
1657  */
1658 void sp_node_selected_break()
1660     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1661     if (!nodepath) return;
1663     GList *temp = NULL;
1664     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1665        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1666        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1667         if (nn == NULL) continue; // no break, no new node
1668         temp = g_list_prepend(temp, nn);
1669     }
1671     if (temp) {
1672         sp_nodepath_deselect(nodepath);
1673     }
1674     for (GList *l = temp; l != NULL; l = l->next) {
1675         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1676     }
1678     sp_nodepath_update_handles(nodepath);
1680     sp_nodepath_update_repr(nodepath, _("Break path"));
1683 /**
1684  * Duplicate the selected node(s).
1685  */
1686 void sp_node_selected_duplicate()
1688     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1689     if (!nodepath) {
1690         return;
1691     }
1693     GList *temp = NULL;
1694     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1695        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1696        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1697         if (nn == NULL) continue; // could not duplicate
1698         temp = g_list_prepend(temp, nn);
1699     }
1701     if (temp) {
1702         sp_nodepath_deselect(nodepath);
1703     }
1704     for (GList *l = temp; l != NULL; l = l->next) {
1705         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1706     }
1708     sp_nodepath_update_handles(nodepath);
1710     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1713 /**
1714  *  Join two nodes by merging them into one.
1715  */
1716 void sp_node_selected_join()
1718     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1719     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1721     if (g_list_length(nodepath->selected) != 2) {
1722         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1723         return;
1724     }
1726    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1727    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1729     g_assert(a != b);
1730     g_assert(a->p.other || a->n.other);
1731     g_assert(b->p.other || b->n.other);
1733     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1734         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1735         return;
1736     }
1738     /* a and b are endpoints */
1740     NR::Point c;
1741     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1742         c = a->pos;
1743     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1744         c = b->pos;
1745     } else {
1746         c = (a->pos + b->pos) / 2;
1747     }
1749     if (a->subpath == b->subpath) {
1750        Inkscape::NodePath::SubPath *sp = a->subpath;
1751         sp_nodepath_subpath_close(sp);
1752         sp_node_moveto (sp->first, c);
1754         sp_nodepath_update_handles(sp->nodepath);
1755         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1756         return;
1757     }
1759     /* a and b are separate subpaths */
1760    Inkscape::NodePath::SubPath *sa = a->subpath;
1761    Inkscape::NodePath::SubPath *sb = b->subpath;
1762     NR::Point p;
1763    Inkscape::NodePath::Node *n;
1764     NRPathcode code;
1765     if (a == sa->first) {
1766         p = sa->first->n.pos;
1767         code = (NRPathcode)sa->first->n.other->code;
1768        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1769         n = sa->last;
1770         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1771         n = n->p.other;
1772         while (n) {
1773             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1774             n = n->p.other;
1775             if (n == sa->first) n = NULL;
1776         }
1777         sp_nodepath_subpath_destroy(sa);
1778         sa = t;
1779     } else if (a == sa->last) {
1780         p = sa->last->p.pos;
1781         code = (NRPathcode)sa->last->code;
1782         sp_nodepath_node_destroy(sa->last);
1783     } else {
1784         code = NR_END;
1785         g_assert_not_reached();
1786     }
1788     if (b == sb->first) {
1789         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1790         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1791             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1792         }
1793     } else if (b == sb->last) {
1794         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1795         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1796             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1797         }
1798     } else {
1799         g_assert_not_reached();
1800     }
1801     /* and now destroy sb */
1803     sp_nodepath_subpath_destroy(sb);
1805     sp_nodepath_update_handles(sa->nodepath);
1807     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1809     sp_nodepath_update_statusbar(nodepath);
1812 /**
1813  *  Join two nodes by adding a segment between them.
1814  */
1815 void sp_node_selected_join_segment()
1817     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1818     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1820     if (g_list_length(nodepath->selected) != 2) {
1821         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1822         return;
1823     }
1825    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1826    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1828     g_assert(a != b);
1829     g_assert(a->p.other || a->n.other);
1830     g_assert(b->p.other || b->n.other);
1832     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1833         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1834         return;
1835     }
1837     if (a->subpath == b->subpath) {
1838        Inkscape::NodePath::SubPath *sp = a->subpath;
1840         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1841         sp->closed = TRUE;
1843         sp->first->p.other = sp->last;
1844         sp->last->n.other  = sp->first;
1846         sp_node_handle_mirror_p_to_n(sp->last);
1847         sp_node_handle_mirror_n_to_p(sp->first);
1849         sp->first->code = sp->last->code;
1850         sp->first       = sp->last;
1852         sp_nodepath_update_handles(sp->nodepath);
1854         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1856         return;
1857     }
1859     /* a and b are separate subpaths */
1860    Inkscape::NodePath::SubPath *sa = a->subpath;
1861    Inkscape::NodePath::SubPath *sb = b->subpath;
1863    Inkscape::NodePath::Node *n;
1864     NR::Point p;
1865     NRPathcode code;
1866     if (a == sa->first) {
1867         code = (NRPathcode) sa->first->n.other->code;
1868        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1869         n = sa->last;
1870         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1871         for (n = n->p.other; n != NULL; n = n->p.other) {
1872             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1873         }
1874         sp_nodepath_subpath_destroy(sa);
1875         sa = t;
1876     } else if (a == sa->last) {
1877         code = (NRPathcode)sa->last->code;
1878     } else {
1879         code = NR_END;
1880         g_assert_not_reached();
1881     }
1883     if (b == sb->first) {
1884         n = sb->first;
1885         sp_node_handle_mirror_p_to_n(sa->last);
1886         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1887         sp_node_handle_mirror_n_to_p(sa->last);
1888         for (n = n->n.other; n != NULL; n = n->n.other) {
1889             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1890         }
1891     } else if (b == sb->last) {
1892         n = sb->last;
1893         sp_node_handle_mirror_p_to_n(sa->last);
1894         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1895         sp_node_handle_mirror_n_to_p(sa->last);
1896         for (n = n->p.other; n != NULL; n = n->p.other) {
1897             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1898         }
1899     } else {
1900         g_assert_not_reached();
1901     }
1902     /* and now destroy sb */
1904     sp_nodepath_subpath_destroy(sb);
1906     sp_nodepath_update_handles(sa->nodepath);
1908     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1911 /**
1912  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1913  */
1914 void sp_node_delete_preserve(GList *nodes_to_delete)
1916     GSList *nodepaths = NULL;
1917     
1918     while (nodes_to_delete) {
1919         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1920         Inkscape::NodePath::SubPath *sp = node->subpath;
1921         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1922         Inkscape::NodePath::Node *sample_cursor = NULL;
1923         Inkscape::NodePath::Node *sample_end = NULL;
1924         Inkscape::NodePath::Node *delete_cursor = node;
1925         bool just_delete = false;
1926         
1927         //find the start of this contiguous selection
1928         //move left to the first node that is not selected
1929         //or the start of the non-closed path
1930         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1931             delete_cursor = curr;
1932         }
1934         //just delete at the beginning of an open path
1935         if (!delete_cursor->p.other) {
1936             sample_cursor = delete_cursor;
1937             just_delete = true;
1938         } else {
1939             sample_cursor = delete_cursor->p.other;
1940         }
1941         
1942         //calculate points for each segment
1943         int rate = 5;
1944         float period = 1.0 / rate;
1945         std::vector<NR::Point> data;
1946         if (!just_delete) {
1947             data.push_back(sample_cursor->pos);
1948             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1949                 //just delete at the end of an open path
1950                 if (!sp->closed && curr == sp->last) {
1951                     just_delete = true;
1952                     break;
1953                 }
1954                 
1955                 //sample points on the contiguous selected segment
1956                 NR::Point *bez;
1957                 bez = new NR::Point [4];
1958                 bez[0] = curr->pos;
1959                 bez[1] = curr->n.pos;
1960                 bez[2] = curr->n.other->p.pos;
1961                 bez[3] = curr->n.other->pos;
1962                 for (int i=1; i<rate; i++) {
1963                     gdouble t = i * period;
1964                     NR::Point p = bezier_pt(3, bez, t);
1965                     data.push_back(p);
1966                 }
1967                 data.push_back(curr->n.other->pos);
1969                 sample_end = curr->n.other;
1970                 //break if we've come full circle or hit the end of the selection
1971                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1972                     break;
1973                 }
1974             }
1975         }
1977         if (!just_delete) {
1978             //calculate the best fitting single segment and adjust the endpoints
1979             NR::Point *adata;
1980             adata = new NR::Point [data.size()];
1981             copy(data.begin(), data.end(), adata);
1982             
1983             NR::Point *bez;
1984             bez = new NR::Point [4];
1985             //would decreasing error create a better fitting approximation?
1986             gdouble error = 1.0;
1987             gint ret;
1988             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1990             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
1991             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
1992             //the resulting nodes behave as expected.
1993             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
1994             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
1995             
1996             //adjust endpoints
1997             sample_cursor->n.pos = bez[1];
1998             sample_end->p.pos = bez[2];
1999         }
2000        
2001         //destroy this contiguous selection
2002         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2003             Inkscape::NodePath::Node *temp = delete_cursor;
2004             if (delete_cursor->n.other == delete_cursor) {
2005                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2006                 delete_cursor = NULL; 
2007             } else {
2008                 delete_cursor = delete_cursor->n.other;
2009             }
2010             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2011             sp_nodepath_node_destroy(temp);
2012         }
2014         sp_nodepath_update_handles(nodepath);
2016         if (!g_slist_find(nodepaths, nodepath))
2017             nodepaths = g_slist_prepend (nodepaths, nodepath);
2018     }
2020     for (GSList *i = nodepaths; i; i = i->next) {
2021         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2022         // different nodepaths will give us one undo event per nodepath
2023         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2025         // if the entire nodepath is removed, delete the selected object.
2026         if (nodepath->subpaths == NULL ||
2027             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2028             //at least 2
2029             sp_nodepath_get_node_count(nodepath) < 2) {
2030             SPDocument *document = sp_desktop_document (nodepath->desktop);
2031             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2032             //delete this nodepath's object, not the entire selection! (though at this time, this
2033             //does not matter)
2034             sp_selection_delete();
2035             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2036                               _("Delete nodes"));
2037         } else {
2038             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2039             sp_nodepath_update_statusbar(nodepath);
2040         }
2041     }
2043     g_slist_free (nodepaths);
2046 /**
2047  * Delete one or more selected nodes.
2048  */
2049 void sp_node_selected_delete()
2051     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2052     if (!nodepath) return;
2053     if (!nodepath->selected) return;
2055     /** \todo fixme: do it the right way */
2056     while (nodepath->selected) {
2057        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2058         sp_nodepath_node_destroy(node);
2059     }
2062     //clean up the nodepath (such as for trivial subpaths)
2063     sp_nodepath_cleanup(nodepath);
2065     sp_nodepath_update_handles(nodepath);
2067     // if the entire nodepath is removed, delete the selected object.
2068     if (nodepath->subpaths == NULL ||
2069         sp_nodepath_get_node_count(nodepath) < 2) {
2070         SPDocument *document = sp_desktop_document (nodepath->desktop);
2071         sp_selection_delete();
2072         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2073                           _("Delete nodes"));
2074         return;
2075     }
2077     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2079     sp_nodepath_update_statusbar(nodepath);
2082 /**
2083  * Delete one or more segments between two selected nodes.
2084  * This is the code for 'split'.
2085  */
2086 void
2087 sp_node_selected_delete_segment(void)
2089    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2090    Inkscape::NodePath::Node *curr, *next;     //Iterators
2092     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2093     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2095     if (g_list_length(nodepath->selected) != 2) {
2096         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2097                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2098         return;
2099     }
2101     //Selected nodes, not inclusive
2102    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2103    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2105     if ( ( a==b)                       ||  //same node
2106          (a->subpath  != b->subpath )  ||  //not the same path
2107          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2108          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2109     {
2110         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2111                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2112         return;
2113     }
2115     //###########################################
2116     //# BEGIN EDITS
2117     //###########################################
2118     //##################################
2119     //# CLOSED PATH
2120     //##################################
2121     if (a->subpath->closed) {
2124         gboolean reversed = FALSE;
2126         //Since we can go in a circle, we need to find the shorter distance.
2127         //  a->b or b->a
2128         start = end = NULL;
2129         int distance    = 0;
2130         int minDistance = 0;
2131         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2132             if (curr==b) {
2133                 //printf("a to b:%d\n", distance);
2134                 start = a;//go from a to b
2135                 end   = b;
2136                 minDistance = distance;
2137                 //printf("A to B :\n");
2138                 break;
2139             }
2140             distance++;
2141         }
2143         //try again, the other direction
2144         distance = 0;
2145         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2146             if (curr==a) {
2147                 //printf("b to a:%d\n", distance);
2148                 if (distance < minDistance) {
2149                     start    = b;  //we go from b to a
2150                     end      = a;
2151                     reversed = TRUE;
2152                     //printf("B to A\n");
2153                 }
2154                 break;
2155             }
2156             distance++;
2157         }
2160         //Copy everything from 'end' to 'start' to a new subpath
2161        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2162         for (curr=end ; curr ; curr=curr->n.other) {
2163             NRPathcode code = (NRPathcode) curr->code;
2164             if (curr == end)
2165                 code = NR_MOVETO;
2166             sp_nodepath_node_new(t, NULL,
2167                                  (Inkscape::NodePath::NodeType)curr->type, code,
2168                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2169             if (curr == start)
2170                 break;
2171         }
2172         sp_nodepath_subpath_destroy(a->subpath);
2175     }
2179     //##################################
2180     //# OPEN PATH
2181     //##################################
2182     else {
2184         //We need to get the direction of the list between A and B
2185         //Can we walk from a to b?
2186         start = end = NULL;
2187         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2188             if (curr==b) {
2189                 start = a;  //did it!  we go from a to b
2190                 end   = b;
2191                 //printf("A to B\n");
2192                 break;
2193             }
2194         }
2195         if (!start) {//didn't work?  let's try the other direction
2196             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2197                 if (curr==a) {
2198                     start = b;  //did it!  we go from b to a
2199                     end   = a;
2200                     //printf("B to A\n");
2201                     break;
2202                 }
2203             }
2204         }
2205         if (!start) {
2206             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2207                                                      _("Cannot find path between nodes."));
2208             return;
2209         }
2213         //Copy everything after 'end' to a new subpath
2214        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2215         for (curr=end ; curr ; curr=curr->n.other) {
2216             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
2217                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2218         }
2220         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2221         for (curr = start->n.other ; curr  ; curr=next) {
2222             next = curr->n.other;
2223             sp_nodepath_node_destroy(curr);
2224         }
2226     }
2227     //###########################################
2228     //# END EDITS
2229     //###########################################
2231     //clean up the nodepath (such as for trivial subpaths)
2232     sp_nodepath_cleanup(nodepath);
2234     sp_nodepath_update_handles(nodepath);
2236     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2238     sp_nodepath_update_statusbar(nodepath);
2241 /**
2242  * Call sp_nodepath_set_line() for all selected segments.
2243  */
2244 void
2245 sp_node_selected_set_line_type(NRPathcode code)
2247     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2248     if (nodepath == NULL) return;
2250     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2251        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2252         g_assert(n->selected);
2253         if (n->p.other && n->p.other->selected) {
2254             sp_nodepath_set_line_type(n, code);
2255         }
2256     }
2258     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2261 /**
2262  * Call sp_nodepath_convert_node_type() for all selected nodes.
2263  */
2264 void
2265 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2267     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2268     if (nodepath == NULL) return;
2270     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2271         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2272     }
2274     sp_nodepath_update_repr(nodepath, _("Change node type"));
2277 /**
2278  * Change select status of node, update its own and neighbour handles.
2279  */
2280 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2282     node->selected = selected;
2284     if (selected) {
2285         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2286         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2287         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2288         sp_knot_update_ctrl(node->knot);
2289     } else {
2290         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2291         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2292         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2293         sp_knot_update_ctrl(node->knot);
2294     }
2296     sp_node_update_handles(node);
2297     if (node->n.other) sp_node_update_handles(node->n.other);
2298     if (node->p.other) sp_node_update_handles(node->p.other);
2301 /**
2302 \brief Select a node
2303 \param node     The node to select
2304 \param incremental   If true, add to selection, otherwise deselect others
2305 \param override   If true, always select this node, otherwise toggle selected status
2306 */
2307 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2309     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2311     if (incremental) {
2312         if (override) {
2313             if (!g_list_find(nodepath->selected, node)) {
2314                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2315             }
2316             sp_node_set_selected(node, TRUE);
2317         } else { // toggle
2318             if (node->selected) {
2319                 g_assert(g_list_find(nodepath->selected, node));
2320                 nodepath->selected = g_list_remove(nodepath->selected, node);
2321             } else {
2322                 g_assert(!g_list_find(nodepath->selected, node));
2323                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2324             }
2325             sp_node_set_selected(node, !node->selected);
2326         }
2327     } else {
2328         sp_nodepath_deselect(nodepath);
2329         nodepath->selected = g_list_prepend(nodepath->selected, node);
2330         sp_node_set_selected(node, TRUE);
2331     }
2333     sp_nodepath_update_statusbar(nodepath);
2337 /**
2338 \brief Deselect all nodes in the nodepath
2339 */
2340 void
2341 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2343     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2345     while (nodepath->selected) {
2346         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2347         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2348     }
2349     sp_nodepath_update_statusbar(nodepath);
2352 /**
2353 \brief Select or invert selection of all nodes in the nodepath
2354 */
2355 void
2356 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2358     if (!nodepath) return;
2360     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2361        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2362         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2363            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2364            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2365         }
2366     }
2369 /**
2370  * If nothing selected, does the same as sp_nodepath_select_all();
2371  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2372  * (i.e., similar to "select all in layer", with the "selected" subpaths
2373  * being treated as "layers" in the path).
2374  */
2375 void
2376 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2378     if (!nodepath) return;
2380     if (g_list_length (nodepath->selected) == 0) {
2381         sp_nodepath_select_all (nodepath, invert);
2382         return;
2383     }
2385     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2386     GSList *subpaths = NULL;
2388     for (GList *l = copy; l != NULL; l = l->next) {
2389         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2390         Inkscape::NodePath::SubPath *subpath = n->subpath;
2391         if (!g_slist_find (subpaths, subpath))
2392             subpaths = g_slist_prepend (subpaths, subpath);
2393     }
2395     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2396         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2397         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2398             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2399             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2400         }
2401     }
2403     g_slist_free (subpaths);
2404     g_list_free (copy);
2407 /**
2408  * \brief Select the node after the last selected; if none is selected,
2409  * select the first within path.
2410  */
2411 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2413     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2415    Inkscape::NodePath::Node *last = NULL;
2416     if (nodepath->selected) {
2417         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2418            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2419             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2420             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2421                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2422                 if (node->selected) {
2423                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2424                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2425                             if (spl->next) { // there's a next subpath
2426                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2427                                 last = subpath_next->first;
2428                             } else if (spl->prev) { // there's a previous subpath
2429                                 last = NULL; // to be set later to the first node of first subpath
2430                             } else {
2431                                 last = node->n.other;
2432                             }
2433                         } else {
2434                             last = node->n.other;
2435                         }
2436                     } else {
2437                         if (node->n.other) {
2438                             last = node->n.other;
2439                         } else {
2440                             if (spl->next) { // there's a next subpath
2441                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2442                                 last = subpath_next->first;
2443                             } else if (spl->prev) { // there's a previous subpath
2444                                 last = NULL; // to be set later to the first node of first subpath
2445                             } else {
2446                                 last = (Inkscape::NodePath::Node *) subpath->first;
2447                             }
2448                         }
2449                     }
2450                 }
2451             }
2452         }
2453         sp_nodepath_deselect(nodepath);
2454     }
2456     if (last) { // there's at least one more node after selected
2457         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2458     } else { // no more nodes, select the first one in first subpath
2459        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2460         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2461     }
2464 /**
2465  * \brief Select the node before the first selected; if none is selected,
2466  * select the last within path
2467  */
2468 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2470     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2472    Inkscape::NodePath::Node *last = NULL;
2473     if (nodepath->selected) {
2474         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2475            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2476             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2477                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2478                 if (node->selected) {
2479                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2480                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2481                             if (spl->prev) { // there's a prev subpath
2482                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2483                                 last = subpath_prev->last;
2484                             } else if (spl->next) { // there's a next subpath
2485                                 last = NULL; // to be set later to the last node of last subpath
2486                             } else {
2487                                 last = node->p.other;
2488                             }
2489                         } else {
2490                             last = node->p.other;
2491                         }
2492                     } else {
2493                         if (node->p.other) {
2494                             last = node->p.other;
2495                         } else {
2496                             if (spl->prev) { // there's a prev subpath
2497                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2498                                 last = subpath_prev->last;
2499                             } else if (spl->next) { // there's a next subpath
2500                                 last = NULL; // to be set later to the last node of last subpath
2501                             } else {
2502                                 last = (Inkscape::NodePath::Node *) subpath->last;
2503                             }
2504                         }
2505                     }
2506                 }
2507             }
2508         }
2509         sp_nodepath_deselect(nodepath);
2510     }
2512     if (last) { // there's at least one more node before selected
2513         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2514     } else { // no more nodes, select the last one in last subpath
2515         GList *spl = g_list_last(nodepath->subpaths);
2516        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2517         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2518     }
2521 /**
2522  * \brief Select all nodes that are within the rectangle.
2523  */
2524 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2526     if (!incremental) {
2527         sp_nodepath_deselect(nodepath);
2528     }
2530     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2531        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2532         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2533            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2535             if (b.contains(node->pos)) {
2536                 sp_nodepath_node_select(node, TRUE, TRUE);
2537             }
2538         }
2539     }
2543 void
2544 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2546     g_assert (n);
2547     g_assert (nodepath);
2548     g_assert (n->subpath->nodepath == nodepath);
2550     if (g_list_length (nodepath->selected) == 0) {
2551         if (grow > 0) {
2552             sp_nodepath_node_select(n, TRUE, TRUE);
2553         }
2554         return;
2555     }
2557     if (g_list_length (nodepath->selected) == 1) {
2558         if (grow < 0) {
2559             sp_nodepath_deselect (nodepath);
2560             return;
2561         }
2562     }
2564         double n_sel_range = 0, p_sel_range = 0;
2565             Inkscape::NodePath::Node *farthest_n_node = n;
2566             Inkscape::NodePath::Node *farthest_p_node = n;
2568         // Calculate ranges
2569         {
2570             double n_range = 0, p_range = 0;
2571             bool n_going = true, p_going = true;
2572             Inkscape::NodePath::Node *n_node = n;
2573             Inkscape::NodePath::Node *p_node = n;
2574             do {
2575                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2576                 if (n_node && n_going)
2577                     n_node = n_node->n.other;
2578                 if (n_node == NULL) {
2579                     n_going = false;
2580                 } else {
2581                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2582                     if (n_node->selected) {
2583                         n_sel_range = n_range;
2584                         farthest_n_node = n_node;
2585                     }
2586                     if (n_node == p_node) {
2587                         n_going = false;
2588                         p_going = false;
2589                     }
2590                 }
2591                 if (p_node && p_going)
2592                     p_node = p_node->p.other;
2593                 if (p_node == NULL) {
2594                     p_going = false;
2595                 } else {
2596                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2597                     if (p_node->selected) {
2598                         p_sel_range = p_range;
2599                         farthest_p_node = p_node;
2600                     }
2601                     if (p_node == n_node) {
2602                         n_going = false;
2603                         p_going = false;
2604                     }
2605                 }
2606             } while (n_going || p_going);
2607         }
2609     if (grow > 0) {
2610         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2611                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2612         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2613                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2614         }
2615     } else {
2616         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2617                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2618         } else if (farthest_p_node && farthest_p_node->selected) {
2619                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2620         }
2621     }
2624 void
2625 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2627     g_assert (n);
2628     g_assert (nodepath);
2629     g_assert (n->subpath->nodepath == nodepath);
2631     if (g_list_length (nodepath->selected) == 0) {
2632         if (grow > 0) {
2633             sp_nodepath_node_select(n, TRUE, TRUE);
2634         }
2635         return;
2636     }
2638     if (g_list_length (nodepath->selected) == 1) {
2639         if (grow < 0) {
2640             sp_nodepath_deselect (nodepath);
2641             return;
2642         }
2643     }
2645     Inkscape::NodePath::Node *farthest_selected = NULL;
2646     double farthest_dist = 0;
2648     Inkscape::NodePath::Node *closest_unselected = NULL;
2649     double closest_dist = NR_HUGE;
2651     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2652        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2653         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2654            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2655            if (node == n)
2656                continue;
2657            if (node->selected) {
2658                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2659                    farthest_dist = NR::L2(node->pos - n->pos);
2660                    farthest_selected = node;
2661                }
2662            } else {
2663                if (NR::L2(node->pos - n->pos) < closest_dist) {
2664                    closest_dist = NR::L2(node->pos - n->pos);
2665                    closest_unselected = node;
2666                }
2667            }
2668         }
2669     }
2671     if (grow > 0) {
2672         if (closest_unselected) {
2673             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2674         }
2675     } else {
2676         if (farthest_selected) {
2677             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2678         }
2679     }
2683 /**
2684 \brief  Saves all nodes' and handles' current positions in their origin members
2685 */
2686 void
2687 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2689     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2690        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2691         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2692            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2693            n->origin = n->pos;
2694            n->p.origin = n->p.pos;
2695            n->n.origin = n->n.pos;
2696         }
2697     }
2698
2700 /**
2701 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2702 */
2703 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2705     if (!nodepath->selected) {
2706         return NULL;
2707     }
2709     GList *r = NULL;
2710     guint i = 0;
2711     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2712        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2713         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2714            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2715             i++;
2716             if (node->selected) {
2717                 r = g_list_append(r, GINT_TO_POINTER(i));
2718             }
2719         }
2720     }
2721     return r;
2724 /**
2725 \brief  Restores selection by selecting nodes whose positions are in the list
2726 */
2727 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2729     sp_nodepath_deselect(nodepath);
2731     guint i = 0;
2732     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2733        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2734         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2735            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2736             i++;
2737             if (g_list_find(r, GINT_TO_POINTER(i))) {
2738                 sp_nodepath_node_select(node, TRUE, TRUE);
2739             }
2740         }
2741     }
2745 /**
2746 \brief Adjusts handle according to node type and line code.
2747 */
2748 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2750     double len, otherlen, linelen;
2752     g_assert(node);
2754    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2755    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2757     /** \todo fixme: */
2758     if (me->other == NULL) return;
2759     if (other->other == NULL) return;
2761     /* I have line */
2763     NRPathcode mecode, ocode;
2764     if (which_adjust == 1) {
2765         mecode = (NRPathcode)me->other->code;
2766         ocode = (NRPathcode)node->code;
2767     } else {
2768         mecode = (NRPathcode)node->code;
2769         ocode = (NRPathcode)other->other->code;
2770     }
2772     if (mecode == NR_LINETO) return;
2774     /* I am curve */
2776     if (other->other == NULL) return;
2778     /* Other has line */
2780     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2782     NR::Point delta;
2783     if (ocode == NR_LINETO) {
2784         /* other is lineto, we are either smooth or symm */
2785        Inkscape::NodePath::Node *othernode = other->other;
2786         len = NR::L2(me->pos - node->pos);
2787         delta = node->pos - othernode->pos;
2788         linelen = NR::L2(delta);
2789         if (linelen < 1e-18) 
2790             return;
2791         me->pos = node->pos + (len / linelen)*delta;
2792         return;
2793     }
2795     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2797         me->pos = 2 * node->pos - other->pos;
2798         return;
2799     }
2801     /* We are smooth */
2803     len = NR::L2(me->pos - node->pos);
2804     delta = other->pos - node->pos;
2805     otherlen = NR::L2(delta);
2806     if (otherlen < 1e-18) return;
2808     me->pos = node->pos - (len / otherlen) * delta;
2811 /**
2812  \brief Adjusts both handles according to node type and line code
2813  */
2814 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2816     g_assert(node);
2818     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2820     /* we are either smooth or symm */
2822     if (node->p.other == NULL) return;
2824     if (node->n.other == NULL) return;
2826     if (node->code == NR_LINETO) {
2827         if (node->n.other->code == NR_LINETO) return;
2828         sp_node_adjust_handle(node, 1);
2829         return;
2830     }
2832     if (node->n.other->code == NR_LINETO) {
2833         if (node->code == NR_LINETO) return;
2834         sp_node_adjust_handle(node, -1);
2835         return;
2836     }
2838     /* both are curves */
2839     NR::Point const delta( node->n.pos - node->p.pos );
2841     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2842         node->p.pos = node->pos - delta / 2;
2843         node->n.pos = node->pos + delta / 2;
2844         return;
2845     }
2847     /* We are smooth */
2848     double plen = NR::L2(node->p.pos - node->pos);
2849     if (plen < 1e-18) return;
2850     double nlen = NR::L2(node->n.pos - node->pos);
2851     if (nlen < 1e-18) return;
2852     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2853     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2856 /**
2857  * Node event callback.
2858  */
2859 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2861     gboolean ret = FALSE;
2862     switch (event->type) {
2863         case GDK_ENTER_NOTIFY:
2864             active_node = n;
2865             break;
2866         case GDK_LEAVE_NOTIFY:
2867             active_node = NULL;
2868             break;
2869         case GDK_SCROLL:
2870             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
2871                 switch (event->scroll.direction) {
2872                     case GDK_SCROLL_UP:
2873                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2874                         break;
2875                     case GDK_SCROLL_DOWN:
2876                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2877                         break;
2878                     default:
2879                         break;
2880                 }
2881                 ret = TRUE;
2882             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
2883                 switch (event->scroll.direction) {
2884                     case GDK_SCROLL_UP:
2885                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2886                         break;
2887                     case GDK_SCROLL_DOWN:
2888                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2889                         break;
2890                     default:
2891                         break;
2892                 }
2893                 ret = TRUE;
2894             }
2895             break;
2896         case GDK_KEY_PRESS:
2897             switch (get_group0_keyval (&event->key)) {
2898                 case GDK_space:
2899                     if (event->key.state & GDK_BUTTON1_MASK) {
2900                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2901                         stamp_repr(nodepath);
2902                         ret = TRUE;
2903                     }
2904                     break;
2905                 case GDK_Page_Up:
2906                     if (event->key.state & GDK_CONTROL_MASK) {
2907                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2908                     } else {
2909                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2910                     }
2911                     break;
2912                 case GDK_Page_Down:
2913                     if (event->key.state & GDK_CONTROL_MASK) {
2914                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2915                     } else {
2916                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2917                     }
2918                     break;
2919                 default:
2920                     break;
2921             }
2922             break;
2923         default:
2924             break;
2925     }
2927     return ret;
2930 /**
2931  * Handle keypress on node; directly called.
2932  */
2933 gboolean node_key(GdkEvent *event)
2935     Inkscape::NodePath::Path *np;
2937     // there is no way to verify nodes so set active_node to nil when deleting!!
2938     if (active_node == NULL) return FALSE;
2940     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2941         gint ret = FALSE;
2942         switch (get_group0_keyval (&event->key)) {
2943             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2944             case GDK_BackSpace:
2945                 np = active_node->subpath->nodepath;
2946                 sp_nodepath_node_destroy(active_node);
2947                 sp_nodepath_update_repr(np, _("Delete node"));
2948                 active_node = NULL;
2949                 ret = TRUE;
2950                 break;
2951             case GDK_c:
2952                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2953                 ret = TRUE;
2954                 break;
2955             case GDK_s:
2956                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2957                 ret = TRUE;
2958                 break;
2959             case GDK_y:
2960                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2961                 ret = TRUE;
2962                 break;
2963             case GDK_b:
2964                 sp_nodepath_node_break(active_node);
2965                 ret = TRUE;
2966                 break;
2967         }
2968         return ret;
2969     }
2970     return FALSE;
2973 /**
2974  * Mouseclick on node callback.
2975  */
2976 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2978    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2980     if (state & GDK_CONTROL_MASK) {
2981         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2983         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2984             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2985                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2986             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2987                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2988             } else {
2989                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2990             }
2991             sp_nodepath_update_repr(nodepath, _("Change node type"));
2992             sp_nodepath_update_statusbar(nodepath);
2994         } else { //ctrl+alt+click: delete node
2995             GList *node_to_delete = NULL;
2996             node_to_delete = g_list_append(node_to_delete, n);
2997             sp_node_delete_preserve(node_to_delete);
2998         }
3000     } else {
3001         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3002     }
3005 /**
3006  * Mouse grabbed node callback.
3007  */
3008 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3010    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3012     if (!n->selected) {
3013         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3014     }
3016     n->is_dragging = true;
3017     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3019     sp_nodepath_remember_origins (n->subpath->nodepath);
3022 /**
3023  * Mouse ungrabbed node callback.
3024  */
3025 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
3027    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3029    n->dragging_out = NULL;
3030    n->is_dragging = false;
3031    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3033    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3036 /**
3037  * The point on a line, given by its angle, closest to the given point.
3038  * \param p  A point.
3039  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3040  * \param closest  Pointer to the point struct where the result is stored.
3041  * \todo FIXME: use dot product perhaps?
3042  */
3043 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3045     if (a == HUGE_VAL) { // vertical
3046         *closest = NR::Point(0, (*p)[NR::Y]);
3047     } else {
3048         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3049         (*closest)[NR::Y] = a * (*closest)[NR::X];
3050     }
3053 /**
3054  * Distance from the point to a line given by its angle.
3055  * \param p  A point.
3056  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3057  */
3058 static double point_line_distance(NR::Point *p, double a)
3060     NR::Point c;
3061     point_line_closest(p, a, &c);
3062     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]));
3065 /**
3066  * Callback for node "request" signal.
3067  * \todo fixme: This goes to "moved" event? (lauris)
3068  */
3069 static gboolean
3070 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3072     double yn, xn, yp, xp;
3073     double an, ap, na, pa;
3074     double d_an, d_ap, d_na, d_pa;
3075     gboolean collinear = FALSE;
3076     NR::Point c;
3077     NR::Point pr;
3079    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3081    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3082    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3084        NR::Point mouse = (*p);
3086        if (!n->dragging_out) {
3087            // This is the first drag-out event; find out which handle to drag out
3088            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3089            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3091            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3092                return FALSE;
3094            Inkscape::NodePath::NodeSide *opposite;
3095            if (appr_p > appr_n) { // closer to p
3096                n->dragging_out = &n->p;
3097                opposite = &n->n;
3098                n->code = NR_CURVETO;
3099            } else if (appr_p < appr_n) { // closer to n
3100                n->dragging_out = &n->n;
3101                opposite = &n->p;
3102                n->n.other->code = NR_CURVETO;
3103            } else { // p and n nodes are the same
3104                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3105                    n->dragging_out = &n->p;
3106                    opposite = &n->n;
3107                    n->code = NR_CURVETO;
3108                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3109                    n->dragging_out = &n->n;
3110                    opposite = &n->p;
3111                    n->n.other->code = NR_CURVETO;
3112                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3113                    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);
3114                    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);
3115                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3116                        n->dragging_out = &n->n;
3117                        opposite = &n->p;
3118                        n->n.other->code = NR_CURVETO;
3119                    } else { // closer to other's n handle
3120                        n->dragging_out = &n->p;
3121                        opposite = &n->n;
3122                        n->code = NR_CURVETO;
3123                    }
3124                }
3125            }
3127            // if there's another handle, make sure the one we drag out starts parallel to it
3128            if (opposite->pos != n->pos) {
3129                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3130            }
3132            // knots might not be created yet!
3133            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3134            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3135        }
3137        // pass this on to the handle-moved callback
3138        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3139        sp_node_update_handles(n);
3140        return TRUE;
3141    }
3143     if (state & GDK_CONTROL_MASK) { // constrained motion
3145         // calculate relative distances of handles
3146         // n handle:
3147         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3148         xn = n->n.pos[NR::X] - n->pos[NR::X];
3149         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3150         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3151             if (n->n.other) { // if there is the next point
3152                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3153                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3154                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3155             }
3156         }
3157         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3158         if (yn < 0) { xn = -xn; yn = -yn; }
3160         // p handle:
3161         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3162         xp = n->p.pos[NR::X] - n->pos[NR::X];
3163         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3164         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3165             if (n->p.other) {
3166                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3167                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3168                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3169             }
3170         }
3171         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3172         if (yp < 0) { xp = -xp; yp = -yp; }
3174         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3175             // sliding on handles, only if at least one of the handles is non-vertical
3176             // (otherwise it's the same as ctrl+drag anyway)
3178             // calculate angles of the handles
3179             if (xn == 0) {
3180                 if (yn == 0) { // no handle, consider it the continuation of the other one
3181                     an = 0;
3182                     collinear = TRUE;
3183                 }
3184                 else an = 0; // vertical; set the angle to horizontal
3185             } else an = yn/xn;
3187             if (xp == 0) {
3188                 if (yp == 0) { // no handle, consider it the continuation of the other one
3189                     ap = an;
3190                 }
3191                 else ap = 0; // vertical; set the angle to horizontal
3192             } else  ap = yp/xp;
3194             if (collinear) an = ap;
3196             // angles of the perpendiculars; HUGE_VAL means vertical
3197             if (an == 0) na = HUGE_VAL; else na = -1/an;
3198             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3200             // mouse point relative to the node's original pos
3201             pr = (*p) - n->origin;
3203             // distances to the four lines (two handles and two perpendiculars)
3204             d_an = point_line_distance(&pr, an);
3205             d_na = point_line_distance(&pr, na);
3206             d_ap = point_line_distance(&pr, ap);
3207             d_pa = point_line_distance(&pr, pa);
3209             // find out which line is the closest, save its closest point in c
3210             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3211                 point_line_closest(&pr, an, &c);
3212             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3213                 point_line_closest(&pr, ap, &c);
3214             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3215                 point_line_closest(&pr, na, &c);
3216             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3217                 point_line_closest(&pr, pa, &c);
3218             }
3220             // move the node to the closest point
3221             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3222                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3223                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3225         } else {  // constraining to hor/vert
3227             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3228                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3229             } else { // snap to vert
3230                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3231             }
3232         }
3233     } else { // move freely
3234         if (n->is_dragging) {
3235             if (state & GDK_MOD1_MASK) { // sculpt
3236                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3237             } else {
3238                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3239                                             (*p)[NR::X] - n->pos[NR::X],
3240                                             (*p)[NR::Y] - n->pos[NR::Y],
3241                                             (state & GDK_SHIFT_MASK) == 0);
3242             }
3243         }
3244     }
3246     n->subpath->nodepath->desktop->scroll_to_point(p);
3248     return TRUE;
3251 /**
3252  * Node handle clicked callback.
3253  */
3254 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3256    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3258     if (state & GDK_CONTROL_MASK) { // "delete" handle
3259         if (n->p.knot == knot) {
3260             n->p.pos = n->pos;
3261         } else if (n->n.knot == knot) {
3262             n->n.pos = n->pos;
3263         }
3264         sp_node_update_handles(n);
3265         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3266         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3267         sp_nodepath_update_statusbar(nodepath);
3269     } else { // just select or add to selection, depending in Shift
3270         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3271     }
3274 /**
3275  * Node handle grabbed callback.
3276  */
3277 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3279    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3281     if (!n->selected) {
3282         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3283     }
3285     // remember the origin point of the handle
3286     if (n->p.knot == knot) {
3287         n->p.origin_radial = n->p.pos - n->pos;
3288     } else if (n->n.knot == knot) {
3289         n->n.origin_radial = n->n.pos - n->pos;
3290     } else {
3291         g_assert_not_reached();
3292     }
3294     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3297 /**
3298  * Node handle ungrabbed callback.
3299  */
3300 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3302    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3304     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3305     if (n->p.knot == knot) {
3306         n->p.origin_radial.a = 0;
3307         sp_knot_set_position(knot, &n->p.pos, state);
3308     } else if (n->n.knot == knot) {
3309         n->n.origin_radial.a = 0;
3310         sp_knot_set_position(knot, &n->n.pos, state);
3311     } else {
3312         g_assert_not_reached();
3313     }
3315     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3316     sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3319 /**
3320  * Node handle "request" signal callback.
3321  */
3322 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3324     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3326     Inkscape::NodePath::NodeSide *me, *opposite;
3327     gint which;
3328     if (n->p.knot == knot) {
3329         me = &n->p;
3330         opposite = &n->n;
3331         which = -1;
3332     } else if (n->n.knot == knot) {
3333         me = &n->n;
3334         opposite = &n->p;
3335         which = 1;
3336     } else {
3337         me = opposite = NULL;
3338         which = 0;
3339         g_assert_not_reached();
3340     }
3342     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3344     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3346     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3347         /* We are smooth node adjacent with line */
3348         NR::Point const delta = *p - n->pos;
3349         NR::Coord const len = NR::L2(delta);
3350         Inkscape::NodePath::Node *othernode = opposite->other;
3351         NR::Point const ndelta = n->pos - othernode->pos;
3352         NR::Coord const linelen = NR::L2(ndelta);
3353         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3354             NR::Coord const scal = dot(delta, ndelta) / linelen;
3355             (*p) = n->pos + (scal / linelen) * ndelta;
3356         }
3357         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3358     } else {
3359         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3360     }
3362     sp_node_adjust_handle(n, -which);
3364     return FALSE;
3367 /**
3368  * Node handle moved callback.
3369  */
3370 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3372    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3374    Inkscape::NodePath::NodeSide *me;
3375    Inkscape::NodePath::NodeSide *other;
3376     if (n->p.knot == knot) {
3377         me = &n->p;
3378         other = &n->n;
3379     } else if (n->n.knot == knot) {
3380         me = &n->n;
3381         other = &n->p;
3382     } else {
3383         me = NULL;
3384         other = NULL;
3385         g_assert_not_reached();
3386     }
3388     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3389     Radial rme(me->pos - n->pos);
3390     Radial rother(other->pos - n->pos);
3391     Radial rnew(*p - n->pos);
3393     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3394         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3395         /* 0 interpreted as "no snapping". */
3397         // The closest PI/snaps angle, starting from zero.
3398         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3399         if (me->origin_radial.a == HUGE_VAL) {
3400             // ortho doesn't exist: original handle was zero length.
3401             rnew.a = a_snapped;
3402         } else {
3403             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3404              * its opposite and perpendiculars). */
3405             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3407             // Snap to the closest.
3408             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3409                        ? a_snapped
3410                        : a_ortho );
3411         }
3412     }
3414     if (state & GDK_MOD1_MASK) {
3415         // lock handle length
3416         rnew.r = me->origin_radial.r;
3417     }
3419     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3420         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3421         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3422         rother.a += rnew.a - rme.a;
3423         other->pos = NR::Point(rother) + n->pos;
3424         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3425         sp_knot_set_position(other->knot, &other->pos, 0);
3426     }
3428     me->pos = NR::Point(rnew) + n->pos;
3429     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3431     // this is what sp_knot_set_position does, but without emitting the signal:
3432     // we cannot emit a "moved" signal because we're now processing it
3433     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3435     knot->desktop->set_coordinate_status(me->pos);
3437     update_object(n->subpath->nodepath);
3439     /* status text */
3440     SPDesktop *desktop = n->subpath->nodepath->desktop;
3441     if (!desktop) return;
3442     SPEventContext *ec = desktop->event_context;
3443     if (!ec) return;
3444     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3445     if (!mc) return;
3447     double degrees = 180 / M_PI * rnew.a;
3448     if (degrees > 180) degrees -= 360;
3449     if (degrees < -180) degrees += 360;
3450     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3451         degrees = angle_to_compass (degrees);
3453     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3455     mc->setF(Inkscape::NORMAL_MESSAGE,
3456          _("<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);
3458     g_string_free(length, TRUE);
3461 /**
3462  * Node handle event callback.
3463  */
3464 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3466     gboolean ret = FALSE;
3467     switch (event->type) {
3468         case GDK_KEY_PRESS:
3469             switch (get_group0_keyval (&event->key)) {
3470                 case GDK_space:
3471                     if (event->key.state & GDK_BUTTON1_MASK) {
3472                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3473                         stamp_repr(nodepath);
3474                         ret = TRUE;
3475                     }
3476                     break;
3477                 default:
3478                     break;
3479             }
3480             break;
3481         default:
3482             break;
3483     }
3485     return ret;
3488 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3489                                  Radial &rme, Radial &rother, gboolean const both)
3491     rme.a += angle;
3492     if ( both
3493          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3494          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3495     {
3496         rother.a += angle;
3497     }
3500 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3501                                         Radial &rme, Radial &rother, gboolean const both)
3503     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3505     gdouble r;
3506     if ( both
3507          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3508          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3509     {
3510         r = MAX(rme.r, rother.r);
3511     } else {
3512         r = rme.r;
3513     }
3515     gdouble const weird_angle = atan2(norm_angle, r);
3516 /* Bulia says norm_angle is just the visible distance that the
3517  * object's end must travel on the screen.  Left as 'angle' for want of
3518  * a better name.*/
3520     rme.a += weird_angle;
3521     if ( both
3522          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3523          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3524     {
3525         rother.a += weird_angle;
3526     }
3529 /**
3530  * Rotate one node.
3531  */
3532 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3534     Inkscape::NodePath::NodeSide *me, *other;
3535     bool both = false;
3537     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3538     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3540     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3541         me = &(n->p);
3542         other = &(n->n);
3543     } else if (!n->p.other) {
3544         me = &(n->n);
3545         other = &(n->p);
3546     } else {
3547         if (which > 0) { // right handle
3548             if (xn > xp) {
3549                 me = &(n->n);
3550                 other = &(n->p);
3551             } else {
3552                 me = &(n->p);
3553                 other = &(n->n);
3554             }
3555         } else if (which < 0){ // left handle
3556             if (xn <= xp) {
3557                 me = &(n->n);
3558                 other = &(n->p);
3559             } else {
3560                 me = &(n->p);
3561                 other = &(n->n);
3562             }
3563         } else { // both handles
3564             me = &(n->n);
3565             other = &(n->p);
3566             both = true;
3567         }
3568     }
3570     Radial rme(me->pos - n->pos);
3571     Radial rother(other->pos - n->pos);
3573     if (screen) {
3574         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3575     } else {
3576         node_rotate_one_internal (*n, angle, rme, rother, both);
3577     }
3579     me->pos = n->pos + NR::Point(rme);
3581     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3582         other->pos =  n->pos + NR::Point(rother);
3583     }
3585     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3586     // so here we just move all the knots without emitting move signals, for speed
3587     sp_node_update_handles(n, false);
3590 /**
3591  * Rotate selected nodes.
3592  */
3593 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3595     if (!nodepath || !nodepath->selected) return;
3597     if (g_list_length(nodepath->selected) == 1) {
3598        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3599         node_rotate_one (n, angle, which, screen);
3600     } else {
3601        // rotate as an object:
3603         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3604         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3605         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3606             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3607             box.expandTo (n->pos); // contain all selected nodes
3608         }
3610         gdouble rot;
3611         if (screen) {
3612             gdouble const zoom = nodepath->desktop->current_zoom();
3613             gdouble const zmove = angle / zoom;
3614             gdouble const r = NR::L2(box.max() - box.midpoint());
3615             rot = atan2(zmove, r);
3616         } else {
3617             rot = angle;
3618         }
3620         NR::Matrix t =
3621             NR::Matrix (NR::translate(-box.midpoint())) *
3622             NR::Matrix (NR::rotate(rot)) *
3623             NR::Matrix (NR::translate(box.midpoint()));
3625         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3626             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3627             n->pos *= t;
3628             n->n.pos *= t;
3629             n->p.pos *= t;
3630             sp_node_update_handles(n, false);
3631         }
3632     }
3634     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3637 /**
3638  * Scale one node.
3639  */
3640 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3642     bool both = false;
3643     Inkscape::NodePath::NodeSide *me, *other;
3645     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3646     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3648     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3649         me = &(n->p);
3650         other = &(n->n);
3651         n->code = NR_CURVETO;
3652     } else if (!n->p.other) {
3653         me = &(n->n);
3654         other = &(n->p);
3655         if (n->n.other)
3656             n->n.other->code = NR_CURVETO;
3657     } else {
3658         if (which > 0) { // right handle
3659             if (xn > xp) {
3660                 me = &(n->n);
3661                 other = &(n->p);
3662                 if (n->n.other)
3663                     n->n.other->code = NR_CURVETO;
3664             } else {
3665                 me = &(n->p);
3666                 other = &(n->n);
3667                 n->code = NR_CURVETO;
3668             }
3669         } else if (which < 0){ // left handle
3670             if (xn <= xp) {
3671                 me = &(n->n);
3672                 other = &(n->p);
3673                 if (n->n.other)
3674                     n->n.other->code = NR_CURVETO;
3675             } else {
3676                 me = &(n->p);
3677                 other = &(n->n);
3678                 n->code = NR_CURVETO;
3679             }
3680         } else { // both handles
3681             me = &(n->n);
3682             other = &(n->p);
3683             both = true;
3684             n->code = NR_CURVETO;
3685             if (n->n.other)
3686                 n->n.other->code = NR_CURVETO;
3687         }
3688     }
3690     Radial rme(me->pos - n->pos);
3691     Radial rother(other->pos - n->pos);
3693     rme.r += grow;
3694     if (rme.r < 0) rme.r = 0;
3695     if (rme.a == HUGE_VAL) {
3696         if (me->other) { // if direction is unknown, initialize it towards the next node
3697             Radial rme_next(me->other->pos - n->pos);
3698             rme.a = rme_next.a;
3699         } else { // if there's no next, initialize to 0
3700             rme.a = 0;
3701         }
3702     }
3703     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3704         rother.r += grow;
3705         if (rother.r < 0) rother.r = 0;
3706         if (rother.a == HUGE_VAL) {
3707             rother.a = rme.a + M_PI;
3708         }
3709     }
3711     me->pos = n->pos + NR::Point(rme);
3713     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3714         other->pos = n->pos + NR::Point(rother);
3715     }
3717     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3718     // so here we just move all the knots without emitting move signals, for speed
3719     sp_node_update_handles(n, false);
3722 /**
3723  * Scale selected nodes.
3724  */
3725 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3727     if (!nodepath || !nodepath->selected) return;
3729     if (g_list_length(nodepath->selected) == 1) {
3730         // scale handles of the single selected node
3731         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3732         node_scale_one (n, grow, which);
3733     } else {
3734         // scale nodes as an "object":
3736         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3737         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3738         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3739             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3740             box.expandTo (n->pos); // contain all selected nodes
3741         }
3743         double scale = (box.maxExtent() + grow)/box.maxExtent();
3745         NR::Matrix t =
3746             NR::Matrix (NR::translate(-box.midpoint())) *
3747             NR::Matrix (NR::scale(scale, scale)) *
3748             NR::Matrix (NR::translate(box.midpoint()));
3750         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3751             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3752             n->pos *= t;
3753             n->n.pos *= t;
3754             n->p.pos *= t;
3755             sp_node_update_handles(n, false);
3756         }
3757     }
3759     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3762 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3764     if (!nodepath) return;
3765     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3768 /**
3769  * Flip selected nodes horizontally/vertically.
3770  */
3771 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3773     if (!nodepath || !nodepath->selected) return;
3775     if (g_list_length(nodepath->selected) == 1) {
3776         // flip handles of the single selected node
3777         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3778         double temp = n->p.pos[axis];
3779         n->p.pos[axis] = n->n.pos[axis];
3780         n->n.pos[axis] = temp;
3781         sp_node_update_handles(n, false);
3782     } else {
3783         // scale nodes as an "object":
3785         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3786         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3787         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3788             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3789             box.expandTo (n->pos); // contain all selected nodes
3790         }
3792         NR::Matrix t =
3793             NR::Matrix (NR::translate(-box.midpoint())) *
3794             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3795             NR::Matrix (NR::translate(box.midpoint()));
3797         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3798             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3799             n->pos *= t;
3800             n->n.pos *= t;
3801             n->p.pos *= t;
3802             sp_node_update_handles(n, false);
3803         }
3804     }
3806     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3809 //-----------------------------------------------
3810 /**
3811  * Return new subpath under given nodepath.
3812  */
3813 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3815     g_assert(nodepath);
3816     g_assert(nodepath->desktop);
3818    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3820     s->nodepath = nodepath;
3821     s->closed = FALSE;
3822     s->nodes = NULL;
3823     s->first = NULL;
3824     s->last = NULL;
3826     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3827     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3828     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3830     return s;
3833 /**
3834  * Destroy nodes in subpath, then subpath itself.
3835  */
3836 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3838     g_assert(subpath);
3839     g_assert(subpath->nodepath);
3840     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3842     while (subpath->nodes) {
3843         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3844     }
3846     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3848     g_free(subpath);
3851 /**
3852  * Link head to tail in subpath.
3853  */
3854 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3856     g_assert(!sp->closed);
3857     g_assert(sp->last != sp->first);
3858     g_assert(sp->first->code == NR_MOVETO);
3860     sp->closed = TRUE;
3862     //Link the head to the tail
3863     sp->first->p.other = sp->last;
3864     sp->last->n.other  = sp->first;
3865     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3866     sp->first          = sp->last;
3868     //Remove the extra end node
3869     sp_nodepath_node_destroy(sp->last->n.other);
3872 /**
3873  * Open closed (loopy) subpath at node.
3874  */
3875 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3877     g_assert(sp->closed);
3878     g_assert(n->subpath == sp);
3879     g_assert(sp->first == sp->last);
3881     /* We create new startpoint, current node will become last one */
3883    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3884                                                 &n->pos, &n->pos, &n->n.pos);
3887     sp->closed        = FALSE;
3889     //Unlink to make a head and tail
3890     sp->first         = new_path;
3891     sp->last          = n;
3892     n->n.other        = NULL;
3893     new_path->p.other = NULL;
3896 /**
3897  * Returns area in triangle given by points; may be negative.
3898  */
3899 inline double
3900 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3902     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]);
3905 /**
3906  * Return new node in subpath with given properties.
3907  * \param pos Position of node.
3908  * \param ppos Handle position in previous direction
3909  * \param npos Handle position in previous direction
3910  */
3911 Inkscape::NodePath::Node *
3912 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)
3914     g_assert(sp);
3915     g_assert(sp->nodepath);
3916     g_assert(sp->nodepath->desktop);
3918     if (nodechunk == NULL)
3919         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3921     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3923     n->subpath  = sp;
3925     if (type != Inkscape::NodePath::NODE_NONE) {
3926         // use the type from sodipodi:nodetypes
3927         n->type = type;
3928     } else {
3929         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3930             // points are (almost) collinear
3931             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3932                 // endnode, or a node with a retracted handle
3933                 n->type = Inkscape::NodePath::NODE_CUSP;
3934             } else {
3935                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3936             }
3937         } else {
3938             n->type = Inkscape::NodePath::NODE_CUSP;
3939         }
3940     }
3942     n->code     = code;
3943     n->selected = FALSE;
3944     n->pos      = *pos;
3945     n->p.pos    = *ppos;
3946     n->n.pos    = *npos;
3948     n->dragging_out = NULL;
3950     Inkscape::NodePath::Node *prev;
3951     if (next) {
3952         //g_assert(g_list_find(sp->nodes, next));
3953         prev = next->p.other;
3954     } else {
3955         prev = sp->last;
3956     }
3958     if (prev)
3959         prev->n.other = n;
3960     else
3961         sp->first = n;
3963     if (next)
3964         next->p.other = n;
3965     else
3966         sp->last = n;
3968     n->p.other = prev;
3969     n->n.other = next;
3971     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"));
3972     sp_knot_set_position(n->knot, pos, 0);
3974     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3975     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3976     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3977     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3978     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3979     sp_knot_update_ctrl(n->knot);
3981     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3982     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3983     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3984     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3985     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3986     sp_knot_show(n->knot);
3988     // We only create handle knots and lines on demand
3989     n->p.knot = NULL;
3990     n->p.line = NULL;
3991     n->n.knot = NULL;
3992     n->n.line = NULL;
3994     sp->nodes = g_list_prepend(sp->nodes, n);
3996     return n;
3999 /**
4000  * Destroy node and its knots, link neighbors in subpath.
4001  */
4002 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4004     g_assert(node);
4005     g_assert(node->subpath);
4006     g_assert(SP_IS_KNOT(node->knot));
4008    Inkscape::NodePath::SubPath *sp = node->subpath;
4010     if (node->selected) { // first, deselect
4011         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4012         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4013     }
4015     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4017     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4018     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4019     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4020     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4021     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4022     g_object_unref(G_OBJECT(node->knot));
4024     if (node->p.knot) {
4025         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4026         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4027         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4028         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4029         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4030         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4031         g_object_unref(G_OBJECT(node->p.knot));
4032     }
4034     if (node->n.knot) {
4035         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4036         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4037         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4038         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4039         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4040         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4041         g_object_unref(G_OBJECT(node->n.knot));
4042     }
4044     if (node->p.line)
4045         gtk_object_destroy(GTK_OBJECT(node->p.line));
4046     if (node->n.line)
4047         gtk_object_destroy(GTK_OBJECT(node->n.line));
4049     if (sp->nodes) { // there are others nodes on the subpath
4050         if (sp->closed) {
4051             if (sp->first == node) {
4052                 g_assert(sp->last == node);
4053                 sp->first = node->n.other;
4054                 sp->last = sp->first;
4055             }
4056             node->p.other->n.other = node->n.other;
4057             node->n.other->p.other = node->p.other;
4058         } else {
4059             if (sp->first == node) {
4060                 sp->first = node->n.other;
4061                 sp->first->code = NR_MOVETO;
4062             }
4063             if (sp->last == node) sp->last = node->p.other;
4064             if (node->p.other) node->p.other->n.other = node->n.other;
4065             if (node->n.other) node->n.other->p.other = node->p.other;
4066         }
4067     } else { // this was the last node on subpath
4068         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4069     }
4071     g_mem_chunk_free(nodechunk, node);
4074 /**
4075  * Returns one of the node's two sides.
4076  * \param which Indicates which side.
4077  * \return Pointer to previous node side if which==-1, next if which==1.
4078  */
4079 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4081     g_assert(node);
4083     switch (which) {
4084         case -1:
4085             return &node->p;
4086         case 1:
4087             return &node->n;
4088         default:
4089             break;
4090     }
4092     g_assert_not_reached();
4094     return NULL;
4097 /**
4098  * Return the other side of the node, given one of its sides.
4099  */
4100 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4102     g_assert(node);
4104     if (me == &node->p) return &node->n;
4105     if (me == &node->n) return &node->p;
4107     g_assert_not_reached();
4109     return NULL;
4112 /**
4113  * Return NRPathcode on the given side of the node.
4114  */
4115 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4117     g_assert(node);
4119     if (me == &node->p) {
4120         if (node->p.other) return (NRPathcode)node->code;
4121         return NR_MOVETO;
4122     }
4124     if (me == &node->n) {
4125         if (node->n.other) return (NRPathcode)node->n.other->code;
4126         return NR_MOVETO;
4127     }
4129     g_assert_not_reached();
4131     return NR_END;
4134 /**
4135  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4136  */
4137 Inkscape::NodePath::Node *
4138 sp_nodepath_get_node_by_index(int index)
4140     Inkscape::NodePath::Node *e = NULL;
4142     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4143     if (!nodepath) {
4144         return e;
4145     }
4147     //find segment
4148     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4150         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4151         int n = g_list_length(sp->nodes);
4152         if (sp->closed) {
4153             n++;
4154         }
4156         //if the piece belongs to this subpath grab it
4157         //otherwise move onto the next subpath
4158         if (index < n) {
4159             e = sp->first;
4160             for (int i = 0; i < index; ++i) {
4161                 e = e->n.other;
4162             }
4163             break;
4164         } else {
4165             if (sp->closed) {
4166                 index -= (n+1);
4167             } else {
4168                 index -= n;
4169             }
4170         }
4171     }
4173     return e;
4176 /**
4177  * Returns plain text meaning of node type.
4178  */
4179 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4181     unsigned retracted = 0;
4182     bool endnode = false;
4184     for (int which = -1; which <= 1; which += 2) {
4185         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4186         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4187             retracted ++;
4188         if (!side->other)
4189             endnode = true;
4190     }
4192     if (retracted == 0) {
4193         if (endnode) {
4194                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4195                 return _("end node");
4196         } else {
4197             switch (node->type) {
4198                 case Inkscape::NodePath::NODE_CUSP:
4199                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4200                     return _("cusp");
4201                 case Inkscape::NodePath::NODE_SMOOTH:
4202                     // TRANSLATORS: "smooth" is an adjective here
4203                     return _("smooth");
4204                 case Inkscape::NodePath::NODE_SYMM:
4205                     return _("symmetric");
4206             }
4207         }
4208     } else if (retracted == 1) {
4209         if (endnode) {
4210             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4211             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4212         } else {
4213             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4214         }
4215     } else {
4216         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4217     }
4219     return NULL;
4222 /**
4223  * Handles content of statusbar as long as node tool is active.
4224  */
4225 void
4226 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
4228     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");
4229     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4231     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4232     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4233     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4234     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4236     SPDesktop *desktop = NULL;
4237     if (nodepath) {
4238         desktop = nodepath->desktop;
4239     } else {
4240         desktop = SP_ACTIVE_DESKTOP;
4241     }
4243     SPEventContext *ec = desktop->event_context;
4244     if (!ec) return;
4245     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4246     if (!mc) return;
4248     if (selected_nodes == 0) {
4249         Inkscape::Selection *sel = desktop->selection;
4250         if (!sel || sel->isEmpty()) {
4251             mc->setF(Inkscape::NORMAL_MESSAGE,
4252                      _("Select a single object to edit its nodes or handles."));
4253         } else {
4254             if (nodepath) {
4255             mc->setF(Inkscape::NORMAL_MESSAGE,
4256                      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.",
4257                               "<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.",
4258                               total_nodes),
4259                      total_nodes);
4260             } else {
4261                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4262                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4263                 } else {
4264                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4265                 }
4266             }
4267         }
4268     } else if (nodepath && selected_nodes == 1) {
4269         mc->setF(Inkscape::NORMAL_MESSAGE,
4270                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4271                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4272                           total_nodes),
4273                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4274     } else {
4275         if (selected_subpaths > 1) {
4276             mc->setF(Inkscape::NORMAL_MESSAGE,
4277                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4278                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4279                               total_nodes),
4280                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4281         } else {
4282             mc->setF(Inkscape::NORMAL_MESSAGE,
4283                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4284                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4285                               total_nodes),
4286                      selected_nodes, total_nodes, when_selected);
4287         }
4288     }
4292 /*
4293   Local Variables:
4294   mode:c++
4295   c-file-style:"stroustrup"
4296   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4297   indent-tabs-mode:nil
4298   fill-column:99
4299   End:
4300 */
4301 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :