Code

isolate the nodepath-or-knotholder unit into the new ShapeEditor class which handles...
[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 "shape-editor.h"
35 #include "selection-chemistry.h"
36 #include "selection.h"
37 #include "xml/repr.h"
38 #include "prefs-utils.h"
39 #include "sp-metrics.h"
40 #include "sp-path.h"
41 #include "libnr/nr-matrix-ops.h"
42 #include "splivarot.h"
43 #include "svg/svg.h"
44 #include "verbs.h"
45 #include "display/bezier-utils.h"
46 #include <vector>
47 #include <algorithm>
49 class NR::Matrix;
51 /// \todo
52 /// evil evil evil. FIXME: conflict of two different Path classes!
53 /// There is a conflict in the namespace between two classes named Path.
54 /// #include "sp-flowtext.h"
55 /// #include "sp-flowregion.h"
57 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
58 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
59 GType sp_flowregion_get_type (void);
60 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
61 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
62 GType sp_flowtext_get_type (void);
63 // end evil workaround
65 #include "helper/stlport.h"
68 /// \todo fixme: Implement these via preferences */
70 #define NODE_FILL          0xbfbfbf00
71 #define NODE_STROKE        0x000000ff
72 #define NODE_FILL_HI       0xff000000
73 #define NODE_STROKE_HI     0x000000ff
74 #define NODE_FILL_SEL      0x0000ffff
75 #define NODE_STROKE_SEL    0x000000ff
76 #define NODE_FILL_SEL_HI   0xff000000
77 #define NODE_STROKE_SEL_HI 0x000000ff
78 #define KNOT_FILL          0xffffffff
79 #define KNOT_STROKE        0x000000ff
80 #define KNOT_FILL_HI       0xff000000
81 #define KNOT_STROKE_HI     0x000000ff
83 static GMemChunk *nodechunk = NULL;
85 /* Creation from object */
87 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
88 static gchar *parse_nodetypes(gchar const *types, gint length);
90 /* Object updating */
92 static void stamp_repr(Inkscape::NodePath::Path *np);
93 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
94 static gchar *create_typestr(Inkscape::NodePath::Path *np);
96 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
98 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
100 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
102 /* Adjust handle placement, if the node or the other handle is moved */
103 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
104 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
106 /* Node event callbacks */
107 static void node_clicked(SPKnot *knot, guint state, gpointer data);
108 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
109 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
110 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
112 /* Handle event callbacks */
113 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
114 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
115 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
116 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
117 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
118 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
120 /* Constructors and destructors */
122 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
123 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
124 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
125 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
126 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
127                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
128 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
130 /* Helpers */
132 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
133 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
134 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
136 // active_node indicates mouseover node
137 static Inkscape::NodePath::Node *active_node = NULL;
139 /**
140  * \brief Creates new nodepath from item
141  */
142 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool show_handles)
144     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
146     /** \todo
147      * FIXME: remove this. We don't want to edit paths inside flowtext.
148      * Instead we will build our flowtext with cloned paths, so that the
149      * real paths are outside the flowtext and thus editable as usual.
150      */
151     if (SP_IS_FLOWTEXT(item)) {
152         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
153             if SP_IS_FLOWREGION(child) {
154                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
155                 if (grandchild && SP_IS_PATH(grandchild)) {
156                     item = SP_ITEM(grandchild);
157                     break;
158                 }
159             }
160         }
161     }
163     if (!SP_IS_PATH(item))
164         return NULL;
165     SPPath *path = SP_PATH(item);
166     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
167     if (curve == NULL)
168         return NULL;
170     NArtBpath *bpath = sp_curve_first_bpath(curve);
171     gint length = curve->end;
172     if (length == 0)
173         return NULL; // prevent crash for one-node paths
175     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
176     gchar *typestr = parse_nodetypes(nodetypes, length);
178     //Create new nodepath
179     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
180     if (!np)
181         return NULL;
183     // Set defaults
184     np->desktop     = desktop;
185     np->path        = path;
186     np->subpaths    = NULL;
187     np->selected    = NULL;
188     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
189     np->livarot_path = NULL;
190     np->local_change = 0;
191     np->show_handles = show_handles;
193     // we need to update item's transform from the repr here,
194     // because they may be out of sync when we respond
195     // to a change in repr by regenerating nodepath     --bb
196     sp_object_read_attr(SP_OBJECT(item), "transform");
198     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
199     np->d2i  = np->i2d.inverse();
200     np->repr = repr;
202     // create the subpath(s) from the bpath
203     NArtBpath *b = bpath;
204     while (b->code != NR_END) {
205         b = subpath_from_bpath(np, b, typestr + (b - bpath));
206     }
208     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
209     np->subpaths = g_list_reverse(np->subpaths);
211     g_free(typestr);
212     sp_curve_unref(curve);
214     // create the livarot representation from the same item
215     sp_nodepath_ensure_livarot_path(np);
217     return np;
220 /**
221  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
222  */
223 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
225     if (!np)  //soft fail, like delete
226         return;
228     while (np->subpaths) {
229         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
230     }
232     //Inform the ShapeEditor that made me, if any, that I am gone.
233     if (np->shape_editor)
234         np->shape_editor->nodepath_destroyed();
236     g_assert(!np->selected);
238     if (np->livarot_path) {
239         delete np->livarot_path;
240         np->livarot_path = NULL;
241     }
243     np->desktop = NULL;
245     g_free(np);
249 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
251     if (np && np->livarot_path == NULL && np->path && SP_IS_ITEM(np->path)) {
252         np->livarot_path = Path_for_item (np->path, true, true);
253         if (np->livarot_path)
254             np->livarot_path->ConvertWithBackData(0.01);
255     }
259 /**
260  *  Return the node count of a given NodeSubPath.
261  */
262 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
264     if (!subpath)
265         return 0;
266     gint nodeCount = g_list_length(subpath->nodes);
267     return nodeCount;
270 /**
271  *  Return the node count of a given NodePath.
272  */
273 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
275     if (!np)
276         return 0;
277     gint nodeCount = 0;
278     for (GList *item = np->subpaths ; item ; item=item->next) {
279        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
280         nodeCount += g_list_length(subpath->nodes);
281     }
282     return nodeCount;
285 /**
286  *  Return the subpath count of a given NodePath.
287  */
288 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
290     if (!np)
291         return 0;
292     return g_list_length (np->subpaths);
295 /**
296  *  Return the selected node count of a given NodePath.
297  */
298 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
300     if (!np)
301         return 0;
302     return g_list_length (np->selected);
305 /**
306  *  Return the number of subpaths where nodes are selected in a given NodePath.
307  */
308 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
310     if (!np)
311         return 0;
312     if (!np->selected)
313         return 0;
314     if (!np->selected->next)
315         return 1;
316     gint count = 0;
317     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
318         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
319         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
320             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
321             if (node->selected) {
322                 count ++;
323                 break;
324             }
325         }
326     }
327     return count;
329  
330 /**
331  * Clean up a nodepath after editing.
332  *
333  * Currently we are deleting trivial subpaths.
334  */
335 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
337     GList *badSubPaths = NULL;
339     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
340     for (GList *l = nodepath->subpaths; l ; l=l->next) {
341        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
342        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
343             badSubPaths = g_list_append(badSubPaths, sp);
344     }
346     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
347     //also removes the subpath from nodepath->subpaths
348     for (GList *l = badSubPaths; l ; l=l->next) {
349        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
350         sp_nodepath_subpath_destroy(sp);
351     }
353     g_list_free(badSubPaths);
356 /**
357  * Create new nodepath from b, make it subpath of np.
358  * \param t The node type.
359  * \todo Fixme: t should be a proper type, rather than gchar
360  */
361 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
363     NR::Point ppos, pos, npos;
365     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
367     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
368     bool const closed = (b->code == NR_MOVETO);
370     pos = NR::Point(b->x3, b->y3) * np->i2d;
371     if (b[1].code == NR_CURVETO) {
372         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
373     } else {
374         npos = pos;
375     }
376     Inkscape::NodePath::Node *n;
377     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
378     g_assert(sp->first == n);
379     g_assert(sp->last  == n);
381     b++;
382     t++;
383     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
384         pos = NR::Point(b->x3, b->y3) * np->i2d;
385         if (b->code == NR_CURVETO) {
386             ppos = NR::Point(b->x2, b->y2) * np->i2d;
387         } else {
388             ppos = pos;
389         }
390         if (b[1].code == NR_CURVETO) {
391             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
392         } else {
393             npos = pos;
394         }
395         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
396         b++;
397         t++;
398     }
400     if (closed) sp_nodepath_subpath_close(sp);
402     return b;
405 /**
406  * Convert from sodipodi:nodetypes to new style type string.
407  */
408 static gchar *parse_nodetypes(gchar const *types, gint length)
410     g_assert(length > 0);
412     gchar *typestr = g_new(gchar, length + 1);
414     gint pos = 0;
416     if (types) {
417         for (gint i = 0; types[i] && ( i < length ); i++) {
418             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
419             if (types[i] != '\0') {
420                 switch (types[i]) {
421                     case 's':
422                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
423                         break;
424                     case 'z':
425                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
426                         break;
427                     case 'c':
428                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
429                         break;
430                     default:
431                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
432                         break;
433                 }
434             }
435         }
436     }
438     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
440     return typestr;
443 /**
444  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
445  * updated but repr is not (for speed). Used during curve and node drag.
446  */
447 static void update_object(Inkscape::NodePath::Path *np)
449     g_assert(np);
451     SPCurve *curve = create_curve(np);
453     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
455     sp_curve_unref(curve);
458 /**
459  * Update XML path node with data from path object.
460  */
461 static void update_repr_internal(Inkscape::NodePath::Path *np)
463     g_assert(np);
465     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
467     SPCurve *curve = create_curve(np);
468     gchar *typestr = create_typestr(np);
469     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
471     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
472         np->local_change++;
473         repr->setAttribute("d", svgpath);
474     }
476     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
477         np->local_change++;
478         repr->setAttribute("sodipodi:nodetypes", typestr);
479     }
481     g_free(svgpath);
482     g_free(typestr);
483     sp_curve_unref(curve);
486 /**
487  * Update XML path node with data from path object, commit changes forever.
488  */
489 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
491     //fixme: np can be NULL, so check before proceeding
492     g_return_if_fail(np != NULL);
494     if (np->livarot_path) {
495         delete np->livarot_path;
496         np->livarot_path = NULL;
497     }
499     update_repr_internal(np);
500     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
501     
502     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
503                      annotation);
506 /**
507  * Update XML path node with data from path object, commit changes with undo.
508  */
509 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
511     if (np->livarot_path) {
512         delete np->livarot_path;
513         np->livarot_path = NULL;
514     }
516     update_repr_internal(np);
517     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
518                            annotation);
521 /**
522  * Make duplicate of path, replace corresponding XML node in tree, commit.
523  */
524 static void stamp_repr(Inkscape::NodePath::Path *np)
526     g_assert(np);
528     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
529     Inkscape::XML::Node *new_repr = old_repr->duplicate();
531     // remember the position of the item
532     gint pos = old_repr->position();
533     // remember parent
534     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
536     SPCurve *curve = create_curve(np);
537     gchar *typestr = create_typestr(np);
539     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
541     new_repr->setAttribute("d", svgpath);
542     new_repr->setAttribute("sodipodi:nodetypes", typestr);
544     // add the new repr to the parent
545     parent->appendChild(new_repr);
546     // move to the saved position
547     new_repr->setPosition(pos > 0 ? pos : 0);
549     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
550                      _("Stamp"));
552     Inkscape::GC::release(new_repr);
553     g_free(svgpath);
554     g_free(typestr);
555     sp_curve_unref(curve);
558 /**
559  * Create curve from path.
560  */
561 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
563     SPCurve *curve = sp_curve_new();
565     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
566        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
567         sp_curve_moveto(curve,
568                         sp->first->pos * np->d2i);
569        Inkscape::NodePath::Node *n = sp->first->n.other;
570         while (n) {
571             NR::Point const end_pt = n->pos * np->d2i;
572             switch (n->code) {
573                 case NR_LINETO:
574                     sp_curve_lineto(curve, end_pt);
575                     break;
576                 case NR_CURVETO:
577                     sp_curve_curveto(curve,
578                                      n->p.other->n.pos * np->d2i,
579                                      n->p.pos * np->d2i,
580                                      end_pt);
581                     break;
582                 default:
583                     g_assert_not_reached();
584                     break;
585             }
586             if (n != sp->last) {
587                 n = n->n.other;
588             } else {
589                 n = NULL;
590             }
591         }
592         if (sp->closed) {
593             sp_curve_closepath(curve);
594         }
595     }
597     return curve;
600 /**
601  * Convert path type string to sodipodi:nodetypes style.
602  */
603 static gchar *create_typestr(Inkscape::NodePath::Path *np)
605     gchar *typestr = g_new(gchar, 32);
606     gint len = 32;
607     gint pos = 0;
609     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
610        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
612         if (pos >= len) {
613             typestr = g_renew(gchar, typestr, len + 32);
614             len += 32;
615         }
617         typestr[pos++] = 'c';
619        Inkscape::NodePath::Node *n;
620         n = sp->first->n.other;
621         while (n) {
622             gchar code;
624             switch (n->type) {
625                 case Inkscape::NodePath::NODE_CUSP:
626                     code = 'c';
627                     break;
628                 case Inkscape::NodePath::NODE_SMOOTH:
629                     code = 's';
630                     break;
631                 case Inkscape::NodePath::NODE_SYMM:
632                     code = 'z';
633                     break;
634                 default:
635                     g_assert_not_reached();
636                     code = '\0';
637                     break;
638             }
640             if (pos >= len) {
641                 typestr = g_renew(gchar, typestr, len + 32);
642                 len += 32;
643             }
645             typestr[pos++] = code;
647             if (n != sp->last) {
648                 n = n->n.other;
649             } else {
650                 n = NULL;
651             }
652         }
653     }
655     if (pos >= len) {
656         typestr = g_renew(gchar, typestr, len + 1);
657         len += 1;
658     }
660     typestr[pos++] = '\0';
662     return typestr;
665 /**
666  * Returns current path in context. // later eliminate this function at all!
667  */
668 static Inkscape::NodePath::Path *sp_nodepath_current()
670     if (!SP_ACTIVE_DESKTOP) {
671         return NULL;
672     }
674     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
676     if (!SP_IS_NODE_CONTEXT(event_context)) {
677         return NULL;
678     }
680     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
685 /**
686  \brief Fills node and handle positions for three nodes, splitting line
687   marked by end at distance t.
688  */
689 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
691     g_assert(new_path != NULL);
692     g_assert(end      != NULL);
694     g_assert(end->p.other == new_path);
695    Inkscape::NodePath::Node *start = new_path->p.other;
696     g_assert(start);
698     if (end->code == NR_LINETO) {
699         new_path->type =Inkscape::NodePath::NODE_CUSP;
700         new_path->code = NR_LINETO;
701         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
702     } else {
703         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
704         new_path->code = NR_CURVETO;
705         gdouble s      = 1 - t;
706         for (int dim = 0; dim < 2; dim++) {
707             NR::Coord const f000 = start->pos[dim];
708             NR::Coord const f001 = start->n.pos[dim];
709             NR::Coord const f011 = end->p.pos[dim];
710             NR::Coord const f111 = end->pos[dim];
711             NR::Coord const f00t = s * f000 + t * f001;
712             NR::Coord const f01t = s * f001 + t * f011;
713             NR::Coord const f11t = s * f011 + t * f111;
714             NR::Coord const f0tt = s * f00t + t * f01t;
715             NR::Coord const f1tt = s * f01t + t * f11t;
716             NR::Coord const fttt = s * f0tt + t * f1tt;
717             start->n.pos[dim]    = f00t;
718             new_path->p.pos[dim] = f0tt;
719             new_path->pos[dim]   = fttt;
720             new_path->n.pos[dim] = f1tt;
721             end->p.pos[dim]      = f11t;
722         }
723     }
726 /**
727  * Adds new node on direct line between two nodes, activates handles of all
728  * three nodes.
729  */
730 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
732     g_assert(end);
733     g_assert(end->subpath);
734     g_assert(g_list_find(end->subpath->nodes, end));
736    Inkscape::NodePath::Node *start = end->p.other;
737     g_assert( start->n.other == end );
738    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
739                                                end,
740                                               Inkscape::NodePath::NODE_SMOOTH,
741                                                (NRPathcode)end->code,
742                                                &start->pos, &start->pos, &start->n.pos);
743     sp_nodepath_line_midpoint(newnode, end, t);
745     sp_node_update_handles(start);
746     sp_node_update_handles(newnode);
747     sp_node_update_handles(end);
749     return newnode;
752 /**
753 \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
754 */
755 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
757     g_assert(node);
758     g_assert(node->subpath);
759     g_assert(g_list_find(node->subpath->nodes, node));
761    Inkscape::NodePath::SubPath *sp = node->subpath;
762     Inkscape::NodePath::Path *np    = sp->nodepath;
764     if (sp->closed) {
765         sp_nodepath_subpath_open(sp, node);
766         return sp->first;
767     } else {
768         // no break for end nodes
769         if (node == sp->first) return NULL;
770         if (node == sp->last ) return NULL;
772         // create a new subpath
773        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
775         // duplicate the break node as start of the new subpath
776        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
778         while (node->n.other) { // copy the remaining nodes into the new subpath
779            Inkscape::NodePath::Node *n  = node->n.other;
780            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);
781             if (n->selected) {
782                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
783             }
784             sp_nodepath_node_destroy(n); // remove the point on the original subpath
785         }
787         return newnode;
788     }
791 /**
792  * Duplicate node and connect to neighbours.
793  */
794 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
796     g_assert(node);
797     g_assert(node->subpath);
798     g_assert(g_list_find(node->subpath->nodes, node));
800    Inkscape::NodePath::SubPath *sp = node->subpath;
802     NRPathcode code = (NRPathcode) node->code;
803     if (code == NR_MOVETO) { // if node is the endnode,
804         node->code = NR_LINETO; // new one is inserted before it, so change that to line
805     }
807     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
809     if (!node->n.other || !node->p.other) // if node is an endnode, select it
810         return node;
811     else
812         return newnode; // otherwise select the newly created node
815 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
817     node->p.pos = (node->pos + (node->pos - node->n.pos));
820 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
822     node->n.pos = (node->pos + (node->pos - node->p.pos));
825 /**
826  * Change line type at node, with side effects on neighbours.
827  */
828 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
830     g_assert(end);
831     g_assert(end->subpath);
832     g_assert(end->p.other);
834     if (end->code == static_cast< guint > ( code ) )
835         return;
837    Inkscape::NodePath::Node *start = end->p.other;
839     end->code = code;
841     if (code == NR_LINETO) {
842         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
843         if (end->n.other) {
844             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
845         }
846         sp_node_adjust_handle(start, -1);
847         sp_node_adjust_handle(end, 1);
848     } else {
849         NR::Point delta = end->pos - start->pos;
850         start->n.pos = start->pos + delta / 3;
851         end->p.pos = end->pos - delta / 3;
852         sp_node_adjust_handle(start, 1);
853         sp_node_adjust_handle(end, -1);
854     }
856     sp_node_update_handles(start);
857     sp_node_update_handles(end);
860 /**
861  * Change node type, and its handles accordingly.
862  */
863 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
865     g_assert(node);
866     g_assert(node->subpath);
868     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
869         return node;
871     if ((node->p.other != NULL) && (node->n.other != NULL)) {
872         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
873             type =Inkscape::NodePath::NODE_CUSP;
874         }
875     }
877     node->type = type;
879     if (node->type == Inkscape::NodePath::NODE_CUSP) {
880         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
881         node->knot->setSize (node->selected? 11 : 9);
882         sp_knot_update_ctrl(node->knot);
883     } else {
884         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
885         node->knot->setSize (node->selected? 9 : 7);
886         sp_knot_update_ctrl(node->knot);
887     }
889     // if one of handles is mouseovered, preserve its position
890     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
891         sp_node_adjust_handle(node, 1);
892     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
893         sp_node_adjust_handle(node, -1);
894     } else {
895         sp_node_adjust_handles(node);
896     }
898     sp_node_update_handles(node);
900     sp_nodepath_update_statusbar(node->subpath->nodepath);
902     return node;
905 /**
906  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
907  * adjacent segments from lines to curves.
908 */
909 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
911     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
912         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
913             // convert adjacent segment BEFORE to curve
914             node->code = NR_CURVETO;
915             NR::Point delta;
916             if (node->n.other != NULL)
917                 delta = node->n.other->pos - node->p.other->pos;
918             else
919                 delta = node->pos - node->p.other->pos;
920             node->p.pos = node->pos - delta / 4;
921             sp_node_update_handles(node);
922         }
924         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
925             // convert adjacent segment AFTER to curve
926             node->n.other->code = NR_CURVETO;
927             NR::Point delta;
928             if (node->p.other != NULL)
929                 delta = node->p.other->pos - node->n.other->pos;
930             else
931                 delta = node->pos - node->n.other->pos;
932             node->n.pos = node->pos - delta / 4;
933             sp_node_update_handles(node);
934         }
935     }
937     sp_nodepath_set_node_type (node, type);
940 /**
941  * Move node to point, and adjust its and neighbouring handles.
942  */
943 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
945     NR::Point delta = p - node->pos;
946     node->pos = p;
948     node->p.pos += delta;
949     node->n.pos += delta;
951     if (node->p.other) {
952         if (node->code == NR_LINETO) {
953             sp_node_adjust_handle(node, 1);
954             sp_node_adjust_handle(node->p.other, -1);
955         }
956     }
957     if (node->n.other) {
958         if (node->n.other->code == NR_LINETO) {
959             sp_node_adjust_handle(node, -1);
960             sp_node_adjust_handle(node->n.other, 1);
961         }
962     }
964     // this function is only called from batch movers that will update display at the end
965     // themselves, so here we just move all the knots without emitting move signals, for speed
966     sp_node_update_handles(node, false);
969 /**
970  * Call sp_node_moveto() for node selection and handle possible snapping.
971  */
972 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
973                                             bool const snap = true)
975     NR::Coord best = NR_HUGE;
976     NR::Point delta(dx, dy);
977     NR::Point best_pt = delta;
979     if (snap) {
980         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
981         
982         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
983             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
984             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, NULL);
985             if (s.getDistance() < best) {
986                 best = s.getDistance();
987                 best_pt = s.getPoint() - n->pos;
988             }
989         }
990     }
992     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
993         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
994         sp_node_moveto(n, n->pos + best_pt);
995     }
997     // do not update repr here so that node dragging is acceptably fast
998     update_object(nodepath);
1001 /**
1002 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1003 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1004 near x = 0.
1005  */
1006 double
1007 sculpt_profile (double x, double alpha, guint profile)
1009     if (x >= 1)
1010         return 0;
1011     if (x <= 0)
1012         return 1;
1014     switch (profile) {
1015         case SCULPT_PROFILE_LINEAR:
1016         return 1 - x;
1017         case SCULPT_PROFILE_BELL:
1018         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1019         case SCULPT_PROFILE_ELLIPTIC:
1020         return sqrt(1 - x*x);
1021     }
1023     return 1;
1026 double
1027 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1029     // extremely primitive for now, don't have time to look for the real one
1030     double lower = NR::L2(b - a);
1031     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1032     return (lower + upper)/2;
1035 void
1036 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1038     n->pos = n->origin + delta;
1039     n->n.pos = n->n.origin + delta_n;
1040     n->p.pos = n->p.origin + delta_p;
1041     sp_node_adjust_handles(n);
1042     sp_node_update_handles(n, false);
1045 /**
1046  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1047  * on how far they are from the dragged node n.
1048  */
1049 static void 
1050 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1052     g_assert (n);
1053     g_assert (nodepath);
1054     g_assert (n->subpath->nodepath == nodepath);
1056     double pressure = n->knot->pressure;
1057     if (pressure == 0)
1058         pressure = 0.5; // default
1059     pressure = CLAMP (pressure, 0.2, 0.8);
1061     // map pressure to alpha = 1/5 ... 5
1062     double alpha = 1 - 2 * fabs(pressure - 0.5);
1063     if (pressure > 0.5)
1064         alpha = 1/alpha;
1066     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1068     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1069         // Only one subpath has selected nodes:
1070         // use linear mode, where the distance from n to node being dragged is calculated along the path
1072         double n_sel_range = 0, p_sel_range = 0;
1073         guint n_nodes = 0, p_nodes = 0;
1074         guint n_sel_nodes = 0, p_sel_nodes = 0;
1076         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1077         {
1078             double n_range = 0, p_range = 0;
1079             bool n_going = true, p_going = true;
1080             Inkscape::NodePath::Node *n_node = n;
1081             Inkscape::NodePath::Node *p_node = n;
1082             do {
1083                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1084                 if (n_node && n_going)
1085                     n_node = n_node->n.other;
1086                 if (n_node == NULL) {
1087                     n_going = false;
1088                 } else {
1089                     n_nodes ++;
1090                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1091                     if (n_node->selected) {
1092                         n_sel_nodes ++;
1093                         n_sel_range = n_range;
1094                     }
1095                     if (n_node == p_node) {
1096                         n_going = false;
1097                         p_going = false;
1098                     }
1099                 }
1100                 if (p_node && p_going)
1101                     p_node = p_node->p.other;
1102                 if (p_node == NULL) {
1103                     p_going = false;
1104                 } else {
1105                     p_nodes ++;
1106                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1107                     if (p_node->selected) {
1108                         p_sel_nodes ++;
1109                         p_sel_range = p_range;
1110                     }
1111                     if (p_node == n_node) {
1112                         n_going = false;
1113                         p_going = false;
1114                     }
1115                 }
1116             } while (n_going || p_going);
1117         }
1119         // Second pass: actually move nodes in this subpath
1120         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1121         {
1122             double n_range = 0, p_range = 0;
1123             bool n_going = true, p_going = true;
1124             Inkscape::NodePath::Node *n_node = n;
1125             Inkscape::NodePath::Node *p_node = n;
1126             do {
1127                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1128                 if (n_node && n_going)
1129                     n_node = n_node->n.other;
1130                 if (n_node == NULL) {
1131                     n_going = false;
1132                 } else {
1133                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1134                     if (n_node->selected) {
1135                         sp_nodepath_move_node_and_handles (n_node, 
1136                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1137                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1138                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1139                     }
1140                     if (n_node == p_node) {
1141                         n_going = false;
1142                         p_going = false;
1143                     }
1144                 }
1145                 if (p_node && p_going)
1146                     p_node = p_node->p.other;
1147                 if (p_node == NULL) {
1148                     p_going = false;
1149                 } else {
1150                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1151                     if (p_node->selected) {
1152                         sp_nodepath_move_node_and_handles (p_node, 
1153                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1154                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1155                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1156                     }
1157                     if (p_node == n_node) {
1158                         n_going = false;
1159                         p_going = false;
1160                     }
1161                 }
1162             } while (n_going || p_going);
1163         }
1165     } else {
1166         // Multiple subpaths have selected nodes:
1167         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1168         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1169         // fix the pear-like shape when sculpting e.g. a ring
1171         // First pass: calculate range
1172         gdouble direct_range = 0;
1173         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1174             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1175             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1176                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1177                 if (node->selected) {
1178                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1179                 }
1180             }
1181         }
1183         // Second pass: actually move nodes
1184         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1185             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1186             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1187                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1188                 if (node->selected) {
1189                     if (direct_range > 1e-6) {
1190                         sp_nodepath_move_node_and_handles (node,
1191                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1192                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1193                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1194                     } else {
1195                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1196                     }
1198                 }
1199             }
1200         }
1201     }
1203     // do not update repr here so that node dragging is acceptably fast
1204     update_object(nodepath);
1208 /**
1209  * Move node selection to point, adjust its and neighbouring handles,
1210  * handle possible snapping, and commit the change with possible undo.
1211  */
1212 void
1213 sp_node_selected_move(gdouble dx, gdouble dy)
1215     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1216     if (!nodepath) return;
1218     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1220     if (dx == 0) {
1221         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1222     } else if (dy == 0) {
1223         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1224     } else {
1225         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1226     }
1229 /**
1230  * Move node selection off screen and commit the change.
1231  */
1232 void
1233 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1235     // borrowed from sp_selection_move_screen in selection-chemistry.c
1236     // we find out the current zoom factor and divide deltas by it
1237     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1239     gdouble zoom = desktop->current_zoom();
1240     gdouble zdx = dx / zoom;
1241     gdouble zdy = dy / zoom;
1243     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1244     if (!nodepath) return;
1246     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1248     if (dx == 0) {
1249         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1250     } else if (dy == 0) {
1251         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1252     } else {
1253         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1254     }
1257 /** If they don't yet exist, creates knot and line for the given side of the node */
1258 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1260     if (!side->knot) {
1261         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"));
1263         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1264         side->knot->setSize (7);
1265         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1266         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1267         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1268         sp_knot_update_ctrl(side->knot);
1270         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1271         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1272         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1273         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1274         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1275         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1276     }
1278     if (!side->line) {
1279         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1280                                         SP_TYPE_CTRLLINE, NULL);
1281     }
1284 /**
1285  * Ensure the given handle of the node is visible/invisible, update its screen position
1286  */
1287 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1289     g_assert(node != NULL);
1291    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1292     NRPathcode code = sp_node_path_code_from_side(node, side);
1294     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1296     if (show_handle) {
1297         if (!side->knot) { // No handle knot at all
1298             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1299             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1300             side->knot->pos = side->pos;
1301             if (side->knot->item) 
1302                 SP_CTRL(side->knot->item)->moveto(side->pos);
1303             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1304             sp_knot_show(side->knot);
1305         } else {
1306             if (side->knot->pos != side->pos) { // only if it's really moved
1307                 if (fire_move_signals) {
1308                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1309                 } else {
1310                     sp_knot_moveto(side->knot, &side->pos);
1311                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1312                 }
1313             }
1314             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1315                 sp_knot_show(side->knot);
1316             }
1317         }
1318         sp_canvas_item_show(side->line);
1319     } else {
1320         if (side->knot) {
1321             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1322                 sp_knot_hide(side->knot);
1323             }
1324         }
1325         if (side->line) {
1326             sp_canvas_item_hide(side->line);
1327         }
1328     }
1331 /**
1332  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1333  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1334  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1335  * updated; otherwise, just move the knots silently (used in batch moves).
1336  */
1337 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1339     g_assert(node != NULL);
1341     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1342         sp_knot_show(node->knot);
1343     }
1345     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1346         if (fire_move_signals)
1347             sp_knot_set_position(node->knot, &node->pos, 0);
1348         else 
1349             sp_knot_moveto(node->knot, &node->pos);
1350     }
1352     gboolean show_handles = node->selected;
1353     if (node->p.other != NULL) {
1354         if (node->p.other->selected) show_handles = TRUE;
1355     }
1356     if (node->n.other != NULL) {
1357         if (node->n.other->selected) show_handles = TRUE;
1358     }
1360     if (node->subpath->nodepath->show_handles == false)
1361         show_handles = FALSE;
1363     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1364     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1367 /**
1368  * Call sp_node_update_handles() for all nodes on subpath.
1369  */
1370 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1372     g_assert(subpath != NULL);
1374     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1375         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1376     }
1379 /**
1380  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1381  */
1382 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1384     g_assert(nodepath != NULL);
1386     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1387         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1388     }
1391 void
1392 sp_nodepath_show_handles(bool show)
1394     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1395     if (nodepath == NULL) return;
1397     nodepath->show_handles = show;
1398     sp_nodepath_update_handles(nodepath);
1401 /**
1402  * Adds all selected nodes in nodepath to list.
1403  */
1404 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1406     StlConv<Node *>::list(l, selected);
1407 /// \todo this adds a copying, rework when the selection becomes a stl list
1410 /**
1411  * Align selected nodes on the specified axis.
1412  */
1413 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1415     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1416         return;
1417     }
1419     if ( !nodepath->selected->next ) { // only one node selected
1420         return;
1421     }
1422    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1423     NR::Point dest(pNode->pos);
1424     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1425         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1426         if (pNode) {
1427             dest[axis] = pNode->pos[axis];
1428             sp_node_moveto(pNode, dest);
1429         }
1430     }
1432     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1435 /// Helper struct.
1436 struct NodeSort
1438    Inkscape::NodePath::Node *_node;
1439     NR::Coord _coord;
1440     /// \todo use vectorof pointers instead of calling copy ctor
1441     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1442         _node(node), _coord(node->pos[axis])
1443     {}
1445 };
1447 static bool operator<(NodeSort const &a, NodeSort const &b)
1449     return (a._coord < b._coord);
1452 /**
1453  * Distribute selected nodes on the specified axis.
1454  */
1455 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1457     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1458         return;
1459     }
1461     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1462         return;
1463     }
1465    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1466     std::vector<NodeSort> sorted;
1467     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1468         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1469         if (pNode) {
1470             NodeSort n(pNode, axis);
1471             sorted.push_back(n);
1472             //dest[axis] = pNode->pos[axis];
1473             //sp_node_moveto(pNode, dest);
1474         }
1475     }
1476     std::sort(sorted.begin(), sorted.end());
1477     unsigned int len = sorted.size();
1478     //overall bboxes span
1479     float dist = (sorted.back()._coord -
1480                   sorted.front()._coord);
1481     //new distance between each bbox
1482     float step = (dist) / (len - 1);
1483     float pos = sorted.front()._coord;
1484     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1485           it < sorted.end();
1486           it ++ )
1487     {
1488         NR::Point dest((*it)._node->pos);
1489         dest[axis] = pos;
1490         sp_node_moveto((*it)._node, dest);
1491         pos += step;
1492     }
1494     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1498 /**
1499  * Call sp_nodepath_line_add_node() for all selected segments.
1500  */
1501 void
1502 sp_node_selected_add_node(void)
1504     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1505     if (!nodepath) {
1506         return;
1507     }
1509     GList *nl = NULL;
1511     int n_added = 0;
1513     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1514        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1515         g_assert(t->selected);
1516         if (t->p.other && t->p.other->selected) {
1517             nl = g_list_prepend(nl, t);
1518         }
1519     }
1521     while (nl) {
1522        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1523        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1524        sp_nodepath_node_select(n, TRUE, FALSE);
1525        n_added ++;
1526        nl = g_list_remove(nl, t);
1527     }
1529     /** \todo fixme: adjust ? */
1530     sp_nodepath_update_handles(nodepath);
1532     if (n_added > 1) {
1533         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1534     } else if (n_added > 0) {
1535         sp_nodepath_update_repr(nodepath, _("Add node"));
1536     }
1538     sp_nodepath_update_statusbar(nodepath);
1541 /**
1542  * Select segment nearest to point
1543  */
1544 void
1545 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1547     if (!nodepath) {
1548         return;
1549     }
1551     sp_nodepath_ensure_livarot_path(nodepath);
1552     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1554     //find segment to segment
1555     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1557     //fixme: this can return NULL, so check before proceeding.
1558     g_return_if_fail(e != NULL);
1559     
1560     gboolean force = FALSE;
1561     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1562         force = TRUE;
1563     }
1564     sp_nodepath_node_select(e, (gboolean) toggle, force);
1565     if (e->p.other)
1566         sp_nodepath_node_select(e->p.other, TRUE, force);
1568     sp_nodepath_update_handles(nodepath);
1570     sp_nodepath_update_statusbar(nodepath);
1573 /**
1574  * Add a node nearest to point
1575  */
1576 void
1577 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1579     if (!nodepath) {
1580         return;
1581     }
1583     sp_nodepath_ensure_livarot_path(nodepath);
1584     Path::cut_position position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1586     //find segment to split
1587     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1589     //don't know why but t seems to flip for lines
1590     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1591         position.t = 1.0 - position.t;
1592     }
1593     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1594     sp_nodepath_node_select(n, FALSE, TRUE);
1596     /* fixme: adjust ? */
1597     sp_nodepath_update_handles(nodepath);
1599     sp_nodepath_update_repr(nodepath, _("Add node"));
1601     sp_nodepath_update_statusbar(nodepath);
1604 /*
1605  * Adjusts a segment so that t moves by a certain delta for dragging
1606  * converts lines to curves
1607  *
1608  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1609  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1610  */
1611 void
1612 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1614     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1616     //fixme: e and e->p can be NULL, so check for those before proceeding
1617     g_return_if_fail(e != NULL);
1618     g_return_if_fail(&e->p != NULL);
1619     
1620     /* feel good is an arbitrary parameter that distributes the delta between handles
1621      * if t of the drag point is less than 1/6 distance form the endpoint only
1622      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1623      */
1624     double feel_good;
1625     if (t <= 1.0 / 6.0)
1626         feel_good = 0;
1627     else if (t <= 0.5)
1628         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1629     else if (t <= 5.0 / 6.0)
1630         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1631     else
1632         feel_good = 1;
1634     //if we're dragging a line convert it to a curve
1635     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1636         sp_nodepath_set_line_type(e, NR_CURVETO);
1637     }
1639     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1640     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1641     e->p.other->n.pos += offsetcoord0;
1642     e->p.pos += offsetcoord1;
1644     // adjust handles of adjacent nodes where necessary
1645     sp_node_adjust_handle(e,1);
1646     sp_node_adjust_handle(e->p.other,-1);
1648     sp_nodepath_update_handles(e->subpath->nodepath);
1650     update_object(e->subpath->nodepath);
1652     sp_nodepath_update_statusbar(e->subpath->nodepath);
1656 /**
1657  * Call sp_nodepath_break() for all selected segments.
1658  */
1659 void sp_node_selected_break()
1661     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1662     if (!nodepath) return;
1664     GList *temp = NULL;
1665     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1666        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1667        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1668         if (nn == NULL) continue; // no break, no new node
1669         temp = g_list_prepend(temp, nn);
1670     }
1672     if (temp) {
1673         sp_nodepath_deselect(nodepath);
1674     }
1675     for (GList *l = temp; l != NULL; l = l->next) {
1676         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1677     }
1679     sp_nodepath_update_handles(nodepath);
1681     sp_nodepath_update_repr(nodepath, _("Break path"));
1684 /**
1685  * Duplicate the selected node(s).
1686  */
1687 void sp_node_selected_duplicate()
1689     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1690     if (!nodepath) {
1691         return;
1692     }
1694     GList *temp = NULL;
1695     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1696        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1697        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1698         if (nn == NULL) continue; // could not duplicate
1699         temp = g_list_prepend(temp, nn);
1700     }
1702     if (temp) {
1703         sp_nodepath_deselect(nodepath);
1704     }
1705     for (GList *l = temp; l != NULL; l = l->next) {
1706         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1707     }
1709     sp_nodepath_update_handles(nodepath);
1711     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1714 /**
1715  *  Join two nodes by merging them into one.
1716  */
1717 void sp_node_selected_join()
1719     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1720     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1722     if (g_list_length(nodepath->selected) != 2) {
1723         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1724         return;
1725     }
1727    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1728    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1730     g_assert(a != b);
1731     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1732         // someone tried to join an orphan node (i.e. a single-node subpath).
1733         // this is not worth an error message, just fail silently.
1734         return;
1735     }
1737     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1738         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1739         return;
1740     }
1742     /* a and b are endpoints */
1744     NR::Point c;
1745     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1746         c = a->pos;
1747     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1748         c = b->pos;
1749     } else {
1750         c = (a->pos + b->pos) / 2;
1751     }
1753     if (a->subpath == b->subpath) {
1754        Inkscape::NodePath::SubPath *sp = a->subpath;
1755         sp_nodepath_subpath_close(sp);
1756         sp_node_moveto (sp->first, c);
1758         sp_nodepath_update_handles(sp->nodepath);
1759         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1760         return;
1761     }
1763     /* a and b are separate subpaths */
1764    Inkscape::NodePath::SubPath *sa = a->subpath;
1765    Inkscape::NodePath::SubPath *sb = b->subpath;
1766     NR::Point p;
1767    Inkscape::NodePath::Node *n;
1768     NRPathcode code;
1769     if (a == sa->first) {
1770         p = sa->first->n.pos;
1771         code = (NRPathcode)sa->first->n.other->code;
1772        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1773         n = sa->last;
1774         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1775         n = n->p.other;
1776         while (n) {
1777             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1778             n = n->p.other;
1779             if (n == sa->first) n = NULL;
1780         }
1781         sp_nodepath_subpath_destroy(sa);
1782         sa = t;
1783     } else if (a == sa->last) {
1784         p = sa->last->p.pos;
1785         code = (NRPathcode)sa->last->code;
1786         sp_nodepath_node_destroy(sa->last);
1787     } else {
1788         code = NR_END;
1789         g_assert_not_reached();
1790     }
1792     if (b == sb->first) {
1793         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1794         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1795             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1796         }
1797     } else if (b == sb->last) {
1798         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1799         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1800             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1801         }
1802     } else {
1803         g_assert_not_reached();
1804     }
1805     /* and now destroy sb */
1807     sp_nodepath_subpath_destroy(sb);
1809     sp_nodepath_update_handles(sa->nodepath);
1811     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1813     sp_nodepath_update_statusbar(nodepath);
1816 /**
1817  *  Join two nodes by adding a segment between them.
1818  */
1819 void sp_node_selected_join_segment()
1821     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1822     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1824     if (g_list_length(nodepath->selected) != 2) {
1825         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1826         return;
1827     }
1829    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1830    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1832     g_assert(a != b);
1833     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1834         // someone tried to join an orphan node (i.e. a single-node subpath).
1835         // this is not worth an error message, just fail silently.
1836         return;
1837     }
1839     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1840         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1841         return;
1842     }
1844     if (a->subpath == b->subpath) {
1845        Inkscape::NodePath::SubPath *sp = a->subpath;
1847         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1848         sp->closed = TRUE;
1850         sp->first->p.other = sp->last;
1851         sp->last->n.other  = sp->first;
1853         sp_node_handle_mirror_p_to_n(sp->last);
1854         sp_node_handle_mirror_n_to_p(sp->first);
1856         sp->first->code = sp->last->code;
1857         sp->first       = sp->last;
1859         sp_nodepath_update_handles(sp->nodepath);
1861         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1863         return;
1864     }
1866     /* a and b are separate subpaths */
1867    Inkscape::NodePath::SubPath *sa = a->subpath;
1868    Inkscape::NodePath::SubPath *sb = b->subpath;
1870    Inkscape::NodePath::Node *n;
1871     NR::Point p;
1872     NRPathcode code;
1873     if (a == sa->first) {
1874         code = (NRPathcode) sa->first->n.other->code;
1875        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1876         n = sa->last;
1877         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1878         for (n = n->p.other; n != NULL; n = n->p.other) {
1879             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1880         }
1881         sp_nodepath_subpath_destroy(sa);
1882         sa = t;
1883     } else if (a == sa->last) {
1884         code = (NRPathcode)sa->last->code;
1885     } else {
1886         code = NR_END;
1887         g_assert_not_reached();
1888     }
1890     if (b == sb->first) {
1891         n = sb->first;
1892         sp_node_handle_mirror_p_to_n(sa->last);
1893         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1894         sp_node_handle_mirror_n_to_p(sa->last);
1895         for (n = n->n.other; n != NULL; n = n->n.other) {
1896             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1897         }
1898     } else if (b == sb->last) {
1899         n = sb->last;
1900         sp_node_handle_mirror_p_to_n(sa->last);
1901         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1902         sp_node_handle_mirror_n_to_p(sa->last);
1903         for (n = n->p.other; n != NULL; n = n->p.other) {
1904             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1905         }
1906     } else {
1907         g_assert_not_reached();
1908     }
1909     /* and now destroy sb */
1911     sp_nodepath_subpath_destroy(sb);
1913     sp_nodepath_update_handles(sa->nodepath);
1915     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1918 /**
1919  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1920  */
1921 void sp_node_delete_preserve(GList *nodes_to_delete)
1923     GSList *nodepaths = NULL;
1924     
1925     while (nodes_to_delete) {
1926         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1927         Inkscape::NodePath::SubPath *sp = node->subpath;
1928         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1929         Inkscape::NodePath::Node *sample_cursor = NULL;
1930         Inkscape::NodePath::Node *sample_end = NULL;
1931         Inkscape::NodePath::Node *delete_cursor = node;
1932         bool just_delete = false;
1933         
1934         //find the start of this contiguous selection
1935         //move left to the first node that is not selected
1936         //or the start of the non-closed path
1937         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1938             delete_cursor = curr;
1939         }
1941         //just delete at the beginning of an open path
1942         if (!delete_cursor->p.other) {
1943             sample_cursor = delete_cursor;
1944             just_delete = true;
1945         } else {
1946             sample_cursor = delete_cursor->p.other;
1947         }
1948         
1949         //calculate points for each segment
1950         int rate = 5;
1951         float period = 1.0 / rate;
1952         std::vector<NR::Point> data;
1953         if (!just_delete) {
1954             data.push_back(sample_cursor->pos);
1955             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1956                 //just delete at the end of an open path
1957                 if (!sp->closed && curr == sp->last) {
1958                     just_delete = true;
1959                     break;
1960                 }
1961                 
1962                 //sample points on the contiguous selected segment
1963                 NR::Point *bez;
1964                 bez = new NR::Point [4];
1965                 bez[0] = curr->pos;
1966                 bez[1] = curr->n.pos;
1967                 bez[2] = curr->n.other->p.pos;
1968                 bez[3] = curr->n.other->pos;
1969                 for (int i=1; i<rate; i++) {
1970                     gdouble t = i * period;
1971                     NR::Point p = bezier_pt(3, bez, t);
1972                     data.push_back(p);
1973                 }
1974                 data.push_back(curr->n.other->pos);
1976                 sample_end = curr->n.other;
1977                 //break if we've come full circle or hit the end of the selection
1978                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1979                     break;
1980                 }
1981             }
1982         }
1984         if (!just_delete) {
1985             //calculate the best fitting single segment and adjust the endpoints
1986             NR::Point *adata;
1987             adata = new NR::Point [data.size()];
1988             copy(data.begin(), data.end(), adata);
1989             
1990             NR::Point *bez;
1991             bez = new NR::Point [4];
1992             //would decreasing error create a better fitting approximation?
1993             gdouble error = 1.0;
1994             gint ret;
1995             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
1997             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
1998             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
1999             //the resulting nodes behave as expected.
2000             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2001             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2002             
2003             //adjust endpoints
2004             sample_cursor->n.pos = bez[1];
2005             sample_end->p.pos = bez[2];
2006         }
2007        
2008         //destroy this contiguous selection
2009         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2010             Inkscape::NodePath::Node *temp = delete_cursor;
2011             if (delete_cursor->n.other == delete_cursor) {
2012                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2013                 delete_cursor = NULL; 
2014             } else {
2015                 delete_cursor = delete_cursor->n.other;
2016             }
2017             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2018             sp_nodepath_node_destroy(temp);
2019         }
2021         sp_nodepath_update_handles(nodepath);
2023         if (!g_slist_find(nodepaths, nodepath))
2024             nodepaths = g_slist_prepend (nodepaths, nodepath);
2025     }
2027     for (GSList *i = nodepaths; i; i = i->next) {
2028         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2029         // different nodepaths will give us one undo event per nodepath
2030         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2032         // if the entire nodepath is removed, delete the selected object.
2033         if (nodepath->subpaths == NULL ||
2034             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2035             //at least 2
2036             sp_nodepath_get_node_count(nodepath) < 2) {
2037             SPDocument *document = sp_desktop_document (nodepath->desktop);
2038             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2039             //delete this nodepath's object, not the entire selection! (though at this time, this
2040             //does not matter)
2041             sp_selection_delete();
2042             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2043                               _("Delete nodes"));
2044         } else {
2045             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2046             sp_nodepath_update_statusbar(nodepath);
2047         }
2048     }
2050     g_slist_free (nodepaths);
2053 /**
2054  * Delete one or more selected nodes.
2055  */
2056 void sp_node_selected_delete()
2058     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2059     if (!nodepath) return;
2060     if (!nodepath->selected) return;
2062     /** \todo fixme: do it the right way */
2063     while (nodepath->selected) {
2064        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2065         sp_nodepath_node_destroy(node);
2066     }
2069     //clean up the nodepath (such as for trivial subpaths)
2070     sp_nodepath_cleanup(nodepath);
2072     sp_nodepath_update_handles(nodepath);
2074     // if the entire nodepath is removed, delete the selected object.
2075     if (nodepath->subpaths == NULL ||
2076         sp_nodepath_get_node_count(nodepath) < 2) {
2077         SPDocument *document = sp_desktop_document (nodepath->desktop);
2078         sp_selection_delete();
2079         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2080                           _("Delete nodes"));
2081         return;
2082     }
2084     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2086     sp_nodepath_update_statusbar(nodepath);
2089 /**
2090  * Delete one or more segments between two selected nodes.
2091  * This is the code for 'split'.
2092  */
2093 void
2094 sp_node_selected_delete_segment(void)
2096    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2097    Inkscape::NodePath::Node *curr, *next;     //Iterators
2099     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2100     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2102     if (g_list_length(nodepath->selected) != 2) {
2103         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2104                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2105         return;
2106     }
2108     //Selected nodes, not inclusive
2109    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2110    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2112     if ( ( a==b)                       ||  //same node
2113          (a->subpath  != b->subpath )  ||  //not the same path
2114          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2115          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2116     {
2117         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2118                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2119         return;
2120     }
2122     //###########################################
2123     //# BEGIN EDITS
2124     //###########################################
2125     //##################################
2126     //# CLOSED PATH
2127     //##################################
2128     if (a->subpath->closed) {
2131         gboolean reversed = FALSE;
2133         //Since we can go in a circle, we need to find the shorter distance.
2134         //  a->b or b->a
2135         start = end = NULL;
2136         int distance    = 0;
2137         int minDistance = 0;
2138         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2139             if (curr==b) {
2140                 //printf("a to b:%d\n", distance);
2141                 start = a;//go from a to b
2142                 end   = b;
2143                 minDistance = distance;
2144                 //printf("A to B :\n");
2145                 break;
2146             }
2147             distance++;
2148         }
2150         //try again, the other direction
2151         distance = 0;
2152         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2153             if (curr==a) {
2154                 //printf("b to a:%d\n", distance);
2155                 if (distance < minDistance) {
2156                     start    = b;  //we go from b to a
2157                     end      = a;
2158                     reversed = TRUE;
2159                     //printf("B to A\n");
2160                 }
2161                 break;
2162             }
2163             distance++;
2164         }
2167         //Copy everything from 'end' to 'start' to a new subpath
2168        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2169         for (curr=end ; curr ; curr=curr->n.other) {
2170             NRPathcode code = (NRPathcode) curr->code;
2171             if (curr == end)
2172                 code = NR_MOVETO;
2173             sp_nodepath_node_new(t, NULL,
2174                                  (Inkscape::NodePath::NodeType)curr->type, code,
2175                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2176             if (curr == start)
2177                 break;
2178         }
2179         sp_nodepath_subpath_destroy(a->subpath);
2182     }
2186     //##################################
2187     //# OPEN PATH
2188     //##################################
2189     else {
2191         //We need to get the direction of the list between A and B
2192         //Can we walk from a to b?
2193         start = end = NULL;
2194         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2195             if (curr==b) {
2196                 start = a;  //did it!  we go from a to b
2197                 end   = b;
2198                 //printf("A to B\n");
2199                 break;
2200             }
2201         }
2202         if (!start) {//didn't work?  let's try the other direction
2203             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2204                 if (curr==a) {
2205                     start = b;  //did it!  we go from b to a
2206                     end   = a;
2207                     //printf("B to A\n");
2208                     break;
2209                 }
2210             }
2211         }
2212         if (!start) {
2213             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2214                                                      _("Cannot find path between nodes."));
2215             return;
2216         }
2220         //Copy everything after 'end' to a new subpath
2221        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2222         for (curr=end ; curr ; curr=curr->n.other) {
2223             NRPathcode code = (NRPathcode) curr->code;
2224             if (curr == end)
2225                 code = NR_MOVETO;
2226             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2227                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2228         }
2230         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2231         for (curr = start->n.other ; curr  ; curr=next) {
2232             next = curr->n.other;
2233             sp_nodepath_node_destroy(curr);
2234         }
2236     }
2237     //###########################################
2238     //# END EDITS
2239     //###########################################
2241     //clean up the nodepath (such as for trivial subpaths)
2242     sp_nodepath_cleanup(nodepath);
2244     sp_nodepath_update_handles(nodepath);
2246     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2248     sp_nodepath_update_statusbar(nodepath);
2251 /**
2252  * Call sp_nodepath_set_line() for all selected segments.
2253  */
2254 void
2255 sp_node_selected_set_line_type(NRPathcode code)
2257     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2258     if (nodepath == NULL) return;
2260     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2261        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2262         g_assert(n->selected);
2263         if (n->p.other && n->p.other->selected) {
2264             sp_nodepath_set_line_type(n, code);
2265         }
2266     }
2268     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2271 /**
2272  * Call sp_nodepath_convert_node_type() for all selected nodes.
2273  */
2274 void
2275 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2277     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2278     if (nodepath == NULL) return;
2280     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2281         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2282     }
2284     sp_nodepath_update_repr(nodepath, _("Change node type"));
2287 /**
2288  * Change select status of node, update its own and neighbour handles.
2289  */
2290 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2292     node->selected = selected;
2294     if (selected) {
2295         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2296         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2297         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2298         sp_knot_update_ctrl(node->knot);
2299     } else {
2300         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2301         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2302         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2303         sp_knot_update_ctrl(node->knot);
2304     }
2306     sp_node_update_handles(node);
2307     if (node->n.other) sp_node_update_handles(node->n.other);
2308     if (node->p.other) sp_node_update_handles(node->p.other);
2311 /**
2312 \brief Select a node
2313 \param node     The node to select
2314 \param incremental   If true, add to selection, otherwise deselect others
2315 \param override   If true, always select this node, otherwise toggle selected status
2316 */
2317 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2319     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2321     if (incremental) {
2322         if (override) {
2323             if (!g_list_find(nodepath->selected, node)) {
2324                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2325             }
2326             sp_node_set_selected(node, TRUE);
2327         } else { // toggle
2328             if (node->selected) {
2329                 g_assert(g_list_find(nodepath->selected, node));
2330                 nodepath->selected = g_list_remove(nodepath->selected, node);
2331             } else {
2332                 g_assert(!g_list_find(nodepath->selected, node));
2333                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2334             }
2335             sp_node_set_selected(node, !node->selected);
2336         }
2337     } else {
2338         sp_nodepath_deselect(nodepath);
2339         nodepath->selected = g_list_prepend(nodepath->selected, node);
2340         sp_node_set_selected(node, TRUE);
2341     }
2343     sp_nodepath_update_statusbar(nodepath);
2347 /**
2348 \brief Deselect all nodes in the nodepath
2349 */
2350 void
2351 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2353     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2355     while (nodepath->selected) {
2356         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2357         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2358     }
2359     sp_nodepath_update_statusbar(nodepath);
2362 /**
2363 \brief Select or invert selection of all nodes in the nodepath
2364 */
2365 void
2366 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2368     if (!nodepath) return;
2370     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2371        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2372         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2373            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2374            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2375         }
2376     }
2379 /**
2380  * If nothing selected, does the same as sp_nodepath_select_all();
2381  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2382  * (i.e., similar to "select all in layer", with the "selected" subpaths
2383  * being treated as "layers" in the path).
2384  */
2385 void
2386 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2388     if (!nodepath) return;
2390     if (g_list_length (nodepath->selected) == 0) {
2391         sp_nodepath_select_all (nodepath, invert);
2392         return;
2393     }
2395     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2396     GSList *subpaths = NULL;
2398     for (GList *l = copy; l != NULL; l = l->next) {
2399         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2400         Inkscape::NodePath::SubPath *subpath = n->subpath;
2401         if (!g_slist_find (subpaths, subpath))
2402             subpaths = g_slist_prepend (subpaths, subpath);
2403     }
2405     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2406         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2407         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2408             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2409             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2410         }
2411     }
2413     g_slist_free (subpaths);
2414     g_list_free (copy);
2417 /**
2418  * \brief Select the node after the last selected; if none is selected,
2419  * select the first within path.
2420  */
2421 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2423     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2425    Inkscape::NodePath::Node *last = NULL;
2426     if (nodepath->selected) {
2427         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2428            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2429             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2430             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2431                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2432                 if (node->selected) {
2433                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2434                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2435                             if (spl->next) { // there's a next subpath
2436                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2437                                 last = subpath_next->first;
2438                             } else if (spl->prev) { // there's a previous subpath
2439                                 last = NULL; // to be set later to the first node of first subpath
2440                             } else {
2441                                 last = node->n.other;
2442                             }
2443                         } else {
2444                             last = node->n.other;
2445                         }
2446                     } else {
2447                         if (node->n.other) {
2448                             last = node->n.other;
2449                         } else {
2450                             if (spl->next) { // there's a next subpath
2451                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2452                                 last = subpath_next->first;
2453                             } else if (spl->prev) { // there's a previous subpath
2454                                 last = NULL; // to be set later to the first node of first subpath
2455                             } else {
2456                                 last = (Inkscape::NodePath::Node *) subpath->first;
2457                             }
2458                         }
2459                     }
2460                 }
2461             }
2462         }
2463         sp_nodepath_deselect(nodepath);
2464     }
2466     if (last) { // there's at least one more node after selected
2467         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2468     } else { // no more nodes, select the first one in first subpath
2469        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2470         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2471     }
2474 /**
2475  * \brief Select the node before the first selected; if none is selected,
2476  * select the last within path
2477  */
2478 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2480     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2482    Inkscape::NodePath::Node *last = NULL;
2483     if (nodepath->selected) {
2484         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2485            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2486             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2487                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2488                 if (node->selected) {
2489                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2490                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2491                             if (spl->prev) { // there's a prev subpath
2492                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2493                                 last = subpath_prev->last;
2494                             } else if (spl->next) { // there's a next subpath
2495                                 last = NULL; // to be set later to the last node of last subpath
2496                             } else {
2497                                 last = node->p.other;
2498                             }
2499                         } else {
2500                             last = node->p.other;
2501                         }
2502                     } else {
2503                         if (node->p.other) {
2504                             last = node->p.other;
2505                         } else {
2506                             if (spl->prev) { // there's a prev subpath
2507                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2508                                 last = subpath_prev->last;
2509                             } else if (spl->next) { // there's a next subpath
2510                                 last = NULL; // to be set later to the last node of last subpath
2511                             } else {
2512                                 last = (Inkscape::NodePath::Node *) subpath->last;
2513                             }
2514                         }
2515                     }
2516                 }
2517             }
2518         }
2519         sp_nodepath_deselect(nodepath);
2520     }
2522     if (last) { // there's at least one more node before selected
2523         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2524     } else { // no more nodes, select the last one in last subpath
2525         GList *spl = g_list_last(nodepath->subpaths);
2526        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2527         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2528     }
2531 /**
2532  * \brief Select all nodes that are within the rectangle.
2533  */
2534 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2536     if (!incremental) {
2537         sp_nodepath_deselect(nodepath);
2538     }
2540     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2541        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2542         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2543            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2545             if (b.contains(node->pos)) {
2546                 sp_nodepath_node_select(node, TRUE, TRUE);
2547             }
2548         }
2549     }
2553 void
2554 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2556     g_assert (n);
2557     g_assert (nodepath);
2558     g_assert (n->subpath->nodepath == nodepath);
2560     if (g_list_length (nodepath->selected) == 0) {
2561         if (grow > 0) {
2562             sp_nodepath_node_select(n, TRUE, TRUE);
2563         }
2564         return;
2565     }
2567     if (g_list_length (nodepath->selected) == 1) {
2568         if (grow < 0) {
2569             sp_nodepath_deselect (nodepath);
2570             return;
2571         }
2572     }
2574         double n_sel_range = 0, p_sel_range = 0;
2575             Inkscape::NodePath::Node *farthest_n_node = n;
2576             Inkscape::NodePath::Node *farthest_p_node = n;
2578         // Calculate ranges
2579         {
2580             double n_range = 0, p_range = 0;
2581             bool n_going = true, p_going = true;
2582             Inkscape::NodePath::Node *n_node = n;
2583             Inkscape::NodePath::Node *p_node = n;
2584             do {
2585                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2586                 if (n_node && n_going)
2587                     n_node = n_node->n.other;
2588                 if (n_node == NULL) {
2589                     n_going = false;
2590                 } else {
2591                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2592                     if (n_node->selected) {
2593                         n_sel_range = n_range;
2594                         farthest_n_node = n_node;
2595                     }
2596                     if (n_node == p_node) {
2597                         n_going = false;
2598                         p_going = false;
2599                     }
2600                 }
2601                 if (p_node && p_going)
2602                     p_node = p_node->p.other;
2603                 if (p_node == NULL) {
2604                     p_going = false;
2605                 } else {
2606                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2607                     if (p_node->selected) {
2608                         p_sel_range = p_range;
2609                         farthest_p_node = p_node;
2610                     }
2611                     if (p_node == n_node) {
2612                         n_going = false;
2613                         p_going = false;
2614                     }
2615                 }
2616             } while (n_going || p_going);
2617         }
2619     if (grow > 0) {
2620         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2621                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2622         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2623                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2624         }
2625     } else {
2626         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2627                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2628         } else if (farthest_p_node && farthest_p_node->selected) {
2629                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2630         }
2631     }
2634 void
2635 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2637     g_assert (n);
2638     g_assert (nodepath);
2639     g_assert (n->subpath->nodepath == nodepath);
2641     if (g_list_length (nodepath->selected) == 0) {
2642         if (grow > 0) {
2643             sp_nodepath_node_select(n, TRUE, TRUE);
2644         }
2645         return;
2646     }
2648     if (g_list_length (nodepath->selected) == 1) {
2649         if (grow < 0) {
2650             sp_nodepath_deselect (nodepath);
2651             return;
2652         }
2653     }
2655     Inkscape::NodePath::Node *farthest_selected = NULL;
2656     double farthest_dist = 0;
2658     Inkscape::NodePath::Node *closest_unselected = NULL;
2659     double closest_dist = NR_HUGE;
2661     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2662        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2663         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2664            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2665            if (node == n)
2666                continue;
2667            if (node->selected) {
2668                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2669                    farthest_dist = NR::L2(node->pos - n->pos);
2670                    farthest_selected = node;
2671                }
2672            } else {
2673                if (NR::L2(node->pos - n->pos) < closest_dist) {
2674                    closest_dist = NR::L2(node->pos - n->pos);
2675                    closest_unselected = node;
2676                }
2677            }
2678         }
2679     }
2681     if (grow > 0) {
2682         if (closest_unselected) {
2683             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2684         }
2685     } else {
2686         if (farthest_selected) {
2687             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2688         }
2689     }
2693 /**
2694 \brief  Saves all nodes' and handles' current positions in their origin members
2695 */
2696 void
2697 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2699     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2700        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2701         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2702            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2703            n->origin = n->pos;
2704            n->p.origin = n->p.pos;
2705            n->n.origin = n->n.pos;
2706         }
2707     }
2708
2710 /**
2711 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2712 */
2713 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2715     if (!nodepath->selected) {
2716         return NULL;
2717     }
2719     GList *r = NULL;
2720     guint i = 0;
2721     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2722        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2723         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2724            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2725             i++;
2726             if (node->selected) {
2727                 r = g_list_append(r, GINT_TO_POINTER(i));
2728             }
2729         }
2730     }
2731     return r;
2734 /**
2735 \brief  Restores selection by selecting nodes whose positions are in the list
2736 */
2737 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2739     sp_nodepath_deselect(nodepath);
2741     guint i = 0;
2742     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2743        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2744         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2745            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2746             i++;
2747             if (g_list_find(r, GINT_TO_POINTER(i))) {
2748                 sp_nodepath_node_select(node, TRUE, TRUE);
2749             }
2750         }
2751     }
2755 /**
2756 \brief Adjusts handle according to node type and line code.
2757 */
2758 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2760     double len, otherlen, linelen;
2762     g_assert(node);
2764    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2765    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2767     /** \todo fixme: */
2768     if (me->other == NULL) return;
2769     if (other->other == NULL) return;
2771     /* I have line */
2773     NRPathcode mecode, ocode;
2774     if (which_adjust == 1) {
2775         mecode = (NRPathcode)me->other->code;
2776         ocode = (NRPathcode)node->code;
2777     } else {
2778         mecode = (NRPathcode)node->code;
2779         ocode = (NRPathcode)other->other->code;
2780     }
2782     if (mecode == NR_LINETO) return;
2784     /* I am curve */
2786     if (other->other == NULL) return;
2788     /* Other has line */
2790     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2792     NR::Point delta;
2793     if (ocode == NR_LINETO) {
2794         /* other is lineto, we are either smooth or symm */
2795        Inkscape::NodePath::Node *othernode = other->other;
2796         len = NR::L2(me->pos - node->pos);
2797         delta = node->pos - othernode->pos;
2798         linelen = NR::L2(delta);
2799         if (linelen < 1e-18) 
2800             return;
2801         me->pos = node->pos + (len / linelen)*delta;
2802         return;
2803     }
2805     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2807         me->pos = 2 * node->pos - other->pos;
2808         return;
2809     }
2811     /* We are smooth */
2813     len = NR::L2(me->pos - node->pos);
2814     delta = other->pos - node->pos;
2815     otherlen = NR::L2(delta);
2816     if (otherlen < 1e-18) return;
2818     me->pos = node->pos - (len / otherlen) * delta;
2821 /**
2822  \brief Adjusts both handles according to node type and line code
2823  */
2824 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2826     g_assert(node);
2828     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2830     /* we are either smooth or symm */
2832     if (node->p.other == NULL) return;
2834     if (node->n.other == NULL) return;
2836     if (node->code == NR_LINETO) {
2837         if (node->n.other->code == NR_LINETO) return;
2838         sp_node_adjust_handle(node, 1);
2839         return;
2840     }
2842     if (node->n.other->code == NR_LINETO) {
2843         if (node->code == NR_LINETO) return;
2844         sp_node_adjust_handle(node, -1);
2845         return;
2846     }
2848     /* both are curves */
2849     NR::Point const delta( node->n.pos - node->p.pos );
2851     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2852         node->p.pos = node->pos - delta / 2;
2853         node->n.pos = node->pos + delta / 2;
2854         return;
2855     }
2857     /* We are smooth */
2858     double plen = NR::L2(node->p.pos - node->pos);
2859     if (plen < 1e-18) return;
2860     double nlen = NR::L2(node->n.pos - node->pos);
2861     if (nlen < 1e-18) return;
2862     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2863     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2866 /**
2867  * Node event callback.
2868  */
2869 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2871     gboolean ret = FALSE;
2872     switch (event->type) {
2873         case GDK_ENTER_NOTIFY:
2874             active_node = n;
2875             break;
2876         case GDK_LEAVE_NOTIFY:
2877             active_node = NULL;
2878             break;
2879         case GDK_SCROLL:
2880             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
2881                 switch (event->scroll.direction) {
2882                     case GDK_SCROLL_UP:
2883                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2884                         break;
2885                     case GDK_SCROLL_DOWN:
2886                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2887                         break;
2888                     default:
2889                         break;
2890                 }
2891                 ret = TRUE;
2892             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
2893                 switch (event->scroll.direction) {
2894                     case GDK_SCROLL_UP:
2895                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2896                         break;
2897                     case GDK_SCROLL_DOWN:
2898                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2899                         break;
2900                     default:
2901                         break;
2902                 }
2903                 ret = TRUE;
2904             }
2905             break;
2906         case GDK_KEY_PRESS:
2907             switch (get_group0_keyval (&event->key)) {
2908                 case GDK_space:
2909                     if (event->key.state & GDK_BUTTON1_MASK) {
2910                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2911                         stamp_repr(nodepath);
2912                         ret = TRUE;
2913                     }
2914                     break;
2915                 case GDK_Page_Up:
2916                     if (event->key.state & GDK_CONTROL_MASK) {
2917                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2918                     } else {
2919                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2920                     }
2921                     break;
2922                 case GDK_Page_Down:
2923                     if (event->key.state & GDK_CONTROL_MASK) {
2924                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2925                     } else {
2926                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2927                     }
2928                     break;
2929                 default:
2930                     break;
2931             }
2932             break;
2933         default:
2934             break;
2935     }
2937     return ret;
2940 /**
2941  * Handle keypress on node; directly called.
2942  */
2943 gboolean node_key(GdkEvent *event)
2945     Inkscape::NodePath::Path *np;
2947     // there is no way to verify nodes so set active_node to nil when deleting!!
2948     if (active_node == NULL) return FALSE;
2950     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2951         gint ret = FALSE;
2952         switch (get_group0_keyval (&event->key)) {
2953             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2954             case GDK_BackSpace:
2955                 np = active_node->subpath->nodepath;
2956                 sp_nodepath_node_destroy(active_node);
2957                 sp_nodepath_update_repr(np, _("Delete node"));
2958                 active_node = NULL;
2959                 ret = TRUE;
2960                 break;
2961             case GDK_c:
2962                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2963                 ret = TRUE;
2964                 break;
2965             case GDK_s:
2966                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2967                 ret = TRUE;
2968                 break;
2969             case GDK_y:
2970                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2971                 ret = TRUE;
2972                 break;
2973             case GDK_b:
2974                 sp_nodepath_node_break(active_node);
2975                 ret = TRUE;
2976                 break;
2977         }
2978         return ret;
2979     }
2980     return FALSE;
2983 /**
2984  * Mouseclick on node callback.
2985  */
2986 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2988    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2990     if (state & GDK_CONTROL_MASK) {
2991         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2993         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2994             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2995                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2996             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2997                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2998             } else {
2999                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3000             }
3001             sp_nodepath_update_repr(nodepath, _("Change node type"));
3002             sp_nodepath_update_statusbar(nodepath);
3004         } else { //ctrl+alt+click: delete node
3005             GList *node_to_delete = NULL;
3006             node_to_delete = g_list_append(node_to_delete, n);
3007             sp_node_delete_preserve(node_to_delete);
3008         }
3010     } else {
3011         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3012     }
3015 /**
3016  * Mouse grabbed node callback.
3017  */
3018 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3020    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3022     if (!n->selected) {
3023         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3024     }
3026     n->is_dragging = true;
3027     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3029     sp_nodepath_remember_origins (n->subpath->nodepath);
3032 /**
3033  * Mouse ungrabbed node callback.
3034  */
3035 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
3037    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3039    n->dragging_out = NULL;
3040    n->is_dragging = false;
3041    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3043    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3046 /**
3047  * The point on a line, given by its angle, closest to the given point.
3048  * \param p  A point.
3049  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3050  * \param closest  Pointer to the point struct where the result is stored.
3051  * \todo FIXME: use dot product perhaps?
3052  */
3053 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3055     if (a == HUGE_VAL) { // vertical
3056         *closest = NR::Point(0, (*p)[NR::Y]);
3057     } else {
3058         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3059         (*closest)[NR::Y] = a * (*closest)[NR::X];
3060     }
3063 /**
3064  * Distance from the point to a line given by its angle.
3065  * \param p  A point.
3066  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3067  */
3068 static double point_line_distance(NR::Point *p, double a)
3070     NR::Point c;
3071     point_line_closest(p, a, &c);
3072     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]));
3075 /**
3076  * Callback for node "request" signal.
3077  * \todo fixme: This goes to "moved" event? (lauris)
3078  */
3079 static gboolean
3080 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3082     double yn, xn, yp, xp;
3083     double an, ap, na, pa;
3084     double d_an, d_ap, d_na, d_pa;
3085     gboolean collinear = FALSE;
3086     NR::Point c;
3087     NR::Point pr;
3089    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3091    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3092    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3094        NR::Point mouse = (*p);
3096        if (!n->dragging_out) {
3097            // This is the first drag-out event; find out which handle to drag out
3098            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3099            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3101            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3102                return FALSE;
3104            Inkscape::NodePath::NodeSide *opposite;
3105            if (appr_p > appr_n) { // closer to p
3106                n->dragging_out = &n->p;
3107                opposite = &n->n;
3108                n->code = NR_CURVETO;
3109            } else if (appr_p < appr_n) { // closer to n
3110                n->dragging_out = &n->n;
3111                opposite = &n->p;
3112                n->n.other->code = NR_CURVETO;
3113            } else { // p and n nodes are the same
3114                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3115                    n->dragging_out = &n->p;
3116                    opposite = &n->n;
3117                    n->code = NR_CURVETO;
3118                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3119                    n->dragging_out = &n->n;
3120                    opposite = &n->p;
3121                    n->n.other->code = NR_CURVETO;
3122                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3123                    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);
3124                    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);
3125                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3126                        n->dragging_out = &n->n;
3127                        opposite = &n->p;
3128                        n->n.other->code = NR_CURVETO;
3129                    } else { // closer to other's n handle
3130                        n->dragging_out = &n->p;
3131                        opposite = &n->n;
3132                        n->code = NR_CURVETO;
3133                    }
3134                }
3135            }
3137            // if there's another handle, make sure the one we drag out starts parallel to it
3138            if (opposite->pos != n->pos) {
3139                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3140            }
3142            // knots might not be created yet!
3143            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3144            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3145        }
3147        // pass this on to the handle-moved callback
3148        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3149        sp_node_update_handles(n);
3150        return TRUE;
3151    }
3153     if (state & GDK_CONTROL_MASK) { // constrained motion
3155         // calculate relative distances of handles
3156         // n handle:
3157         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3158         xn = n->n.pos[NR::X] - n->pos[NR::X];
3159         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3160         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3161             if (n->n.other) { // if there is the next point
3162                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3163                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3164                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3165             }
3166         }
3167         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3168         if (yn < 0) { xn = -xn; yn = -yn; }
3170         // p handle:
3171         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3172         xp = n->p.pos[NR::X] - n->pos[NR::X];
3173         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3174         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3175             if (n->p.other) {
3176                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3177                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3178                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3179             }
3180         }
3181         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3182         if (yp < 0) { xp = -xp; yp = -yp; }
3184         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3185             // sliding on handles, only if at least one of the handles is non-vertical
3186             // (otherwise it's the same as ctrl+drag anyway)
3188             // calculate angles of the handles
3189             if (xn == 0) {
3190                 if (yn == 0) { // no handle, consider it the continuation of the other one
3191                     an = 0;
3192                     collinear = TRUE;
3193                 }
3194                 else an = 0; // vertical; set the angle to horizontal
3195             } else an = yn/xn;
3197             if (xp == 0) {
3198                 if (yp == 0) { // no handle, consider it the continuation of the other one
3199                     ap = an;
3200                 }
3201                 else ap = 0; // vertical; set the angle to horizontal
3202             } else  ap = yp/xp;
3204             if (collinear) an = ap;
3206             // angles of the perpendiculars; HUGE_VAL means vertical
3207             if (an == 0) na = HUGE_VAL; else na = -1/an;
3208             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3210             // mouse point relative to the node's original pos
3211             pr = (*p) - n->origin;
3213             // distances to the four lines (two handles and two perpendiculars)
3214             d_an = point_line_distance(&pr, an);
3215             d_na = point_line_distance(&pr, na);
3216             d_ap = point_line_distance(&pr, ap);
3217             d_pa = point_line_distance(&pr, pa);
3219             // find out which line is the closest, save its closest point in c
3220             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3221                 point_line_closest(&pr, an, &c);
3222             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3223                 point_line_closest(&pr, ap, &c);
3224             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3225                 point_line_closest(&pr, na, &c);
3226             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3227                 point_line_closest(&pr, pa, &c);
3228             }
3230             // move the node to the closest point
3231             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3232                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3233                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3235         } else {  // constraining to hor/vert
3237             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3238                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3239             } else { // snap to vert
3240                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3241             }
3242         }
3243     } else { // move freely
3244         if (n->is_dragging) {
3245             if (state & GDK_MOD1_MASK) { // sculpt
3246                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3247             } else {
3248                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3249                                             (*p)[NR::X] - n->pos[NR::X],
3250                                             (*p)[NR::Y] - n->pos[NR::Y],
3251                                             (state & GDK_SHIFT_MASK) == 0);
3252             }
3253         }
3254     }
3256     n->subpath->nodepath->desktop->scroll_to_point(p);
3258     return TRUE;
3261 /**
3262  * Node handle clicked callback.
3263  */
3264 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3266    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3268     if (state & GDK_CONTROL_MASK) { // "delete" handle
3269         if (n->p.knot == knot) {
3270             n->p.pos = n->pos;
3271         } else if (n->n.knot == knot) {
3272             n->n.pos = n->pos;
3273         }
3274         sp_node_update_handles(n);
3275         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3276         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3277         sp_nodepath_update_statusbar(nodepath);
3279     } else { // just select or add to selection, depending in Shift
3280         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3281     }
3284 /**
3285  * Node handle grabbed callback.
3286  */
3287 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3289    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3291     if (!n->selected) {
3292         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3293     }
3295     // remember the origin point of the handle
3296     if (n->p.knot == knot) {
3297         n->p.origin_radial = n->p.pos - n->pos;
3298     } else if (n->n.knot == knot) {
3299         n->n.origin_radial = n->n.pos - n->pos;
3300     } else {
3301         g_assert_not_reached();
3302     }
3304     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3307 /**
3308  * Node handle ungrabbed callback.
3309  */
3310 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3312    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3314     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3315     if (n->p.knot == knot) {
3316         n->p.origin_radial.a = 0;
3317         sp_knot_set_position(knot, &n->p.pos, state);
3318     } else if (n->n.knot == knot) {
3319         n->n.origin_radial.a = 0;
3320         sp_knot_set_position(knot, &n->n.pos, state);
3321     } else {
3322         g_assert_not_reached();
3323     }
3325     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3328 /**
3329  * Node handle "request" signal callback.
3330  */
3331 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3333     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3335     Inkscape::NodePath::NodeSide *me, *opposite;
3336     gint which;
3337     if (n->p.knot == knot) {
3338         me = &n->p;
3339         opposite = &n->n;
3340         which = -1;
3341     } else if (n->n.knot == knot) {
3342         me = &n->n;
3343         opposite = &n->p;
3344         which = 1;
3345     } else {
3346         me = opposite = NULL;
3347         which = 0;
3348         g_assert_not_reached();
3349     }
3351     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3353     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3355     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3356         /* We are smooth node adjacent with line */
3357         NR::Point const delta = *p - n->pos;
3358         NR::Coord const len = NR::L2(delta);
3359         Inkscape::NodePath::Node *othernode = opposite->other;
3360         NR::Point const ndelta = n->pos - othernode->pos;
3361         NR::Coord const linelen = NR::L2(ndelta);
3362         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3363             NR::Coord const scal = dot(delta, ndelta) / linelen;
3364             (*p) = n->pos + (scal / linelen) * ndelta;
3365         }
3366         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3367     } else {
3368         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3369     }
3371     sp_node_adjust_handle(n, -which);
3373     return FALSE;
3376 /**
3377  * Node handle moved callback.
3378  */
3379 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3381    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3383    Inkscape::NodePath::NodeSide *me;
3384    Inkscape::NodePath::NodeSide *other;
3385     if (n->p.knot == knot) {
3386         me = &n->p;
3387         other = &n->n;
3388     } else if (n->n.knot == knot) {
3389         me = &n->n;
3390         other = &n->p;
3391     } else {
3392         me = NULL;
3393         other = NULL;
3394         g_assert_not_reached();
3395     }
3397     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3398     Radial rme(me->pos - n->pos);
3399     Radial rother(other->pos - n->pos);
3400     Radial rnew(*p - n->pos);
3402     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3403         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3404         /* 0 interpreted as "no snapping". */
3406         // The closest PI/snaps angle, starting from zero.
3407         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3408         if (me->origin_radial.a == HUGE_VAL) {
3409             // ortho doesn't exist: original handle was zero length.
3410             rnew.a = a_snapped;
3411         } else {
3412             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3413              * its opposite and perpendiculars). */
3414             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3416             // Snap to the closest.
3417             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3418                        ? a_snapped
3419                        : a_ortho );
3420         }
3421     }
3423     if (state & GDK_MOD1_MASK) {
3424         // lock handle length
3425         rnew.r = me->origin_radial.r;
3426     }
3428     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3429         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
3430         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3431         rother.a += rnew.a - rme.a;
3432         other->pos = NR::Point(rother) + n->pos;
3433         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3434         sp_knot_set_position(other->knot, &other->pos, 0);
3435     }
3437     me->pos = NR::Point(rnew) + n->pos;
3438     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3440     // this is what sp_knot_set_position does, but without emitting the signal:
3441     // we cannot emit a "moved" signal because we're now processing it
3442     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
3444     knot->desktop->set_coordinate_status(me->pos);
3446     update_object(n->subpath->nodepath);
3448     /* status text */
3449     SPDesktop *desktop = n->subpath->nodepath->desktop;
3450     if (!desktop) return;
3451     SPEventContext *ec = desktop->event_context;
3452     if (!ec) return;
3453     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3454     if (!mc) return;
3456     double degrees = 180 / M_PI * rnew.a;
3457     if (degrees > 180) degrees -= 360;
3458     if (degrees < -180) degrees += 360;
3459     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3460         degrees = angle_to_compass (degrees);
3462     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3464     mc->setF(Inkscape::NORMAL_MESSAGE,
3465          _("<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);
3467     g_string_free(length, TRUE);
3470 /**
3471  * Node handle event callback.
3472  */
3473 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3475     gboolean ret = FALSE;
3476     switch (event->type) {
3477         case GDK_KEY_PRESS:
3478             switch (get_group0_keyval (&event->key)) {
3479                 case GDK_space:
3480                     if (event->key.state & GDK_BUTTON1_MASK) {
3481                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3482                         stamp_repr(nodepath);
3483                         ret = TRUE;
3484                     }
3485                     break;
3486                 default:
3487                     break;
3488             }
3489             break;
3490         default:
3491             break;
3492     }
3494     return ret;
3497 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3498                                  Radial &rme, Radial &rother, gboolean const both)
3500     rme.a += angle;
3501     if ( both
3502          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3503          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3504     {
3505         rother.a += angle;
3506     }
3509 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3510                                         Radial &rme, Radial &rother, gboolean const both)
3512     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3514     gdouble r;
3515     if ( both
3516          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3517          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3518     {
3519         r = MAX(rme.r, rother.r);
3520     } else {
3521         r = rme.r;
3522     }
3524     gdouble const weird_angle = atan2(norm_angle, r);
3525 /* Bulia says norm_angle is just the visible distance that the
3526  * object's end must travel on the screen.  Left as 'angle' for want of
3527  * a better name.*/
3529     rme.a += weird_angle;
3530     if ( both
3531          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3532          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3533     {
3534         rother.a += weird_angle;
3535     }
3538 /**
3539  * Rotate one node.
3540  */
3541 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3543     Inkscape::NodePath::NodeSide *me, *other;
3544     bool both = false;
3546     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3547     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3549     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3550         me = &(n->p);
3551         other = &(n->n);
3552     } else if (!n->p.other) {
3553         me = &(n->n);
3554         other = &(n->p);
3555     } else {
3556         if (which > 0) { // right handle
3557             if (xn > xp) {
3558                 me = &(n->n);
3559                 other = &(n->p);
3560             } else {
3561                 me = &(n->p);
3562                 other = &(n->n);
3563             }
3564         } else if (which < 0){ // left handle
3565             if (xn <= xp) {
3566                 me = &(n->n);
3567                 other = &(n->p);
3568             } else {
3569                 me = &(n->p);
3570                 other = &(n->n);
3571             }
3572         } else { // both handles
3573             me = &(n->n);
3574             other = &(n->p);
3575             both = true;
3576         }
3577     }
3579     Radial rme(me->pos - n->pos);
3580     Radial rother(other->pos - n->pos);
3582     if (screen) {
3583         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3584     } else {
3585         node_rotate_one_internal (*n, angle, rme, rother, both);
3586     }
3588     me->pos = n->pos + NR::Point(rme);
3590     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3591         other->pos =  n->pos + NR::Point(rother);
3592     }
3594     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3595     // so here we just move all the knots without emitting move signals, for speed
3596     sp_node_update_handles(n, false);
3599 /**
3600  * Rotate selected nodes.
3601  */
3602 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3604     if (!nodepath || !nodepath->selected) return;
3606     if (g_list_length(nodepath->selected) == 1) {
3607        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3608         node_rotate_one (n, angle, which, screen);
3609     } else {
3610        // rotate as an object:
3612         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3613         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3614         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3615             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3616             box.expandTo (n->pos); // contain all selected nodes
3617         }
3619         gdouble rot;
3620         if (screen) {
3621             gdouble const zoom = nodepath->desktop->current_zoom();
3622             gdouble const zmove = angle / zoom;
3623             gdouble const r = NR::L2(box.max() - box.midpoint());
3624             rot = atan2(zmove, r);
3625         } else {
3626             rot = angle;
3627         }
3629         NR::Matrix t =
3630             NR::Matrix (NR::translate(-box.midpoint())) *
3631             NR::Matrix (NR::rotate(rot)) *
3632             NR::Matrix (NR::translate(box.midpoint()));
3634         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3635             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3636             n->pos *= t;
3637             n->n.pos *= t;
3638             n->p.pos *= t;
3639             sp_node_update_handles(n, false);
3640         }
3641     }
3643     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3646 /**
3647  * Scale one node.
3648  */
3649 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3651     bool both = false;
3652     Inkscape::NodePath::NodeSide *me, *other;
3654     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3655     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3657     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3658         me = &(n->p);
3659         other = &(n->n);
3660         n->code = NR_CURVETO;
3661     } else if (!n->p.other) {
3662         me = &(n->n);
3663         other = &(n->p);
3664         if (n->n.other)
3665             n->n.other->code = NR_CURVETO;
3666     } else {
3667         if (which > 0) { // right handle
3668             if (xn > xp) {
3669                 me = &(n->n);
3670                 other = &(n->p);
3671                 if (n->n.other)
3672                     n->n.other->code = NR_CURVETO;
3673             } else {
3674                 me = &(n->p);
3675                 other = &(n->n);
3676                 n->code = NR_CURVETO;
3677             }
3678         } else if (which < 0){ // left handle
3679             if (xn <= xp) {
3680                 me = &(n->n);
3681                 other = &(n->p);
3682                 if (n->n.other)
3683                     n->n.other->code = NR_CURVETO;
3684             } else {
3685                 me = &(n->p);
3686                 other = &(n->n);
3687                 n->code = NR_CURVETO;
3688             }
3689         } else { // both handles
3690             me = &(n->n);
3691             other = &(n->p);
3692             both = true;
3693             n->code = NR_CURVETO;
3694             if (n->n.other)
3695                 n->n.other->code = NR_CURVETO;
3696         }
3697     }
3699     Radial rme(me->pos - n->pos);
3700     Radial rother(other->pos - n->pos);
3702     rme.r += grow;
3703     if (rme.r < 0) rme.r = 0;
3704     if (rme.a == HUGE_VAL) {
3705         if (me->other) { // if direction is unknown, initialize it towards the next node
3706             Radial rme_next(me->other->pos - n->pos);
3707             rme.a = rme_next.a;
3708         } else { // if there's no next, initialize to 0
3709             rme.a = 0;
3710         }
3711     }
3712     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3713         rother.r += grow;
3714         if (rother.r < 0) rother.r = 0;
3715         if (rother.a == HUGE_VAL) {
3716             rother.a = rme.a + M_PI;
3717         }
3718     }
3720     me->pos = n->pos + NR::Point(rme);
3722     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3723         other->pos = n->pos + NR::Point(rother);
3724     }
3726     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3727     // so here we just move all the knots without emitting move signals, for speed
3728     sp_node_update_handles(n, false);
3731 /**
3732  * Scale selected nodes.
3733  */
3734 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3736     if (!nodepath || !nodepath->selected) return;
3738     if (g_list_length(nodepath->selected) == 1) {
3739         // scale handles of the single selected node
3740         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3741         node_scale_one (n, grow, which);
3742     } else {
3743         // scale nodes as an "object":
3745         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3746         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3747         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3748             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3749             box.expandTo (n->pos); // contain all selected nodes
3750         }
3752         double scale = (box.maxExtent() + grow)/box.maxExtent();
3754         NR::Matrix t =
3755             NR::Matrix (NR::translate(-box.midpoint())) *
3756             NR::Matrix (NR::scale(scale, scale)) *
3757             NR::Matrix (NR::translate(box.midpoint()));
3759         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3760             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3761             n->pos *= t;
3762             n->n.pos *= t;
3763             n->p.pos *= t;
3764             sp_node_update_handles(n, false);
3765         }
3766     }
3768     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3771 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3773     if (!nodepath) return;
3774     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3777 /**
3778  * Flip selected nodes horizontally/vertically.
3779  */
3780 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3782     if (!nodepath || !nodepath->selected) return;
3784     if (g_list_length(nodepath->selected) == 1) {
3785         // flip handles of the single selected node
3786         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3787         double temp = n->p.pos[axis];
3788         n->p.pos[axis] = n->n.pos[axis];
3789         n->n.pos[axis] = temp;
3790         sp_node_update_handles(n, false);
3791     } else {
3792         // scale nodes as an "object":
3794         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3795         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3796         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3797             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3798             box.expandTo (n->pos); // contain all selected nodes
3799         }
3801         NR::Matrix t =
3802             NR::Matrix (NR::translate(-box.midpoint())) *
3803             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3804             NR::Matrix (NR::translate(box.midpoint()));
3806         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3807             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3808             n->pos *= t;
3809             n->n.pos *= t;
3810             n->p.pos *= t;
3811             sp_node_update_handles(n, false);
3812         }
3813     }
3815     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3818 //-----------------------------------------------
3819 /**
3820  * Return new subpath under given nodepath.
3821  */
3822 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3824     g_assert(nodepath);
3825     g_assert(nodepath->desktop);
3827    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3829     s->nodepath = nodepath;
3830     s->closed = FALSE;
3831     s->nodes = NULL;
3832     s->first = NULL;
3833     s->last = NULL;
3835     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3836     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3837     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3839     return s;
3842 /**
3843  * Destroy nodes in subpath, then subpath itself.
3844  */
3845 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3847     g_assert(subpath);
3848     g_assert(subpath->nodepath);
3849     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3851     while (subpath->nodes) {
3852         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3853     }
3855     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3857     g_free(subpath);
3860 /**
3861  * Link head to tail in subpath.
3862  */
3863 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3865     g_assert(!sp->closed);
3866     g_assert(sp->last != sp->first);
3867     g_assert(sp->first->code == NR_MOVETO);
3869     sp->closed = TRUE;
3871     //Link the head to the tail
3872     sp->first->p.other = sp->last;
3873     sp->last->n.other  = sp->first;
3874     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3875     sp->first          = sp->last;
3877     //Remove the extra end node
3878     sp_nodepath_node_destroy(sp->last->n.other);
3881 /**
3882  * Open closed (loopy) subpath at node.
3883  */
3884 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3886     g_assert(sp->closed);
3887     g_assert(n->subpath == sp);
3888     g_assert(sp->first == sp->last);
3890     /* We create new startpoint, current node will become last one */
3892    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3893                                                 &n->pos, &n->pos, &n->n.pos);
3896     sp->closed        = FALSE;
3898     //Unlink to make a head and tail
3899     sp->first         = new_path;
3900     sp->last          = n;
3901     n->n.other        = NULL;
3902     new_path->p.other = NULL;
3905 /**
3906  * Returns area in triangle given by points; may be negative.
3907  */
3908 inline double
3909 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3911     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]);
3914 /**
3915  * Return new node in subpath with given properties.
3916  * \param pos Position of node.
3917  * \param ppos Handle position in previous direction
3918  * \param npos Handle position in previous direction
3919  */
3920 Inkscape::NodePath::Node *
3921 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)
3923     g_assert(sp);
3924     g_assert(sp->nodepath);
3925     g_assert(sp->nodepath->desktop);
3927     if (nodechunk == NULL)
3928         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3930     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3932     n->subpath  = sp;
3934     if (type != Inkscape::NodePath::NODE_NONE) {
3935         // use the type from sodipodi:nodetypes
3936         n->type = type;
3937     } else {
3938         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3939             // points are (almost) collinear
3940             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3941                 // endnode, or a node with a retracted handle
3942                 n->type = Inkscape::NodePath::NODE_CUSP;
3943             } else {
3944                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3945             }
3946         } else {
3947             n->type = Inkscape::NodePath::NODE_CUSP;
3948         }
3949     }
3951     n->code     = code;
3952     n->selected = FALSE;
3953     n->pos      = *pos;
3954     n->p.pos    = *ppos;
3955     n->n.pos    = *npos;
3957     n->dragging_out = NULL;
3959     Inkscape::NodePath::Node *prev;
3960     if (next) {
3961         //g_assert(g_list_find(sp->nodes, next));
3962         prev = next->p.other;
3963     } else {
3964         prev = sp->last;
3965     }
3967     if (prev)
3968         prev->n.other = n;
3969     else
3970         sp->first = n;
3972     if (next)
3973         next->p.other = n;
3974     else
3975         sp->last = n;
3977     n->p.other = prev;
3978     n->n.other = next;
3980     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"));
3981     sp_knot_set_position(n->knot, pos, 0);
3983     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3984     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3985     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3986     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3987     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3988     sp_knot_update_ctrl(n->knot);
3990     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3991     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3992     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3993     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3994     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3995     sp_knot_show(n->knot);
3997     // We only create handle knots and lines on demand
3998     n->p.knot = NULL;
3999     n->p.line = NULL;
4000     n->n.knot = NULL;
4001     n->n.line = NULL;
4003     sp->nodes = g_list_prepend(sp->nodes, n);
4005     return n;
4008 /**
4009  * Destroy node and its knots, link neighbors in subpath.
4010  */
4011 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4013     g_assert(node);
4014     g_assert(node->subpath);
4015     g_assert(SP_IS_KNOT(node->knot));
4017    Inkscape::NodePath::SubPath *sp = node->subpath;
4019     if (node->selected) { // first, deselect
4020         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4021         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4022     }
4024     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4026     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4027     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4028     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4029     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4030     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4031     g_object_unref(G_OBJECT(node->knot));
4033     if (node->p.knot) {
4034         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4035         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4036         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4037         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4038         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4039         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4040         g_object_unref(G_OBJECT(node->p.knot));
4041         node->p.knot = NULL;
4042     }
4044     if (node->n.knot) {
4045         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4046         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4047         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4048         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4049         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4050         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4051         g_object_unref(G_OBJECT(node->n.knot));
4052         node->n.knot = NULL;
4053     }
4055     if (node->p.line)
4056         gtk_object_destroy(GTK_OBJECT(node->p.line));
4057     if (node->n.line)
4058         gtk_object_destroy(GTK_OBJECT(node->n.line));
4060     if (sp->nodes) { // there are others nodes on the subpath
4061         if (sp->closed) {
4062             if (sp->first == node) {
4063                 g_assert(sp->last == node);
4064                 sp->first = node->n.other;
4065                 sp->last = sp->first;
4066             }
4067             node->p.other->n.other = node->n.other;
4068             node->n.other->p.other = node->p.other;
4069         } else {
4070             if (sp->first == node) {
4071                 sp->first = node->n.other;
4072                 sp->first->code = NR_MOVETO;
4073             }
4074             if (sp->last == node) sp->last = node->p.other;
4075             if (node->p.other) node->p.other->n.other = node->n.other;
4076             if (node->n.other) node->n.other->p.other = node->p.other;
4077         }
4078     } else { // this was the last node on subpath
4079         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4080     }
4082     g_mem_chunk_free(nodechunk, node);
4085 /**
4086  * Returns one of the node's two sides.
4087  * \param which Indicates which side.
4088  * \return Pointer to previous node side if which==-1, next if which==1.
4089  */
4090 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4092     g_assert(node);
4094     switch (which) {
4095         case -1:
4096             return &node->p;
4097         case 1:
4098             return &node->n;
4099         default:
4100             break;
4101     }
4103     g_assert_not_reached();
4105     return NULL;
4108 /**
4109  * Return the other side of the node, given one of its sides.
4110  */
4111 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4113     g_assert(node);
4115     if (me == &node->p) return &node->n;
4116     if (me == &node->n) return &node->p;
4118     g_assert_not_reached();
4120     return NULL;
4123 /**
4124  * Return NRPathcode on the given side of the node.
4125  */
4126 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4128     g_assert(node);
4130     if (me == &node->p) {
4131         if (node->p.other) return (NRPathcode)node->code;
4132         return NR_MOVETO;
4133     }
4135     if (me == &node->n) {
4136         if (node->n.other) return (NRPathcode)node->n.other->code;
4137         return NR_MOVETO;
4138     }
4140     g_assert_not_reached();
4142     return NR_END;
4145 /**
4146  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
4147  */
4148 Inkscape::NodePath::Node *
4149 sp_nodepath_get_node_by_index(int index)
4151     Inkscape::NodePath::Node *e = NULL;
4153     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4154     if (!nodepath) {
4155         return e;
4156     }
4158     //find segment
4159     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4161         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4162         int n = g_list_length(sp->nodes);
4163         if (sp->closed) {
4164             n++;
4165         }
4167         //if the piece belongs to this subpath grab it
4168         //otherwise move onto the next subpath
4169         if (index < n) {
4170             e = sp->first;
4171             for (int i = 0; i < index; ++i) {
4172                 e = e->n.other;
4173             }
4174             break;
4175         } else {
4176             if (sp->closed) {
4177                 index -= (n+1);
4178             } else {
4179                 index -= n;
4180             }
4181         }
4182     }
4184     return e;
4187 /**
4188  * Returns plain text meaning of node type.
4189  */
4190 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4192     unsigned retracted = 0;
4193     bool endnode = false;
4195     for (int which = -1; which <= 1; which += 2) {
4196         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4197         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4198             retracted ++;
4199         if (!side->other)
4200             endnode = true;
4201     }
4203     if (retracted == 0) {
4204         if (endnode) {
4205                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4206                 return _("end node");
4207         } else {
4208             switch (node->type) {
4209                 case Inkscape::NodePath::NODE_CUSP:
4210                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4211                     return _("cusp");
4212                 case Inkscape::NodePath::NODE_SMOOTH:
4213                     // TRANSLATORS: "smooth" is an adjective here
4214                     return _("smooth");
4215                 case Inkscape::NodePath::NODE_SYMM:
4216                     return _("symmetric");
4217             }
4218         }
4219     } else if (retracted == 1) {
4220         if (endnode) {
4221             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4222             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4223         } else {
4224             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4225         }
4226     } else {
4227         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4228     }
4230     return NULL;
4233 /**
4234  * Handles content of statusbar as long as node tool is active.
4235  */
4236 void
4237 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4239     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");
4240     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4242     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4243     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4244     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4245     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4247     SPDesktop *desktop = NULL;
4248     if (nodepath) {
4249         desktop = nodepath->desktop;
4250     } else {
4251         desktop = SP_ACTIVE_DESKTOP;
4252     }
4254     SPEventContext *ec = desktop->event_context;
4255     if (!ec) return;
4256     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4257     if (!mc) return;
4259     if (selected_nodes == 0) {
4260         Inkscape::Selection *sel = desktop->selection;
4261         if (!sel || sel->isEmpty()) {
4262             mc->setF(Inkscape::NORMAL_MESSAGE,
4263                      _("Select a single object to edit its nodes or handles."));
4264         } else {
4265             if (nodepath) {
4266             mc->setF(Inkscape::NORMAL_MESSAGE,
4267                      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.",
4268                               "<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.",
4269                               total_nodes),
4270                      total_nodes);
4271             } else {
4272                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4273                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4274                 } else {
4275                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4276                 }
4277             }
4278         }
4279     } else if (nodepath && selected_nodes == 1) {
4280         mc->setF(Inkscape::NORMAL_MESSAGE,
4281                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4282                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4283                           total_nodes),
4284                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4285     } else {
4286         if (selected_subpaths > 1) {
4287             mc->setF(Inkscape::NORMAL_MESSAGE,
4288                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4289                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4290                               total_nodes),
4291                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4292         } else {
4293             mc->setF(Inkscape::NORMAL_MESSAGE,
4294                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4295                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4296                               total_nodes),
4297                      selected_nodes, total_nodes, when_selected);
4298         }
4299     }
4303 /*
4304   Local Variables:
4305   mode:c++
4306   c-file-style:"stroustrup"
4307   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4308   indent-tabs-mode:nil
4309   fill-column:99
4310   End:
4311 */
4312 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :