Code

Modified filter rendering area handling to better accommodate upcoming feOffset
[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 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
104 /* Adjust handle placement, if the node or the other handle is moved */
105 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
106 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
108 /* Node event callbacks */
109 static void node_clicked(SPKnot *knot, guint state, gpointer data);
110 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
111 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
112 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
114 /* Handle event callbacks */
115 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
116 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
117 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
118 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
119 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
120 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
122 /* Constructors and destructors */
124 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
125 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
126 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
127 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
128 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
129                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
130 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
132 /* Helpers */
134 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
135 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
136 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
138 // active_node indicates mouseover node
139 Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
141 /**
142  * \brief Creates new nodepath from item
143  */
144 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item, bool show_handles)
146     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
148     /** \todo
149      * FIXME: remove this. We don't want to edit paths inside flowtext.
150      * Instead we will build our flowtext with cloned paths, so that the
151      * real paths are outside the flowtext and thus editable as usual.
152      */
153     if (SP_IS_FLOWTEXT(item)) {
154         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
155             if SP_IS_FLOWREGION(child) {
156                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
157                 if (grandchild && SP_IS_PATH(grandchild)) {
158                     item = SP_ITEM(grandchild);
159                     break;
160                 }
161             }
162         }
163     }
165     if (!SP_IS_PATH(item))
166         return NULL;
167     SPPath *path = SP_PATH(item);
168     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
169     if (curve == NULL)
170         return NULL;
172     NArtBpath *bpath = sp_curve_first_bpath(curve);
173     gint length = curve->end;
174     if (length == 0)
175         return NULL; // prevent crash for one-node paths
177     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
178     gchar *typestr = parse_nodetypes(nodetypes, length);
180     //Create new nodepath
181     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
182     if (!np)
183         return NULL;
185     // Set defaults
186     np->desktop     = desktop;
187     np->path        = path;
188     np->subpaths    = NULL;
189     np->selected    = NULL;
190     np->shape_editor = NULL; //Let the shapeeditor that makes this set it
191     np->livarot_path = NULL;
192     np->local_change = 0;
193     np->show_handles = show_handles;
195     // we need to update item's transform from the repr here,
196     // because they may be out of sync when we respond
197     // to a change in repr by regenerating nodepath     --bb
198     sp_object_read_attr(SP_OBJECT(item), "transform");
200     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
201     np->d2i  = np->i2d.inverse();
202     np->repr = repr;
204     // create the subpath(s) from the bpath
205     NArtBpath *b = bpath;
206     while (b->code != NR_END) {
207         b = subpath_from_bpath(np, b, typestr + (b - bpath));
208     }
210     // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
211     np->subpaths = g_list_reverse(np->subpaths);
213     g_free(typestr);
214     sp_curve_unref(curve);
216     // create the livarot representation from the same item
217     sp_nodepath_ensure_livarot_path(np);
219     return np;
222 /**
223  * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
224  */
225 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
227     if (!np)  //soft fail, like delete
228         return;
230     while (np->subpaths) {
231         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
232     }
234     //Inform the ShapeEditor that made me, if any, that I am gone.
235     if (np->shape_editor)
236         np->shape_editor->nodepath_destroyed();
238     g_assert(!np->selected);
240     if (np->livarot_path) {
241         delete np->livarot_path;
242         np->livarot_path = NULL;
243     }
245     np->desktop = NULL;
247     g_free(np);
251 void sp_nodepath_ensure_livarot_path(Inkscape::NodePath::Path *np)
253     if (np && np->livarot_path == NULL && np->path && SP_IS_ITEM(np->path)) {
254         np->livarot_path = Path_for_item (np->path, true, true);
255         if (np->livarot_path)
256             np->livarot_path->ConvertWithBackData(0.01);
257     }
261 /**
262  *  Return the node count of a given NodeSubPath.
263  */
264 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
266     if (!subpath)
267         return 0;
268     gint nodeCount = g_list_length(subpath->nodes);
269     return nodeCount;
272 /**
273  *  Return the node count of a given NodePath.
274  */
275 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
277     if (!np)
278         return 0;
279     gint nodeCount = 0;
280     for (GList *item = np->subpaths ; item ; item=item->next) {
281        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
282         nodeCount += g_list_length(subpath->nodes);
283     }
284     return nodeCount;
287 /**
288  *  Return the subpath count of a given NodePath.
289  */
290 static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
292     if (!np)
293         return 0;
294     return g_list_length (np->subpaths);
297 /**
298  *  Return the selected node count of a given NodePath.
299  */
300 static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
302     if (!np)
303         return 0;
304     return g_list_length (np->selected);
307 /**
308  *  Return the number of subpaths where nodes are selected in a given NodePath.
309  */
310 static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
312     if (!np)
313         return 0;
314     if (!np->selected)
315         return 0;
316     if (!np->selected->next)
317         return 1;
318     gint count = 0;
319     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
320         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
321         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
322             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
323             if (node->selected) {
324                 count ++;
325                 break;
326             }
327         }
328     }
329     return count;
331  
332 /**
333  * Clean up a nodepath after editing.
334  *
335  * Currently we are deleting trivial subpaths.
336  */
337 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
339     GList *badSubPaths = NULL;
341     //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
342     for (GList *l = nodepath->subpaths; l ; l=l->next) {
343        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
344        if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
345             badSubPaths = g_list_append(badSubPaths, sp);
346     }
348     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
349     //also removes the subpath from nodepath->subpaths
350     for (GList *l = badSubPaths; l ; l=l->next) {
351        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
352         sp_nodepath_subpath_destroy(sp);
353     }
355     g_list_free(badSubPaths);
358 /**
359  * Create new nodepath from b, make it subpath of np.
360  * \param t The node type.
361  * \todo Fixme: t should be a proper type, rather than gchar
362  */
363 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
365     NR::Point ppos, pos, npos;
367     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
369     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
370     bool const closed = (b->code == NR_MOVETO);
372     pos = NR::Point(b->x3, b->y3) * np->i2d;
373     if (b[1].code == NR_CURVETO) {
374         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
375     } else {
376         npos = pos;
377     }
378     Inkscape::NodePath::Node *n;
379     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
380     g_assert(sp->first == n);
381     g_assert(sp->last  == n);
383     b++;
384     t++;
385     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
386         pos = NR::Point(b->x3, b->y3) * np->i2d;
387         if (b->code == NR_CURVETO) {
388             ppos = NR::Point(b->x2, b->y2) * np->i2d;
389         } else {
390             ppos = pos;
391         }
392         if (b[1].code == NR_CURVETO) {
393             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
394         } else {
395             npos = pos;
396         }
397         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
398         b++;
399         t++;
400     }
402     if (closed) sp_nodepath_subpath_close(sp);
404     return b;
407 /**
408  * Convert from sodipodi:nodetypes to new style type string.
409  */
410 static gchar *parse_nodetypes(gchar const *types, gint length)
412     g_assert(length > 0);
414     gchar *typestr = g_new(gchar, length + 1);
416     gint pos = 0;
418     if (types) {
419         for (gint i = 0; types[i] && ( i < length ); i++) {
420             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
421             if (types[i] != '\0') {
422                 switch (types[i]) {
423                     case 's':
424                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
425                         break;
426                     case 'z':
427                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
428                         break;
429                     case 'c':
430                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
431                         break;
432                     default:
433                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
434                         break;
435                 }
436             }
437         }
438     }
440     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
442     return typestr;
445 /**
446  * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
447  * updated but repr is not (for speed). Used during curve and node drag.
448  */
449 static void update_object(Inkscape::NodePath::Path *np)
451     g_assert(np);
453     SPCurve *curve = create_curve(np);
455     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
457     sp_curve_unref(curve);
460 /**
461  * Update XML path node with data from path object.
462  */
463 static void update_repr_internal(Inkscape::NodePath::Path *np)
465     g_assert(np);
467     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
469     SPCurve *curve = create_curve(np);
470     gchar *typestr = create_typestr(np);
471     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
473     if (repr->attribute("d") == NULL || strcmp(svgpath, repr->attribute("d"))) { // d changed
474         np->local_change++;
475         repr->setAttribute("d", svgpath);
476     }
478     if (repr->attribute("sodipodi:nodetypes") == NULL || strcmp(typestr, repr->attribute("sodipodi:nodetypes"))) { // nodetypes changed
479         np->local_change++;
480         repr->setAttribute("sodipodi:nodetypes", typestr);
481     }
483     g_free(svgpath);
484     g_free(typestr);
485     sp_curve_unref(curve);
488 /**
489  * Update XML path node with data from path object, commit changes forever.
490  */
491 void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
493     //fixme: np can be NULL, so check before proceeding
494     g_return_if_fail(np != NULL);
496     if (np->livarot_path) {
497         delete np->livarot_path;
498         np->livarot_path = NULL;
499     }
501     update_repr_internal(np);
502     sp_canvas_end_forced_full_redraws(np->desktop->canvas);
503     
504     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE, 
505                      annotation);
508 /**
509  * Update XML path node with data from path object, commit changes with undo.
510  */
511 static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
513     if (np->livarot_path) {
514         delete np->livarot_path;
515         np->livarot_path = NULL;
516     }
518     update_repr_internal(np);
519     sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE, 
520                            annotation);
523 /**
524  * Make duplicate of path, replace corresponding XML node in tree, commit.
525  */
526 static void stamp_repr(Inkscape::NodePath::Path *np)
528     g_assert(np);
530     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
531     Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
533     // remember the position of the item
534     gint pos = old_repr->position();
535     // remember parent
536     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
538     SPCurve *curve = create_curve(np);
539     gchar *typestr = create_typestr(np);
541     gchar *svgpath = sp_svg_write_path(SP_CURVE_BPATH(curve));
543     new_repr->setAttribute("d", svgpath);
544     new_repr->setAttribute("sodipodi:nodetypes", typestr);
546     // add the new repr to the parent
547     parent->appendChild(new_repr);
548     // move to the saved position
549     new_repr->setPosition(pos > 0 ? pos : 0);
551     sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
552                      _("Stamp"));
554     Inkscape::GC::release(new_repr);
555     g_free(svgpath);
556     g_free(typestr);
557     sp_curve_unref(curve);
560 /**
561  * Create curve from path.
562  */
563 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
565     SPCurve *curve = sp_curve_new();
567     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
568        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
569         sp_curve_moveto(curve,
570                         sp->first->pos * np->d2i);
571        Inkscape::NodePath::Node *n = sp->first->n.other;
572         while (n) {
573             NR::Point const end_pt = n->pos * np->d2i;
574             switch (n->code) {
575                 case NR_LINETO:
576                     sp_curve_lineto(curve, end_pt);
577                     break;
578                 case NR_CURVETO:
579                     sp_curve_curveto(curve,
580                                      n->p.other->n.pos * np->d2i,
581                                      n->p.pos * np->d2i,
582                                      end_pt);
583                     break;
584                 default:
585                     g_assert_not_reached();
586                     break;
587             }
588             if (n != sp->last) {
589                 n = n->n.other;
590             } else {
591                 n = NULL;
592             }
593         }
594         if (sp->closed) {
595             sp_curve_closepath(curve);
596         }
597     }
599     return curve;
602 /**
603  * Convert path type string to sodipodi:nodetypes style.
604  */
605 static gchar *create_typestr(Inkscape::NodePath::Path *np)
607     gchar *typestr = g_new(gchar, 32);
608     gint len = 32;
609     gint pos = 0;
611     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
612        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
614         if (pos >= len) {
615             typestr = g_renew(gchar, typestr, len + 32);
616             len += 32;
617         }
619         typestr[pos++] = 'c';
621        Inkscape::NodePath::Node *n;
622         n = sp->first->n.other;
623         while (n) {
624             gchar code;
626             switch (n->type) {
627                 case Inkscape::NodePath::NODE_CUSP:
628                     code = 'c';
629                     break;
630                 case Inkscape::NodePath::NODE_SMOOTH:
631                     code = 's';
632                     break;
633                 case Inkscape::NodePath::NODE_SYMM:
634                     code = 'z';
635                     break;
636                 default:
637                     g_assert_not_reached();
638                     code = '\0';
639                     break;
640             }
642             if (pos >= len) {
643                 typestr = g_renew(gchar, typestr, len + 32);
644                 len += 32;
645             }
647             typestr[pos++] = code;
649             if (n != sp->last) {
650                 n = n->n.other;
651             } else {
652                 n = NULL;
653             }
654         }
655     }
657     if (pos >= len) {
658         typestr = g_renew(gchar, typestr, len + 1);
659         len += 1;
660     }
662     typestr[pos++] = '\0';
664     return typestr;
667 /**
668  * Returns current path in context. // later eliminate this function at all!
669  */
670 static Inkscape::NodePath::Path *sp_nodepath_current()
672     if (!SP_ACTIVE_DESKTOP) {
673         return NULL;
674     }
676     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
678     if (!SP_IS_NODE_CONTEXT(event_context)) {
679         return NULL;
680     }
682     return SP_NODE_CONTEXT(event_context)->shape_editor->get_nodepath();
687 /**
688  \brief Fills node and handle positions for three nodes, splitting line
689   marked by end at distance t.
690  */
691 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
693     g_assert(new_path != NULL);
694     g_assert(end      != NULL);
696     g_assert(end->p.other == new_path);
697    Inkscape::NodePath::Node *start = new_path->p.other;
698     g_assert(start);
700     if (end->code == NR_LINETO) {
701         new_path->type =Inkscape::NodePath::NODE_CUSP;
702         new_path->code = NR_LINETO;
703         new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
704     } else {
705         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
706         new_path->code = NR_CURVETO;
707         gdouble s      = 1 - t;
708         for (int dim = 0; dim < 2; dim++) {
709             NR::Coord const f000 = start->pos[dim];
710             NR::Coord const f001 = start->n.pos[dim];
711             NR::Coord const f011 = end->p.pos[dim];
712             NR::Coord const f111 = end->pos[dim];
713             NR::Coord const f00t = s * f000 + t * f001;
714             NR::Coord const f01t = s * f001 + t * f011;
715             NR::Coord const f11t = s * f011 + t * f111;
716             NR::Coord const f0tt = s * f00t + t * f01t;
717             NR::Coord const f1tt = s * f01t + t * f11t;
718             NR::Coord const fttt = s * f0tt + t * f1tt;
719             start->n.pos[dim]    = f00t;
720             new_path->p.pos[dim] = f0tt;
721             new_path->pos[dim]   = fttt;
722             new_path->n.pos[dim] = f1tt;
723             end->p.pos[dim]      = f11t;
724         }
725     }
728 /**
729  * Adds new node on direct line between two nodes, activates handles of all
730  * three nodes.
731  */
732 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
734     g_assert(end);
735     g_assert(end->subpath);
736     g_assert(g_list_find(end->subpath->nodes, end));
738    Inkscape::NodePath::Node *start = end->p.other;
739     g_assert( start->n.other == end );
740    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
741                                                end,
742                                                (NRPathcode)end->code == NR_LINETO? 
743                                                   Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
744                                                (NRPathcode)end->code,
745                                                &start->pos, &start->pos, &start->n.pos);
746     sp_nodepath_line_midpoint(newnode, end, t);
748     sp_node_adjust_handles(start);
749     sp_node_update_handles(start);
750     sp_node_update_handles(newnode);
751     sp_node_adjust_handles(end);
752     sp_node_update_handles(end);
754     return newnode;
757 /**
758 \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
759 */
760 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
762     g_assert(node);
763     g_assert(node->subpath);
764     g_assert(g_list_find(node->subpath->nodes, node));
766    Inkscape::NodePath::SubPath *sp = node->subpath;
767     Inkscape::NodePath::Path *np    = sp->nodepath;
769     if (sp->closed) {
770         sp_nodepath_subpath_open(sp, node);
771         return sp->first;
772     } else {
773         // no break for end nodes
774         if (node == sp->first) return NULL;
775         if (node == sp->last ) return NULL;
777         // create a new subpath
778        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
780         // duplicate the break node as start of the new subpath
781        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
783         while (node->n.other) { // copy the remaining nodes into the new subpath
784            Inkscape::NodePath::Node *n  = node->n.other;
785            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);
786             if (n->selected) {
787                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
788             }
789             sp_nodepath_node_destroy(n); // remove the point on the original subpath
790         }
792         return newnode;
793     }
796 /**
797  * Duplicate node and connect to neighbours.
798  */
799 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
801     g_assert(node);
802     g_assert(node->subpath);
803     g_assert(g_list_find(node->subpath->nodes, node));
805    Inkscape::NodePath::SubPath *sp = node->subpath;
807     NRPathcode code = (NRPathcode) node->code;
808     if (code == NR_MOVETO) { // if node is the endnode,
809         node->code = NR_LINETO; // new one is inserted before it, so change that to line
810     }
812     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
814     if (!node->n.other || !node->p.other) // if node is an endnode, select it
815         return node;
816     else
817         return newnode; // otherwise select the newly created node
820 static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
822     node->p.pos = (node->pos + (node->pos - node->n.pos));
825 static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
827     node->n.pos = (node->pos + (node->pos - node->p.pos));
830 /**
831  * Change line type at node, with side effects on neighbours.
832  */
833 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
835     g_assert(end);
836     g_assert(end->subpath);
837     g_assert(end->p.other);
839     if (end->code == static_cast< guint > ( code ) )
840         return;
842    Inkscape::NodePath::Node *start = end->p.other;
844     end->code = code;
846     if (code == NR_LINETO) {
847         if (start->code == NR_LINETO) {
848             sp_nodepath_set_node_type (start, Inkscape::NodePath::NODE_CUSP);
849         }
850         if (end->n.other) {
851             if (end->n.other->code == NR_LINETO) {
852                 sp_nodepath_set_node_type (end, Inkscape::NodePath::NODE_CUSP);
853             }
854         }
855     } else {
856         NR::Point delta = end->pos - start->pos;
857         start->n.pos = start->pos + delta / 3;
858         end->p.pos = end->pos - delta / 3;
859         sp_node_adjust_handle(start, 1);
860         sp_node_adjust_handle(end, -1);
861     }
863     sp_node_update_handles(start);
864     sp_node_update_handles(end);
867 /**
868  * Change node type, and its handles accordingly.
869  */
870 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
872     g_assert(node);
873     g_assert(node->subpath);
875     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
876         return node;
878     if ((node->p.other != NULL) && (node->n.other != NULL)) {
879         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
880             type =Inkscape::NodePath::NODE_CUSP;
881         }
882     }
884     node->type = type;
886     if (node->type == Inkscape::NodePath::NODE_CUSP) {
887         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
888         node->knot->setSize (node->selected? 11 : 9);
889         sp_knot_update_ctrl(node->knot);
890     } else {
891         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
892         node->knot->setSize (node->selected? 9 : 7);
893         sp_knot_update_ctrl(node->knot);
894     }
896     // if one of handles is mouseovered, preserve its position
897     if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
898         sp_node_adjust_handle(node, 1);
899     } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
900         sp_node_adjust_handle(node, -1);
901     } else {
902         sp_node_adjust_handles(node);
903     }
905     sp_node_update_handles(node);
907     sp_nodepath_update_statusbar(node->subpath->nodepath);
909     return node;
912 /**
913  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
914  * adjacent segments from lines to curves.
915 */
916 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
918     bool p_line = (node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos);
919     bool n_line = (node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos);
921     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
922         if (p_line && n_line) {
923             // only if both adjacent segments are lines, 
924             // convert both to curves:
926             // BEFORE:
927             {
928             node->code = NR_CURVETO;
929             NR::Point delta = node->n.other->pos - node->p.other->pos;
930             node->p.pos = node->pos - delta / 4;
931             }
933             // AFTER:
934             {
935             node->n.other->code = NR_CURVETO;
936             NR::Point delta = node->p.other->pos - node->n.other->pos;
937             node->n.pos = node->pos - delta / 4;
938             }
940             sp_node_update_handles(node);
941         }
942     }
944     sp_nodepath_set_node_type (node, type);
947 /**
948  * Move node to point, and adjust its and neighbouring handles.
949  */
950 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
952     NR::Point delta = p - node->pos;
953     node->pos = p;
955     node->p.pos += delta;
956     node->n.pos += delta;
958     Inkscape::NodePath::Node *node_p = NULL;
959     Inkscape::NodePath::Node *node_n = NULL;
961     if (node->p.other) {
962         if (node->code == NR_LINETO) {
963             sp_node_adjust_handle(node, 1);
964             sp_node_adjust_handle(node->p.other, -1);
965             node_p = node->p.other;
966         }
967     }
968     if (node->n.other) {
969         if (node->n.other->code == NR_LINETO) {
970             sp_node_adjust_handle(node, -1);
971             sp_node_adjust_handle(node->n.other, 1);
972             node_n = node->n.other;
973         }
974     }
976     // this function is only called from batch movers that will update display at the end
977     // themselves, so here we just move all the knots without emitting move signals, for speed
978     sp_node_update_handles(node, false);
979     if (node_n) {
980         sp_node_update_handles(node_n, false);
981     }
982     if (node_p) {
983         sp_node_update_handles(node_p, false);
984     }
987 /**
988  * Call sp_node_moveto() for node selection and handle possible snapping.
989  */
990 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
991                                             bool const snap = true)
993     NR::Coord best = NR_HUGE;
994     NR::Point delta(dx, dy);
995     NR::Point best_pt = delta;
997     if (snap) {
998         SnapManager const &m = nodepath->desktop->namedview->snap_manager;
999         
1000         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1001             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1002             Inkscape::SnappedPoint const s = m.freeSnap(Inkscape::Snapper::SNAP_POINT, n->pos + delta, n->subpath->nodepath->path);
1003             if (s.getDistance() < best) {
1004                 best = s.getDistance();
1005                 best_pt = s.getPoint() - n->pos;
1006             }
1007         }
1008     }
1010     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1011         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1012         sp_node_moveto(n, n->pos + best_pt);
1013     }
1015     // do not update repr here so that node dragging is acceptably fast
1016     update_object(nodepath);
1019 /**
1020 Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
1021 curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
1022 near x = 0.
1023  */
1024 double
1025 sculpt_profile (double x, double alpha, guint profile)
1027     if (x >= 1)
1028         return 0;
1029     if (x <= 0)
1030         return 1;
1032     switch (profile) {
1033         case SCULPT_PROFILE_LINEAR:
1034         return 1 - x;
1035         case SCULPT_PROFILE_BELL:
1036         return (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
1037         case SCULPT_PROFILE_ELLIPTIC:
1038         return sqrt(1 - x*x);
1039     }
1041     return 1;
1044 double
1045 bezier_length (NR::Point a, NR::Point ah, NR::Point bh, NR::Point b)
1047     // extremely primitive for now, don't have time to look for the real one
1048     double lower = NR::L2(b - a);
1049     double upper = NR::L2(ah - a) + NR::L2(bh - ah) + NR::L2(bh - b);
1050     return (lower + upper)/2;
1053 void
1054 sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, NR::Point delta, NR::Point delta_n, NR::Point delta_p)
1056     n->pos = n->origin + delta;
1057     n->n.pos = n->n.origin + delta_n;
1058     n->p.pos = n->p.origin + delta_p;
1059     sp_node_adjust_handles(n);
1060     sp_node_update_handles(n, false);
1063 /**
1064  * Displace selected nodes and their handles by fractions of delta (from their origins), depending
1065  * on how far they are from the dragged node n.
1066  */
1067 static void 
1068 sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, NR::Point delta)
1070     g_assert (n);
1071     g_assert (nodepath);
1072     g_assert (n->subpath->nodepath == nodepath);
1074     double pressure = n->knot->pressure;
1075     if (pressure == 0)
1076         pressure = 0.5; // default
1077     pressure = CLAMP (pressure, 0.2, 0.8);
1079     // map pressure to alpha = 1/5 ... 5
1080     double alpha = 1 - 2 * fabs(pressure - 0.5);
1081     if (pressure > 0.5)
1082         alpha = 1/alpha;
1084     guint profile = prefs_get_int_attribute("tools.nodes", "sculpting_profile", SCULPT_PROFILE_BELL);
1086     if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
1087         // Only one subpath has selected nodes:
1088         // use linear mode, where the distance from n to node being dragged is calculated along the path
1090         double n_sel_range = 0, p_sel_range = 0;
1091         guint n_nodes = 0, p_nodes = 0;
1092         guint n_sel_nodes = 0, p_sel_nodes = 0;
1094         // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
1095         {
1096             double n_range = 0, p_range = 0;
1097             bool n_going = true, p_going = true;
1098             Inkscape::NodePath::Node *n_node = n;
1099             Inkscape::NodePath::Node *p_node = n;
1100             do {
1101                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1102                 if (n_node && n_going)
1103                     n_node = n_node->n.other;
1104                 if (n_node == NULL) {
1105                     n_going = false;
1106                 } else {
1107                     n_nodes ++;
1108                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1109                     if (n_node->selected) {
1110                         n_sel_nodes ++;
1111                         n_sel_range = n_range;
1112                     }
1113                     if (n_node == p_node) {
1114                         n_going = false;
1115                         p_going = false;
1116                     }
1117                 }
1118                 if (p_node && p_going)
1119                     p_node = p_node->p.other;
1120                 if (p_node == NULL) {
1121                     p_going = false;
1122                 } else {
1123                     p_nodes ++;
1124                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1125                     if (p_node->selected) {
1126                         p_sel_nodes ++;
1127                         p_sel_range = p_range;
1128                     }
1129                     if (p_node == n_node) {
1130                         n_going = false;
1131                         p_going = false;
1132                     }
1133                 }
1134             } while (n_going || p_going);
1135         }
1137         // Second pass: actually move nodes in this subpath
1138         sp_nodepath_move_node_and_handles (n, delta, delta, delta);
1139         {
1140             double n_range = 0, p_range = 0;
1141             bool n_going = true, p_going = true;
1142             Inkscape::NodePath::Node *n_node = n;
1143             Inkscape::NodePath::Node *p_node = n;
1144             do {
1145                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
1146                 if (n_node && n_going)
1147                     n_node = n_node->n.other;
1148                 if (n_node == NULL) {
1149                     n_going = false;
1150                 } else {
1151                     n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
1152                     if (n_node->selected) {
1153                         sp_nodepath_move_node_and_handles (n_node, 
1154                                                            sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
1155                                                            sculpt_profile ((n_range + NR::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
1156                                                            sculpt_profile ((n_range - NR::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
1157                     }
1158                     if (n_node == p_node) {
1159                         n_going = false;
1160                         p_going = false;
1161                     }
1162                 }
1163                 if (p_node && p_going)
1164                     p_node = p_node->p.other;
1165                 if (p_node == NULL) {
1166                     p_going = false;
1167                 } else {
1168                     p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
1169                     if (p_node->selected) {
1170                         sp_nodepath_move_node_and_handles (p_node, 
1171                                                            sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
1172                                                            sculpt_profile ((p_range - NR::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
1173                                                            sculpt_profile ((p_range + NR::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
1174                     }
1175                     if (p_node == n_node) {
1176                         n_going = false;
1177                         p_going = false;
1178                     }
1179                 }
1180             } while (n_going || p_going);
1181         }
1183     } else {
1184         // Multiple subpaths have selected nodes:
1185         // use spatial mode, where the distance from n to node being dragged is measured directly as NR::L2.
1186         // TODO: correct these distances taking into account their angle relative to the bisector, so as to 
1187         // fix the pear-like shape when sculpting e.g. a ring
1189         // First pass: calculate range
1190         gdouble direct_range = 0;
1191         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1192             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1193             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1194                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1195                 if (node->selected) {
1196                     direct_range = MAX(direct_range, NR::L2(node->origin - n->origin));
1197                 }
1198             }
1199         }
1201         // Second pass: actually move nodes
1202         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1203             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1204             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1205                 Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1206                 if (node->selected) {
1207                     if (direct_range > 1e-6) {
1208                         sp_nodepath_move_node_and_handles (node,
1209                                                        sculpt_profile (NR::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
1210                                                        sculpt_profile (NR::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
1211                                                        sculpt_profile (NR::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
1212                     } else {
1213                         sp_nodepath_move_node_and_handles (node, delta, delta, delta);
1214                     }
1216                 }
1217             }
1218         }
1219     }
1221     // do not update repr here so that node dragging is acceptably fast
1222     update_object(nodepath);
1226 /**
1227  * Move node selection to point, adjust its and neighbouring handles,
1228  * handle possible snapping, and commit the change with possible undo.
1229  */
1230 void
1231 sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1233     if (!nodepath) return;
1235     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1237     if (dx == 0) {
1238         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1239     } else if (dy == 0) {
1240         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1241     } else {
1242         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1243     }
1246 /**
1247  * Move node selection off screen and commit the change.
1248  */
1249 void
1250 sp_node_selected_move_screen(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
1252     // borrowed from sp_selection_move_screen in selection-chemistry.c
1253     // we find out the current zoom factor and divide deltas by it
1254     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1256     gdouble zoom = desktop->current_zoom();
1257     gdouble zdx = dx / zoom;
1258     gdouble zdy = dy / zoom;
1260     if (!nodepath) return;
1262     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1264     if (dx == 0) {
1265         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1266     } else if (dy == 0) {
1267         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1268     } else {
1269         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1270     }
1273 /** If they don't yet exist, creates knot and line for the given side of the node */
1274 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1276     if (!side->knot) {
1277         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"));
1279         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1280         side->knot->setSize (7);
1281         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1282         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1283         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1284         sp_knot_update_ctrl(side->knot);
1286         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1287         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1288         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1289         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1290         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1291         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1292     }
1294     if (!side->line) {
1295         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1296                                         SP_TYPE_CTRLLINE, NULL);
1297     }
1300 /**
1301  * Ensure the given handle of the node is visible/invisible, update its screen position
1302  */
1303 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1305     g_assert(node != NULL);
1307    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1308     NRPathcode code = sp_node_path_code_from_side(node, side);
1310     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1312     if (show_handle) {
1313         if (!side->knot) { // No handle knot at all
1314             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1315             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1316             side->knot->pos = side->pos;
1317             if (side->knot->item) 
1318                 SP_CTRL(side->knot->item)->moveto(side->pos);
1319             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1320             sp_knot_show(side->knot);
1321         } else {
1322             if (side->knot->pos != side->pos) { // only if it's really moved
1323                 if (fire_move_signals) {
1324                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1325                 } else {
1326                     sp_knot_moveto(side->knot, &side->pos);
1327                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1328                 }
1329             }
1330             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1331                 sp_knot_show(side->knot);
1332             }
1333         }
1334         sp_canvas_item_show(side->line);
1335     } else {
1336         if (side->knot) {
1337             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1338                 sp_knot_hide(side->knot);
1339             }
1340         }
1341         if (side->line) {
1342             sp_canvas_item_hide(side->line);
1343         }
1344     }
1347 /**
1348  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1349  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1350  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1351  * updated; otherwise, just move the knots silently (used in batch moves).
1352  */
1353 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1355     g_assert(node != NULL);
1357     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1358         sp_knot_show(node->knot);
1359     }
1361     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1362         if (fire_move_signals)
1363             sp_knot_set_position(node->knot, &node->pos, 0);
1364         else 
1365             sp_knot_moveto(node->knot, &node->pos);
1366     }
1368     gboolean show_handles = node->selected;
1369     if (node->p.other != NULL) {
1370         if (node->p.other->selected) show_handles = TRUE;
1371     }
1372     if (node->n.other != NULL) {
1373         if (node->n.other->selected) show_handles = TRUE;
1374     }
1376     if (node->subpath->nodepath->show_handles == false)
1377         show_handles = FALSE;
1379     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1380     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1383 /**
1384  * Call sp_node_update_handles() for all nodes on subpath.
1385  */
1386 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1388     g_assert(subpath != NULL);
1390     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1391         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1392     }
1395 /**
1396  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1397  */
1398 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1400     g_assert(nodepath != NULL);
1402     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1403         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1404     }
1407 void
1408 sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
1410     if (nodepath == NULL) return;
1412     nodepath->show_handles = show;
1413     sp_nodepath_update_handles(nodepath);
1416 /**
1417  * Adds all selected nodes in nodepath to list.
1418  */
1419 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1421     StlConv<Node *>::list(l, selected);
1422 /// \todo this adds a copying, rework when the selection becomes a stl list
1425 /**
1426  * Align selected nodes on the specified axis.
1427  */
1428 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1430     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1431         return;
1432     }
1434     if ( !nodepath->selected->next ) { // only one node selected
1435         return;
1436     }
1437    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1438     NR::Point dest(pNode->pos);
1439     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1440         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1441         if (pNode) {
1442             dest[axis] = pNode->pos[axis];
1443             sp_node_moveto(pNode, dest);
1444         }
1445     }
1447     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1450 /// Helper struct.
1451 struct NodeSort
1453    Inkscape::NodePath::Node *_node;
1454     NR::Coord _coord;
1455     /// \todo use vectorof pointers instead of calling copy ctor
1456     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1457         _node(node), _coord(node->pos[axis])
1458     {}
1460 };
1462 static bool operator<(NodeSort const &a, NodeSort const &b)
1464     return (a._coord < b._coord);
1467 /**
1468  * Distribute selected nodes on the specified axis.
1469  */
1470 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1472     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1473         return;
1474     }
1476     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1477         return;
1478     }
1480    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1481     std::vector<NodeSort> sorted;
1482     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1483         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1484         if (pNode) {
1485             NodeSort n(pNode, axis);
1486             sorted.push_back(n);
1487             //dest[axis] = pNode->pos[axis];
1488             //sp_node_moveto(pNode, dest);
1489         }
1490     }
1491     std::sort(sorted.begin(), sorted.end());
1492     unsigned int len = sorted.size();
1493     //overall bboxes span
1494     float dist = (sorted.back()._coord -
1495                   sorted.front()._coord);
1496     //new distance between each bbox
1497     float step = (dist) / (len - 1);
1498     float pos = sorted.front()._coord;
1499     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1500           it < sorted.end();
1501           it ++ )
1502     {
1503         NR::Point dest((*it)._node->pos);
1504         dest[axis] = pos;
1505         sp_node_moveto((*it)._node, dest);
1506         pos += step;
1507     }
1509     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1513 /**
1514  * Call sp_nodepath_line_add_node() for all selected segments.
1515  */
1516 void
1517 sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
1519     if (!nodepath) {
1520         return;
1521     }
1523     GList *nl = NULL;
1525     int n_added = 0;
1527     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1528        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1529         g_assert(t->selected);
1530         if (t->p.other && t->p.other->selected) {
1531             nl = g_list_prepend(nl, t);
1532         }
1533     }
1535     while (nl) {
1536        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1537        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1538        sp_nodepath_node_select(n, TRUE, FALSE);
1539        n_added ++;
1540        nl = g_list_remove(nl, t);
1541     }
1543     /** \todo fixme: adjust ? */
1544     sp_nodepath_update_handles(nodepath);
1546     if (n_added > 1) {
1547         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1548     } else if (n_added > 0) {
1549         sp_nodepath_update_repr(nodepath, _("Add node"));
1550     }
1552     sp_nodepath_update_statusbar(nodepath);
1555 /**
1556  * Select segment nearest to point
1557  */
1558 void
1559 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1561     if (!nodepath) {
1562         return;
1563     }
1565     sp_nodepath_ensure_livarot_path(nodepath);
1566     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1567     if (!maybe_position) {
1568         return;
1569     }
1570     Path::cut_position position = *maybe_position;
1572     //find segment to segment
1573     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1575     //fixme: this can return NULL, so check before proceeding.
1576     g_return_if_fail(e != NULL);
1577     
1578     gboolean force = FALSE;
1579     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1580         force = TRUE;
1581     }
1582     sp_nodepath_node_select(e, (gboolean) toggle, force);
1583     if (e->p.other)
1584         sp_nodepath_node_select(e->p.other, TRUE, force);
1586     sp_nodepath_update_handles(nodepath);
1588     sp_nodepath_update_statusbar(nodepath);
1591 /**
1592  * Add a node nearest to point
1593  */
1594 void
1595 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1597     if (!nodepath) {
1598         return;
1599     }
1601     sp_nodepath_ensure_livarot_path(nodepath);
1602     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1603     if (!maybe_position) {
1604         return;
1605     }
1606     Path::cut_position position = *maybe_position;
1608     //find segment to split
1609     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1611     //don't know why but t seems to flip for lines
1612     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1613         position.t = 1.0 - position.t;
1614     }
1615     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1616     sp_nodepath_node_select(n, FALSE, TRUE);
1618     /* fixme: adjust ? */
1619     sp_nodepath_update_handles(nodepath);
1621     sp_nodepath_update_repr(nodepath, _("Add node"));
1623     sp_nodepath_update_statusbar(nodepath);
1626 /*
1627  * Adjusts a segment so that t moves by a certain delta for dragging
1628  * converts lines to curves
1629  *
1630  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1631  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1632  */
1633 void
1634 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1636     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1638     //fixme: e and e->p can be NULL, so check for those before proceeding
1639     g_return_if_fail(e != NULL);
1640     g_return_if_fail(&e->p != NULL);
1641     
1642     /* feel good is an arbitrary parameter that distributes the delta between handles
1643      * if t of the drag point is less than 1/6 distance form the endpoint only
1644      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1645      */
1646     double feel_good;
1647     if (t <= 1.0 / 6.0)
1648         feel_good = 0;
1649     else if (t <= 0.5)
1650         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1651     else if (t <= 5.0 / 6.0)
1652         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1653     else
1654         feel_good = 1;
1656     //if we're dragging a line convert it to a curve
1657     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1658         sp_nodepath_set_line_type(e, NR_CURVETO);
1659     }
1661     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1662     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1663     e->p.other->n.pos += offsetcoord0;
1664     e->p.pos += offsetcoord1;
1666     // adjust handles of adjacent nodes where necessary
1667     sp_node_adjust_handle(e,1);
1668     sp_node_adjust_handle(e->p.other,-1);
1670     sp_nodepath_update_handles(e->subpath->nodepath);
1672     update_object(e->subpath->nodepath);
1674     sp_nodepath_update_statusbar(e->subpath->nodepath);
1678 /**
1679  * Call sp_nodepath_break() for all selected segments.
1680  */
1681 void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
1683     if (!nodepath) return;
1685     GList *temp = NULL;
1686     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1687        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1688        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1689         if (nn == NULL) continue; // no break, no new node
1690         temp = g_list_prepend(temp, nn);
1691     }
1693     if (temp) {
1694         sp_nodepath_deselect(nodepath);
1695     }
1696     for (GList *l = temp; l != NULL; l = l->next) {
1697         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1698     }
1700     sp_nodepath_update_handles(nodepath);
1702     sp_nodepath_update_repr(nodepath, _("Break path"));
1705 /**
1706  * Duplicate the selected node(s).
1707  */
1708 void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
1710     if (!nodepath) {
1711         return;
1712     }
1714     GList *temp = NULL;
1715     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1716        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1717        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1718         if (nn == NULL) continue; // could not duplicate
1719         temp = g_list_prepend(temp, nn);
1720     }
1722     if (temp) {
1723         sp_nodepath_deselect(nodepath);
1724     }
1725     for (GList *l = temp; l != NULL; l = l->next) {
1726         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1727     }
1729     sp_nodepath_update_handles(nodepath);
1731     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1734 /**
1735  *  Join two nodes by merging them into one.
1736  */
1737 void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
1739     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1741     if (g_list_length(nodepath->selected) != 2) {
1742         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1743         return;
1744     }
1746    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1747    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1749     g_assert(a != b);
1750     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1751         // someone tried to join an orphan node (i.e. a single-node subpath).
1752         // this is not worth an error message, just fail silently.
1753         return;
1754     }
1756     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1757         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1758         return;
1759     }
1761     /* a and b are endpoints */
1763     NR::Point c;
1764     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1765         c = a->pos;
1766     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1767         c = b->pos;
1768     } else {
1769         c = (a->pos + b->pos) / 2;
1770     }
1772     if (a->subpath == b->subpath) {
1773        Inkscape::NodePath::SubPath *sp = a->subpath;
1774         sp_nodepath_subpath_close(sp);
1775         sp_node_moveto (sp->first, c);
1777         sp_nodepath_update_handles(sp->nodepath);
1778         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1779         return;
1780     }
1782     /* a and b are separate subpaths */
1783    Inkscape::NodePath::SubPath *sa = a->subpath;
1784    Inkscape::NodePath::SubPath *sb = b->subpath;
1785     NR::Point p;
1786    Inkscape::NodePath::Node *n;
1787     NRPathcode code;
1788     if (a == sa->first) {
1789         p = sa->first->n.pos;
1790         code = (NRPathcode)sa->first->n.other->code;
1791        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1792         n = sa->last;
1793         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1794         n = n->p.other;
1795         while (n) {
1796             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1797             n = n->p.other;
1798             if (n == sa->first) n = NULL;
1799         }
1800         sp_nodepath_subpath_destroy(sa);
1801         sa = t;
1802     } else if (a == sa->last) {
1803         p = sa->last->p.pos;
1804         code = (NRPathcode)sa->last->code;
1805         sp_nodepath_node_destroy(sa->last);
1806     } else {
1807         code = NR_END;
1808         g_assert_not_reached();
1809     }
1811     if (b == sb->first) {
1812         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1813         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1814             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1815         }
1816     } else if (b == sb->last) {
1817         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1818         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1819             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1820         }
1821     } else {
1822         g_assert_not_reached();
1823     }
1824     /* and now destroy sb */
1826     sp_nodepath_subpath_destroy(sb);
1828     sp_nodepath_update_handles(sa->nodepath);
1830     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1832     sp_nodepath_update_statusbar(nodepath);
1835 /**
1836  *  Join two nodes by adding a segment between them.
1837  */
1838 void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
1840     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1842     if (g_list_length(nodepath->selected) != 2) {
1843         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1844         return;
1845     }
1847    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1848    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1850     g_assert(a != b);
1851     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1852         // someone tried to join an orphan node (i.e. a single-node subpath).
1853         // this is not worth an error message, just fail silently.
1854         return;
1855     }
1857     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1858         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1859         return;
1860     }
1862     if (a->subpath == b->subpath) {
1863        Inkscape::NodePath::SubPath *sp = a->subpath;
1865         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1866         sp->closed = TRUE;
1868         sp->first->p.other = sp->last;
1869         sp->last->n.other  = sp->first;
1871         sp_node_handle_mirror_p_to_n(sp->last);
1872         sp_node_handle_mirror_n_to_p(sp->first);
1874         sp->first->code = sp->last->code;
1875         sp->first       = sp->last;
1877         sp_nodepath_update_handles(sp->nodepath);
1879         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1881         return;
1882     }
1884     /* a and b are separate subpaths */
1885    Inkscape::NodePath::SubPath *sa = a->subpath;
1886    Inkscape::NodePath::SubPath *sb = b->subpath;
1888    Inkscape::NodePath::Node *n;
1889     NR::Point p;
1890     NRPathcode code;
1891     if (a == sa->first) {
1892         code = (NRPathcode) sa->first->n.other->code;
1893        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1894         n = sa->last;
1895         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1896         for (n = n->p.other; n != NULL; n = n->p.other) {
1897             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1898         }
1899         sp_nodepath_subpath_destroy(sa);
1900         sa = t;
1901     } else if (a == sa->last) {
1902         code = (NRPathcode)sa->last->code;
1903     } else {
1904         code = NR_END;
1905         g_assert_not_reached();
1906     }
1908     if (b == sb->first) {
1909         n = sb->first;
1910         sp_node_handle_mirror_p_to_n(sa->last);
1911         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1912         sp_node_handle_mirror_n_to_p(sa->last);
1913         for (n = n->n.other; n != NULL; n = n->n.other) {
1914             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1915         }
1916     } else if (b == sb->last) {
1917         n = sb->last;
1918         sp_node_handle_mirror_p_to_n(sa->last);
1919         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1920         sp_node_handle_mirror_n_to_p(sa->last);
1921         for (n = n->p.other; n != NULL; n = n->p.other) {
1922             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1923         }
1924     } else {
1925         g_assert_not_reached();
1926     }
1927     /* and now destroy sb */
1929     sp_nodepath_subpath_destroy(sb);
1931     sp_nodepath_update_handles(sa->nodepath);
1933     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1936 /**
1937  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1938  */
1939 void sp_node_delete_preserve(GList *nodes_to_delete)
1941     GSList *nodepaths = NULL;
1942     
1943     while (nodes_to_delete) {
1944         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1945         Inkscape::NodePath::SubPath *sp = node->subpath;
1946         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1947         Inkscape::NodePath::Node *sample_cursor = NULL;
1948         Inkscape::NodePath::Node *sample_end = NULL;
1949         Inkscape::NodePath::Node *delete_cursor = node;
1950         bool just_delete = false;
1951         
1952         //find the start of this contiguous selection
1953         //move left to the first node that is not selected
1954         //or the start of the non-closed path
1955         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1956             delete_cursor = curr;
1957         }
1959         //just delete at the beginning of an open path
1960         if (!delete_cursor->p.other) {
1961             sample_cursor = delete_cursor;
1962             just_delete = true;
1963         } else {
1964             sample_cursor = delete_cursor->p.other;
1965         }
1966         
1967         //calculate points for each segment
1968         int rate = 5;
1969         float period = 1.0 / rate;
1970         std::vector<NR::Point> data;
1971         if (!just_delete) {
1972             data.push_back(sample_cursor->pos);
1973             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1974                 //just delete at the end of an open path
1975                 if (!sp->closed && curr == sp->last) {
1976                     just_delete = true;
1977                     break;
1978                 }
1979                 
1980                 //sample points on the contiguous selected segment
1981                 NR::Point *bez;
1982                 bez = new NR::Point [4];
1983                 bez[0] = curr->pos;
1984                 bez[1] = curr->n.pos;
1985                 bez[2] = curr->n.other->p.pos;
1986                 bez[3] = curr->n.other->pos;
1987                 for (int i=1; i<rate; i++) {
1988                     gdouble t = i * period;
1989                     NR::Point p = bezier_pt(3, bez, t);
1990                     data.push_back(p);
1991                 }
1992                 data.push_back(curr->n.other->pos);
1994                 sample_end = curr->n.other;
1995                 //break if we've come full circle or hit the end of the selection
1996                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
1997                     break;
1998                 }
1999             }
2000         }
2002         if (!just_delete) {
2003             //calculate the best fitting single segment and adjust the endpoints
2004             NR::Point *adata;
2005             adata = new NR::Point [data.size()];
2006             copy(data.begin(), data.end(), adata);
2007             
2008             NR::Point *bez;
2009             bez = new NR::Point [4];
2010             //would decreasing error create a better fitting approximation?
2011             gdouble error = 1.0;
2012             gint ret;
2013             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2015             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2016             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2017             //the resulting nodes behave as expected.
2018             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2019             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2020             
2021             //adjust endpoints
2022             sample_cursor->n.pos = bez[1];
2023             sample_end->p.pos = bez[2];
2024         }
2025        
2026         //destroy this contiguous selection
2027         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2028             Inkscape::NodePath::Node *temp = delete_cursor;
2029             if (delete_cursor->n.other == delete_cursor) {
2030                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2031                 delete_cursor = NULL; 
2032             } else {
2033                 delete_cursor = delete_cursor->n.other;
2034             }
2035             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2036             sp_nodepath_node_destroy(temp);
2037         }
2039         sp_nodepath_update_handles(nodepath);
2041         if (!g_slist_find(nodepaths, nodepath))
2042             nodepaths = g_slist_prepend (nodepaths, nodepath);
2043     }
2045     for (GSList *i = nodepaths; i; i = i->next) {
2046         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2047         // different nodepaths will give us one undo event per nodepath
2048         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2050         // if the entire nodepath is removed, delete the selected object.
2051         if (nodepath->subpaths == NULL ||
2052             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2053             //at least 2
2054             sp_nodepath_get_node_count(nodepath) < 2) {
2055             SPDocument *document = sp_desktop_document (nodepath->desktop);
2056             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2057             //delete this nodepath's object, not the entire selection! (though at this time, this
2058             //does not matter)
2059             sp_selection_delete();
2060             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2061                               _("Delete nodes"));
2062         } else {
2063             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2064             sp_nodepath_update_statusbar(nodepath);
2065         }
2066     }
2068     g_slist_free (nodepaths);
2071 /**
2072  * Delete one or more selected nodes.
2073  */
2074 void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
2076     if (!nodepath) return;
2077     if (!nodepath->selected) return;
2079     /** \todo fixme: do it the right way */
2080     while (nodepath->selected) {
2081        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2082         sp_nodepath_node_destroy(node);
2083     }
2086     //clean up the nodepath (such as for trivial subpaths)
2087     sp_nodepath_cleanup(nodepath);
2089     sp_nodepath_update_handles(nodepath);
2091     // if the entire nodepath is removed, delete the selected object.
2092     if (nodepath->subpaths == NULL ||
2093         sp_nodepath_get_node_count(nodepath) < 2) {
2094         SPDocument *document = sp_desktop_document (nodepath->desktop);
2095         sp_selection_delete();
2096         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2097                           _("Delete nodes"));
2098         return;
2099     }
2101     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2103     sp_nodepath_update_statusbar(nodepath);
2106 /**
2107  * Delete one or more segments between two selected nodes.
2108  * This is the code for 'split'.
2109  */
2110 void
2111 sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
2113    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2114    Inkscape::NodePath::Node *curr, *next;     //Iterators
2116     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2118     if (g_list_length(nodepath->selected) != 2) {
2119         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2120                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2121         return;
2122     }
2124     //Selected nodes, not inclusive
2125    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2126    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2128     if ( ( a==b)                       ||  //same node
2129          (a->subpath  != b->subpath )  ||  //not the same path
2130          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2131          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2132     {
2133         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2134                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2135         return;
2136     }
2138     //###########################################
2139     //# BEGIN EDITS
2140     //###########################################
2141     //##################################
2142     //# CLOSED PATH
2143     //##################################
2144     if (a->subpath->closed) {
2147         gboolean reversed = FALSE;
2149         //Since we can go in a circle, we need to find the shorter distance.
2150         //  a->b or b->a
2151         start = end = NULL;
2152         int distance    = 0;
2153         int minDistance = 0;
2154         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2155             if (curr==b) {
2156                 //printf("a to b:%d\n", distance);
2157                 start = a;//go from a to b
2158                 end   = b;
2159                 minDistance = distance;
2160                 //printf("A to B :\n");
2161                 break;
2162             }
2163             distance++;
2164         }
2166         //try again, the other direction
2167         distance = 0;
2168         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2169             if (curr==a) {
2170                 //printf("b to a:%d\n", distance);
2171                 if (distance < minDistance) {
2172                     start    = b;  //we go from b to a
2173                     end      = a;
2174                     reversed = TRUE;
2175                     //printf("B to A\n");
2176                 }
2177                 break;
2178             }
2179             distance++;
2180         }
2183         //Copy everything from 'end' to 'start' to a new subpath
2184        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2185         for (curr=end ; curr ; curr=curr->n.other) {
2186             NRPathcode code = (NRPathcode) curr->code;
2187             if (curr == end)
2188                 code = NR_MOVETO;
2189             sp_nodepath_node_new(t, NULL,
2190                                  (Inkscape::NodePath::NodeType)curr->type, code,
2191                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2192             if (curr == start)
2193                 break;
2194         }
2195         sp_nodepath_subpath_destroy(a->subpath);
2198     }
2202     //##################################
2203     //# OPEN PATH
2204     //##################################
2205     else {
2207         //We need to get the direction of the list between A and B
2208         //Can we walk from a to b?
2209         start = end = NULL;
2210         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2211             if (curr==b) {
2212                 start = a;  //did it!  we go from a to b
2213                 end   = b;
2214                 //printf("A to B\n");
2215                 break;
2216             }
2217         }
2218         if (!start) {//didn't work?  let's try the other direction
2219             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2220                 if (curr==a) {
2221                     start = b;  //did it!  we go from b to a
2222                     end   = a;
2223                     //printf("B to A\n");
2224                     break;
2225                 }
2226             }
2227         }
2228         if (!start) {
2229             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2230                                                      _("Cannot find path between nodes."));
2231             return;
2232         }
2236         //Copy everything after 'end' to a new subpath
2237        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2238         for (curr=end ; curr ; curr=curr->n.other) {
2239             NRPathcode code = (NRPathcode) curr->code;
2240             if (curr == end)
2241                 code = NR_MOVETO;
2242             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2243                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2244         }
2246         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2247         for (curr = start->n.other ; curr  ; curr=next) {
2248             next = curr->n.other;
2249             sp_nodepath_node_destroy(curr);
2250         }
2252     }
2253     //###########################################
2254     //# END EDITS
2255     //###########################################
2257     //clean up the nodepath (such as for trivial subpaths)
2258     sp_nodepath_cleanup(nodepath);
2260     sp_nodepath_update_handles(nodepath);
2262     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2264     sp_nodepath_update_statusbar(nodepath);
2267 /**
2268  * Call sp_nodepath_set_line() for all selected segments.
2269  */
2270 void
2271 sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
2273     if (nodepath == NULL) return;
2275     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2276        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2277         g_assert(n->selected);
2278         if (n->p.other && n->p.other->selected) {
2279             sp_nodepath_set_line_type(n, code);
2280         }
2281     }
2283     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2286 /**
2287  * Call sp_nodepath_convert_node_type() for all selected nodes.
2288  */
2289 void
2290 sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
2292     if (nodepath == NULL) return;
2294     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2295         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2296     }
2298     sp_nodepath_update_repr(nodepath, _("Change node type"));
2301 /**
2302  * Change select status of node, update its own and neighbour handles.
2303  */
2304 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2306     node->selected = selected;
2308     if (selected) {
2309         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2310         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2311         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2312         sp_knot_update_ctrl(node->knot);
2313     } else {
2314         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2315         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2316         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2317         sp_knot_update_ctrl(node->knot);
2318     }
2320     sp_node_update_handles(node);
2321     if (node->n.other) sp_node_update_handles(node->n.other);
2322     if (node->p.other) sp_node_update_handles(node->p.other);
2325 /**
2326 \brief Select a node
2327 \param node     The node to select
2328 \param incremental   If true, add to selection, otherwise deselect others
2329 \param override   If true, always select this node, otherwise toggle selected status
2330 */
2331 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2333     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2335     if (incremental) {
2336         if (override) {
2337             if (!g_list_find(nodepath->selected, node)) {
2338                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2339             }
2340             sp_node_set_selected(node, TRUE);
2341         } else { // toggle
2342             if (node->selected) {
2343                 g_assert(g_list_find(nodepath->selected, node));
2344                 nodepath->selected = g_list_remove(nodepath->selected, node);
2345             } else {
2346                 g_assert(!g_list_find(nodepath->selected, node));
2347                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2348             }
2349             sp_node_set_selected(node, !node->selected);
2350         }
2351     } else {
2352         sp_nodepath_deselect(nodepath);
2353         nodepath->selected = g_list_prepend(nodepath->selected, node);
2354         sp_node_set_selected(node, TRUE);
2355     }
2357     sp_nodepath_update_statusbar(nodepath);
2361 /**
2362 \brief Deselect all nodes in the nodepath
2363 */
2364 void
2365 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2367     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2369     while (nodepath->selected) {
2370         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2371         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2372     }
2373     sp_nodepath_update_statusbar(nodepath);
2376 /**
2377 \brief Select or invert selection of all nodes in the nodepath
2378 */
2379 void
2380 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2382     if (!nodepath) return;
2384     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2385        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2386         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2387            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2388            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2389         }
2390     }
2393 /**
2394  * If nothing selected, does the same as sp_nodepath_select_all();
2395  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2396  * (i.e., similar to "select all in layer", with the "selected" subpaths
2397  * being treated as "layers" in the path).
2398  */
2399 void
2400 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2402     if (!nodepath) return;
2404     if (g_list_length (nodepath->selected) == 0) {
2405         sp_nodepath_select_all (nodepath, invert);
2406         return;
2407     }
2409     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2410     GSList *subpaths = NULL;
2412     for (GList *l = copy; l != NULL; l = l->next) {
2413         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2414         Inkscape::NodePath::SubPath *subpath = n->subpath;
2415         if (!g_slist_find (subpaths, subpath))
2416             subpaths = g_slist_prepend (subpaths, subpath);
2417     }
2419     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2420         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2421         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2422             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2423             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2424         }
2425     }
2427     g_slist_free (subpaths);
2428     g_list_free (copy);
2431 /**
2432  * \brief Select the node after the last selected; if none is selected,
2433  * select the first within path.
2434  */
2435 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2437     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2439    Inkscape::NodePath::Node *last = NULL;
2440     if (nodepath->selected) {
2441         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2442            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2443             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2444             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2445                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2446                 if (node->selected) {
2447                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2448                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2449                             if (spl->next) { // there's a next subpath
2450                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2451                                 last = subpath_next->first;
2452                             } else if (spl->prev) { // there's a previous subpath
2453                                 last = NULL; // to be set later to the first node of first subpath
2454                             } else {
2455                                 last = node->n.other;
2456                             }
2457                         } else {
2458                             last = node->n.other;
2459                         }
2460                     } else {
2461                         if (node->n.other) {
2462                             last = node->n.other;
2463                         } else {
2464                             if (spl->next) { // there's a next subpath
2465                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2466                                 last = subpath_next->first;
2467                             } else if (spl->prev) { // there's a previous subpath
2468                                 last = NULL; // to be set later to the first node of first subpath
2469                             } else {
2470                                 last = (Inkscape::NodePath::Node *) subpath->first;
2471                             }
2472                         }
2473                     }
2474                 }
2475             }
2476         }
2477         sp_nodepath_deselect(nodepath);
2478     }
2480     if (last) { // there's at least one more node after selected
2481         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2482     } else { // no more nodes, select the first one in first subpath
2483        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2484         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2485     }
2488 /**
2489  * \brief Select the node before the first selected; if none is selected,
2490  * select the last within path
2491  */
2492 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2494     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2496    Inkscape::NodePath::Node *last = NULL;
2497     if (nodepath->selected) {
2498         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2499            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2500             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2501                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2502                 if (node->selected) {
2503                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2504                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2505                             if (spl->prev) { // there's a prev subpath
2506                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2507                                 last = subpath_prev->last;
2508                             } else if (spl->next) { // there's a next subpath
2509                                 last = NULL; // to be set later to the last node of last subpath
2510                             } else {
2511                                 last = node->p.other;
2512                             }
2513                         } else {
2514                             last = node->p.other;
2515                         }
2516                     } else {
2517                         if (node->p.other) {
2518                             last = node->p.other;
2519                         } else {
2520                             if (spl->prev) { // there's a prev subpath
2521                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2522                                 last = subpath_prev->last;
2523                             } else if (spl->next) { // there's a next subpath
2524                                 last = NULL; // to be set later to the last node of last subpath
2525                             } else {
2526                                 last = (Inkscape::NodePath::Node *) subpath->last;
2527                             }
2528                         }
2529                     }
2530                 }
2531             }
2532         }
2533         sp_nodepath_deselect(nodepath);
2534     }
2536     if (last) { // there's at least one more node before selected
2537         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2538     } else { // no more nodes, select the last one in last subpath
2539         GList *spl = g_list_last(nodepath->subpaths);
2540        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2541         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2542     }
2545 /**
2546  * \brief Select all nodes that are within the rectangle.
2547  */
2548 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2550     if (!incremental) {
2551         sp_nodepath_deselect(nodepath);
2552     }
2554     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2555        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2556         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2557            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2559             if (b.contains(node->pos)) {
2560                 sp_nodepath_node_select(node, TRUE, TRUE);
2561             }
2562         }
2563     }
2567 void
2568 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2570     g_assert (n);
2571     g_assert (nodepath);
2572     g_assert (n->subpath->nodepath == nodepath);
2574     if (g_list_length (nodepath->selected) == 0) {
2575         if (grow > 0) {
2576             sp_nodepath_node_select(n, TRUE, TRUE);
2577         }
2578         return;
2579     }
2581     if (g_list_length (nodepath->selected) == 1) {
2582         if (grow < 0) {
2583             sp_nodepath_deselect (nodepath);
2584             return;
2585         }
2586     }
2588         double n_sel_range = 0, p_sel_range = 0;
2589             Inkscape::NodePath::Node *farthest_n_node = n;
2590             Inkscape::NodePath::Node *farthest_p_node = n;
2592         // Calculate ranges
2593         {
2594             double n_range = 0, p_range = 0;
2595             bool n_going = true, p_going = true;
2596             Inkscape::NodePath::Node *n_node = n;
2597             Inkscape::NodePath::Node *p_node = n;
2598             do {
2599                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2600                 if (n_node && n_going)
2601                     n_node = n_node->n.other;
2602                 if (n_node == NULL) {
2603                     n_going = false;
2604                 } else {
2605                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2606                     if (n_node->selected) {
2607                         n_sel_range = n_range;
2608                         farthest_n_node = n_node;
2609                     }
2610                     if (n_node == p_node) {
2611                         n_going = false;
2612                         p_going = false;
2613                     }
2614                 }
2615                 if (p_node && p_going)
2616                     p_node = p_node->p.other;
2617                 if (p_node == NULL) {
2618                     p_going = false;
2619                 } else {
2620                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2621                     if (p_node->selected) {
2622                         p_sel_range = p_range;
2623                         farthest_p_node = p_node;
2624                     }
2625                     if (p_node == n_node) {
2626                         n_going = false;
2627                         p_going = false;
2628                     }
2629                 }
2630             } while (n_going || p_going);
2631         }
2633     if (grow > 0) {
2634         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2635                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2636         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2637                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2638         }
2639     } else {
2640         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2641                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2642         } else if (farthest_p_node && farthest_p_node->selected) {
2643                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2644         }
2645     }
2648 void
2649 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2651     g_assert (n);
2652     g_assert (nodepath);
2653     g_assert (n->subpath->nodepath == nodepath);
2655     if (g_list_length (nodepath->selected) == 0) {
2656         if (grow > 0) {
2657             sp_nodepath_node_select(n, TRUE, TRUE);
2658         }
2659         return;
2660     }
2662     if (g_list_length (nodepath->selected) == 1) {
2663         if (grow < 0) {
2664             sp_nodepath_deselect (nodepath);
2665             return;
2666         }
2667     }
2669     Inkscape::NodePath::Node *farthest_selected = NULL;
2670     double farthest_dist = 0;
2672     Inkscape::NodePath::Node *closest_unselected = NULL;
2673     double closest_dist = NR_HUGE;
2675     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2676        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2677         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2678            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2679            if (node == n)
2680                continue;
2681            if (node->selected) {
2682                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2683                    farthest_dist = NR::L2(node->pos - n->pos);
2684                    farthest_selected = node;
2685                }
2686            } else {
2687                if (NR::L2(node->pos - n->pos) < closest_dist) {
2688                    closest_dist = NR::L2(node->pos - n->pos);
2689                    closest_unselected = node;
2690                }
2691            }
2692         }
2693     }
2695     if (grow > 0) {
2696         if (closest_unselected) {
2697             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2698         }
2699     } else {
2700         if (farthest_selected) {
2701             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2702         }
2703     }
2707 /**
2708 \brief  Saves all nodes' and handles' current positions in their origin members
2709 */
2710 void
2711 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2713     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2714        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2715         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2716            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2717            n->origin = n->pos;
2718            n->p.origin = n->p.pos;
2719            n->n.origin = n->n.pos;
2720         }
2721     }
2722
2724 /**
2725 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2726 */
2727 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2729     if (!nodepath->selected) {
2730         return NULL;
2731     }
2733     GList *r = NULL;
2734     guint i = 0;
2735     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2736        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2737         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2738            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2739             i++;
2740             if (node->selected) {
2741                 r = g_list_append(r, GINT_TO_POINTER(i));
2742             }
2743         }
2744     }
2745     return r;
2748 /**
2749 \brief  Restores selection by selecting nodes whose positions are in the list
2750 */
2751 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2753     sp_nodepath_deselect(nodepath);
2755     guint i = 0;
2756     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2757        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2758         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2759            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2760             i++;
2761             if (g_list_find(r, GINT_TO_POINTER(i))) {
2762                 sp_nodepath_node_select(node, TRUE, TRUE);
2763             }
2764         }
2765     }
2769 /**
2770 \brief Adjusts handle according to node type and line code.
2771 */
2772 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2774     double len, otherlen, linelen;
2776     g_assert(node);
2778    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2779    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2781     /** \todo fixme: */
2782     if (me->other == NULL) return;
2783     if (other->other == NULL) return;
2785     /* I have line */
2787     NRPathcode mecode, ocode;
2788     if (which_adjust == 1) {
2789         mecode = (NRPathcode)me->other->code;
2790         ocode = (NRPathcode)node->code;
2791     } else {
2792         mecode = (NRPathcode)node->code;
2793         ocode = (NRPathcode)other->other->code;
2794     }
2796     if (mecode == NR_LINETO) return;
2798     /* I am curve */
2800     if (other->other == NULL) return;
2802     /* Other has line */
2804     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2806     NR::Point delta;
2807     if (ocode == NR_LINETO) {
2808         /* other is lineto, we are either smooth or symm */
2809        Inkscape::NodePath::Node *othernode = other->other;
2810         len = NR::L2(me->pos - node->pos);
2811         delta = node->pos - othernode->pos;
2812         linelen = NR::L2(delta);
2813         if (linelen < 1e-18) 
2814             return;
2815         me->pos = node->pos + (len / linelen)*delta;
2816         return;
2817     }
2819     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2821         me->pos = 2 * node->pos - other->pos;
2822         return;
2823     }
2825     /* We are smooth */
2827     len = NR::L2(me->pos - node->pos);
2828     delta = other->pos - node->pos;
2829     otherlen = NR::L2(delta);
2830     if (otherlen < 1e-18) return;
2832     me->pos = node->pos - (len / otherlen) * delta;
2835 /**
2836  \brief Adjusts both handles according to node type and line code
2837  */
2838 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2840     g_assert(node);
2842     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2844     /* we are either smooth or symm */
2846     if (node->p.other == NULL) return;
2848     if (node->n.other == NULL) return;
2850     if (node->code == NR_LINETO) {
2851         if (node->n.other->code == NR_LINETO) return;
2852         sp_node_adjust_handle(node, 1);
2853         return;
2854     }
2856     if (node->n.other->code == NR_LINETO) {
2857         if (node->code == NR_LINETO) return;
2858         sp_node_adjust_handle(node, -1);
2859         return;
2860     }
2862     /* both are curves */
2863     NR::Point const delta( node->n.pos - node->p.pos );
2865     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2866         node->p.pos = node->pos - delta / 2;
2867         node->n.pos = node->pos + delta / 2;
2868         return;
2869     }
2871     /* We are smooth */
2872     double plen = NR::L2(node->p.pos - node->pos);
2873     if (plen < 1e-18) return;
2874     double nlen = NR::L2(node->n.pos - node->pos);
2875     if (nlen < 1e-18) return;
2876     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2877     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2880 /**
2881  * Node event callback.
2882  */
2883 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2885     gboolean ret = FALSE;
2886     switch (event->type) {
2887         case GDK_ENTER_NOTIFY:
2888             Inkscape::NodePath::Path::active_node = n;
2889             break;
2890         case GDK_LEAVE_NOTIFY:
2891             Inkscape::NodePath::Path::active_node = NULL;
2892             break;
2893         case GDK_SCROLL:
2894             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
2895                 switch (event->scroll.direction) {
2896                     case GDK_SCROLL_UP:
2897                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2898                         break;
2899                     case GDK_SCROLL_DOWN:
2900                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2901                         break;
2902                     default:
2903                         break;
2904                 }
2905                 ret = TRUE;
2906             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
2907                 switch (event->scroll.direction) {
2908                     case GDK_SCROLL_UP:
2909                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2910                         break;
2911                     case GDK_SCROLL_DOWN:
2912                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2913                         break;
2914                     default:
2915                         break;
2916                 }
2917                 ret = TRUE;
2918             }
2919             break;
2920         case GDK_KEY_PRESS:
2921             switch (get_group0_keyval (&event->key)) {
2922                 case GDK_space:
2923                     if (event->key.state & GDK_BUTTON1_MASK) {
2924                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2925                         stamp_repr(nodepath);
2926                         ret = TRUE;
2927                     }
2928                     break;
2929                 case GDK_Page_Up:
2930                     if (event->key.state & GDK_CONTROL_MASK) {
2931                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2932                     } else {
2933                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2934                     }
2935                     break;
2936                 case GDK_Page_Down:
2937                     if (event->key.state & GDK_CONTROL_MASK) {
2938                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2939                     } else {
2940                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2941                     }
2942                     break;
2943                 default:
2944                     break;
2945             }
2946             break;
2947         default:
2948             break;
2949     }
2951     return ret;
2954 /**
2955  * Handle keypress on node; directly called.
2956  */
2957 gboolean node_key(GdkEvent *event)
2959     Inkscape::NodePath::Path *np;
2961     // there is no way to verify nodes so set active_node to nil when deleting!!
2962     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
2964     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2965         gint ret = FALSE;
2966         switch (get_group0_keyval (&event->key)) {
2967             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2968             case GDK_BackSpace:
2969                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
2970                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
2971                 sp_nodepath_update_repr(np, _("Delete node"));
2972                 Inkscape::NodePath::Path::active_node = NULL;
2973                 ret = TRUE;
2974                 break;
2975             case GDK_c:
2976                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
2977                 ret = TRUE;
2978                 break;
2979             case GDK_s:
2980                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
2981                 ret = TRUE;
2982                 break;
2983             case GDK_y:
2984                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
2985                 ret = TRUE;
2986                 break;
2987             case GDK_b:
2988                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
2989                 ret = TRUE;
2990                 break;
2991         }
2992         return ret;
2993     }
2994     return FALSE;
2997 /**
2998  * Mouseclick on node callback.
2999  */
3000 static void node_clicked(SPKnot *knot, guint state, gpointer data)
3002    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3004     if (state & GDK_CONTROL_MASK) {
3005         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3007         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3008             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3009                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3010             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3011                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3012             } else {
3013                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3014             }
3015             sp_nodepath_update_repr(nodepath, _("Change node type"));
3016             sp_nodepath_update_statusbar(nodepath);
3018         } else { //ctrl+alt+click: delete node
3019             GList *node_to_delete = NULL;
3020             node_to_delete = g_list_append(node_to_delete, n);
3021             sp_node_delete_preserve(node_to_delete);
3022         }
3024     } else {
3025         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3026     }
3029 /**
3030  * Mouse grabbed node callback.
3031  */
3032 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3034    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3036     if (!n->selected) {
3037         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3038     }
3040     n->is_dragging = true;
3041     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3043     sp_nodepath_remember_origins (n->subpath->nodepath);
3046 /**
3047  * Mouse ungrabbed node callback.
3048  */
3049 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
3051    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3053    n->dragging_out = NULL;
3054    n->is_dragging = false;
3055    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3057    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3060 /**
3061  * The point on a line, given by its angle, closest to the given point.
3062  * \param p  A point.
3063  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3064  * \param closest  Pointer to the point struct where the result is stored.
3065  * \todo FIXME: use dot product perhaps?
3066  */
3067 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3069     if (a == HUGE_VAL) { // vertical
3070         *closest = NR::Point(0, (*p)[NR::Y]);
3071     } else {
3072         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3073         (*closest)[NR::Y] = a * (*closest)[NR::X];
3074     }
3077 /**
3078  * Distance from the point to a line given by its angle.
3079  * \param p  A point.
3080  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3081  */
3082 static double point_line_distance(NR::Point *p, double a)
3084     NR::Point c;
3085     point_line_closest(p, a, &c);
3086     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]));
3089 /**
3090  * Callback for node "request" signal.
3091  * \todo fixme: This goes to "moved" event? (lauris)
3092  */
3093 static gboolean
3094 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3096     double yn, xn, yp, xp;
3097     double an, ap, na, pa;
3098     double d_an, d_ap, d_na, d_pa;
3099     gboolean collinear = FALSE;
3100     NR::Point c;
3101     NR::Point pr;
3103    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3105    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3106    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3108        NR::Point mouse = (*p);
3110        if (!n->dragging_out) {
3111            // This is the first drag-out event; find out which handle to drag out
3112            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3113            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3115            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3116                return FALSE;
3118            Inkscape::NodePath::NodeSide *opposite;
3119            if (appr_p > appr_n) { // closer to p
3120                n->dragging_out = &n->p;
3121                opposite = &n->n;
3122                n->code = NR_CURVETO;
3123            } else if (appr_p < appr_n) { // closer to n
3124                n->dragging_out = &n->n;
3125                opposite = &n->p;
3126                n->n.other->code = NR_CURVETO;
3127            } else { // p and n nodes are the same
3128                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3129                    n->dragging_out = &n->p;
3130                    opposite = &n->n;
3131                    n->code = NR_CURVETO;
3132                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3133                    n->dragging_out = &n->n;
3134                    opposite = &n->p;
3135                    n->n.other->code = NR_CURVETO;
3136                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3137                    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);
3138                    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);
3139                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3140                        n->dragging_out = &n->n;
3141                        opposite = &n->p;
3142                        n->n.other->code = NR_CURVETO;
3143                    } else { // closer to other's n handle
3144                        n->dragging_out = &n->p;
3145                        opposite = &n->n;
3146                        n->code = NR_CURVETO;
3147                    }
3148                }
3149            }
3151            // if there's another handle, make sure the one we drag out starts parallel to it
3152            if (opposite->pos != n->pos) {
3153                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3154            }
3156            // knots might not be created yet!
3157            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3158            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3159        }
3161        // pass this on to the handle-moved callback
3162        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3163        sp_node_update_handles(n);
3164        return TRUE;
3165    }
3167     if (state & GDK_CONTROL_MASK) { // constrained motion
3169         // calculate relative distances of handles
3170         // n handle:
3171         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3172         xn = n->n.pos[NR::X] - n->pos[NR::X];
3173         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3174         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3175             if (n->n.other) { // if there is the next point
3176                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3177                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3178                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3179             }
3180         }
3181         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3182         if (yn < 0) { xn = -xn; yn = -yn; }
3184         // p handle:
3185         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3186         xp = n->p.pos[NR::X] - n->pos[NR::X];
3187         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3188         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3189             if (n->p.other) {
3190                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3191                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3192                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3193             }
3194         }
3195         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3196         if (yp < 0) { xp = -xp; yp = -yp; }
3198         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3199             // sliding on handles, only if at least one of the handles is non-vertical
3200             // (otherwise it's the same as ctrl+drag anyway)
3202             // calculate angles of the handles
3203             if (xn == 0) {
3204                 if (yn == 0) { // no handle, consider it the continuation of the other one
3205                     an = 0;
3206                     collinear = TRUE;
3207                 }
3208                 else an = 0; // vertical; set the angle to horizontal
3209             } else an = yn/xn;
3211             if (xp == 0) {
3212                 if (yp == 0) { // no handle, consider it the continuation of the other one
3213                     ap = an;
3214                 }
3215                 else ap = 0; // vertical; set the angle to horizontal
3216             } else  ap = yp/xp;
3218             if (collinear) an = ap;
3220             // angles of the perpendiculars; HUGE_VAL means vertical
3221             if (an == 0) na = HUGE_VAL; else na = -1/an;
3222             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3224             // mouse point relative to the node's original pos
3225             pr = (*p) - n->origin;
3227             // distances to the four lines (two handles and two perpendiculars)
3228             d_an = point_line_distance(&pr, an);
3229             d_na = point_line_distance(&pr, na);
3230             d_ap = point_line_distance(&pr, ap);
3231             d_pa = point_line_distance(&pr, pa);
3233             // find out which line is the closest, save its closest point in c
3234             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3235                 point_line_closest(&pr, an, &c);
3236             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3237                 point_line_closest(&pr, ap, &c);
3238             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3239                 point_line_closest(&pr, na, &c);
3240             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3241                 point_line_closest(&pr, pa, &c);
3242             }
3244             // move the node to the closest point
3245             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3246                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3247                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3249         } else {  // constraining to hor/vert
3251             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3252                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3253             } else { // snap to vert
3254                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3255             }
3256         }
3257     } else { // move freely
3258         if (n->is_dragging) {
3259             if (state & GDK_MOD1_MASK) { // sculpt
3260                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3261             } else {
3262                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3263                                             (*p)[NR::X] - n->pos[NR::X],
3264                                             (*p)[NR::Y] - n->pos[NR::Y],
3265                                             (state & GDK_SHIFT_MASK) == 0);
3266             }
3267         }
3268     }
3270     n->subpath->nodepath->desktop->scroll_to_point(p);
3272     return TRUE;
3275 /**
3276  * Node handle clicked callback.
3277  */
3278 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3280    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3282     if (state & GDK_CONTROL_MASK) { // "delete" handle
3283         if (n->p.knot == knot) {
3284             n->p.pos = n->pos;
3285         } else if (n->n.knot == knot) {
3286             n->n.pos = n->pos;
3287         }
3288         sp_node_update_handles(n);
3289         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3290         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3291         sp_nodepath_update_statusbar(nodepath);
3293     } else { // just select or add to selection, depending in Shift
3294         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3295     }
3298 /**
3299  * Node handle grabbed callback.
3300  */
3301 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3303    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3305     if (!n->selected) {
3306         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3307     }
3309     // remember the origin point of the handle
3310     if (n->p.knot == knot) {
3311         n->p.origin_radial = n->p.pos - n->pos;
3312     } else if (n->n.knot == knot) {
3313         n->n.origin_radial = n->n.pos - n->pos;
3314     } else {
3315         g_assert_not_reached();
3316     }
3318     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3321 /**
3322  * Node handle ungrabbed callback.
3323  */
3324 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3326    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3328     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3329     if (n->p.knot == knot) {
3330         n->p.origin_radial.a = 0;
3331         sp_knot_set_position(knot, &n->p.pos, state);
3332     } else if (n->n.knot == knot) {
3333         n->n.origin_radial.a = 0;
3334         sp_knot_set_position(knot, &n->n.pos, state);
3335     } else {
3336         g_assert_not_reached();
3337     }
3339     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3342 /**
3343  * Node handle "request" signal callback.
3344  */
3345 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3347     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3349     Inkscape::NodePath::NodeSide *me, *opposite;
3350     gint which;
3351     if (n->p.knot == knot) {
3352         me = &n->p;
3353         opposite = &n->n;
3354         which = -1;
3355     } else if (n->n.knot == knot) {
3356         me = &n->n;
3357         opposite = &n->p;
3358         which = 1;
3359     } else {
3360         me = opposite = NULL;
3361         which = 0;
3362         g_assert_not_reached();
3363     }
3365     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3367     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3369     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3370         /* We are smooth node adjacent with line */
3371         NR::Point const delta = *p - n->pos;
3372         NR::Coord const len = NR::L2(delta);
3373         Inkscape::NodePath::Node *othernode = opposite->other;
3374         NR::Point const ndelta = n->pos - othernode->pos;
3375         NR::Coord const linelen = NR::L2(ndelta);
3376         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3377             NR::Coord const scal = dot(delta, ndelta) / linelen;
3378             (*p) = n->pos + (scal / linelen) * ndelta;
3379         }
3380         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3381     } else {
3382         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3383     }
3385     sp_node_adjust_handle(n, -which);
3387     return FALSE;
3390 /**
3391  * Node handle moved callback.
3392  */
3393 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3395    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3397    Inkscape::NodePath::NodeSide *me;
3398    Inkscape::NodePath::NodeSide *other;
3399     if (n->p.knot == knot) {
3400         me = &n->p;
3401         other = &n->n;
3402     } else if (n->n.knot == knot) {
3403         me = &n->n;
3404         other = &n->p;
3405     } else {
3406         me = NULL;
3407         other = NULL;
3408         g_assert_not_reached();
3409     }
3411     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3412     Radial rme(me->pos - n->pos);
3413     Radial rother(other->pos - n->pos);
3414     Radial rnew(*p - n->pos);
3416     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3417         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3418         /* 0 interpreted as "no snapping". */
3420         // The closest PI/snaps angle, starting from zero.
3421         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3422         if (me->origin_radial.a == HUGE_VAL) {
3423             // ortho doesn't exist: original handle was zero length.
3424             rnew.a = a_snapped;
3425         } else {
3426             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3427              * its opposite and perpendiculars). */
3428             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3430             // Snap to the closest.
3431             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3432                        ? a_snapped
3433                        : a_ortho );
3434         }
3435     }
3437     if (state & GDK_MOD1_MASK) {
3438         // lock handle length
3439         rnew.r = me->origin_radial.r;
3440     }
3442     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3443         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3444         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3445         rother.a += rnew.a - rme.a;
3446         other->pos = NR::Point(rother) + n->pos;
3447         if (other->knot) {
3448             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3449             sp_knot_moveto(other->knot, &other->pos);
3450         }
3451     }
3453     me->pos = NR::Point(rnew) + n->pos;
3454     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3456     // move knot, but without emitting the signal:
3457     // we cannot emit a "moved" signal because we're now processing it
3458     sp_knot_moveto(me->knot, &(me->pos));
3460     update_object(n->subpath->nodepath);
3462     /* status text */
3463     SPDesktop *desktop = n->subpath->nodepath->desktop;
3464     if (!desktop) return;
3465     SPEventContext *ec = desktop->event_context;
3466     if (!ec) return;
3467     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3468     if (!mc) return;
3470     double degrees = 180 / M_PI * rnew.a;
3471     if (degrees > 180) degrees -= 360;
3472     if (degrees < -180) degrees += 360;
3473     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3474         degrees = angle_to_compass (degrees);
3476     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3478     mc->setF(Inkscape::NORMAL_MESSAGE,
3479          _("<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);
3481     g_string_free(length, TRUE);
3484 /**
3485  * Node handle event callback.
3486  */
3487 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3489     gboolean ret = FALSE;
3490     switch (event->type) {
3491         case GDK_KEY_PRESS:
3492             switch (get_group0_keyval (&event->key)) {
3493                 case GDK_space:
3494                     if (event->key.state & GDK_BUTTON1_MASK) {
3495                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3496                         stamp_repr(nodepath);
3497                         ret = TRUE;
3498                     }
3499                     break;
3500                 default:
3501                     break;
3502             }
3503             break;
3504         case GDK_ENTER_NOTIFY:
3505             // we use an experimentally determined threshold that seems to work fine
3506             if (NR::L2(n->pos - knot->pos) < 0.75)
3507                 Inkscape::NodePath::Path::active_node = n;
3508             break;
3509         case GDK_LEAVE_NOTIFY:
3510             // we use an experimentally determined threshold that seems to work fine
3511             if (NR::L2(n->pos - knot->pos) < 0.75)
3512                 Inkscape::NodePath::Path::active_node = NULL;
3513             break;
3514         default:
3515             break;
3516     }
3518     return ret;
3521 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3522                                  Radial &rme, Radial &rother, gboolean const both)
3524     rme.a += angle;
3525     if ( both
3526          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3527          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3528     {
3529         rother.a += angle;
3530     }
3533 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3534                                         Radial &rme, Radial &rother, gboolean const both)
3536     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3538     gdouble r;
3539     if ( both
3540          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3541          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3542     {
3543         r = MAX(rme.r, rother.r);
3544     } else {
3545         r = rme.r;
3546     }
3548     gdouble const weird_angle = atan2(norm_angle, r);
3549 /* Bulia says norm_angle is just the visible distance that the
3550  * object's end must travel on the screen.  Left as 'angle' for want of
3551  * a better name.*/
3553     rme.a += weird_angle;
3554     if ( both
3555          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3556          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3557     {
3558         rother.a += weird_angle;
3559     }
3562 /**
3563  * Rotate one node.
3564  */
3565 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3567     Inkscape::NodePath::NodeSide *me, *other;
3568     bool both = false;
3570     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3571     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3573     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3574         me = &(n->p);
3575         other = &(n->n);
3576     } else if (!n->p.other) {
3577         me = &(n->n);
3578         other = &(n->p);
3579     } else {
3580         if (which > 0) { // right handle
3581             if (xn > xp) {
3582                 me = &(n->n);
3583                 other = &(n->p);
3584             } else {
3585                 me = &(n->p);
3586                 other = &(n->n);
3587             }
3588         } else if (which < 0){ // left handle
3589             if (xn <= xp) {
3590                 me = &(n->n);
3591                 other = &(n->p);
3592             } else {
3593                 me = &(n->p);
3594                 other = &(n->n);
3595             }
3596         } else { // both handles
3597             me = &(n->n);
3598             other = &(n->p);
3599             both = true;
3600         }
3601     }
3603     Radial rme(me->pos - n->pos);
3604     Radial rother(other->pos - n->pos);
3606     if (screen) {
3607         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3608     } else {
3609         node_rotate_one_internal (*n, angle, rme, rother, both);
3610     }
3612     me->pos = n->pos + NR::Point(rme);
3614     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3615         other->pos =  n->pos + NR::Point(rother);
3616     }
3618     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3619     // so here we just move all the knots without emitting move signals, for speed
3620     sp_node_update_handles(n, false);
3623 /**
3624  * Rotate selected nodes.
3625  */
3626 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3628     if (!nodepath || !nodepath->selected) return;
3630     if (g_list_length(nodepath->selected) == 1) {
3631        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3632         node_rotate_one (n, angle, which, screen);
3633     } else {
3634        // rotate as an object:
3636         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3637         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3638         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3639             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3640             box.expandTo (n->pos); // contain all selected nodes
3641         }
3643         gdouble rot;
3644         if (screen) {
3645             gdouble const zoom = nodepath->desktop->current_zoom();
3646             gdouble const zmove = angle / zoom;
3647             gdouble const r = NR::L2(box.max() - box.midpoint());
3648             rot = atan2(zmove, r);
3649         } else {
3650             rot = angle;
3651         }
3653         NR::Point rot_center;
3654         if (Inkscape::NodePath::Path::active_node == NULL)
3655             rot_center = box.midpoint();
3656         else
3657             rot_center = Inkscape::NodePath::Path::active_node->pos;
3659         NR::Matrix t =
3660             NR::Matrix (NR::translate(-rot_center)) *
3661             NR::Matrix (NR::rotate(rot)) *
3662             NR::Matrix (NR::translate(rot_center));
3664         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3665             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3666             n->pos *= t;
3667             n->n.pos *= t;
3668             n->p.pos *= t;
3669             sp_node_update_handles(n, false);
3670         }
3671     }
3673     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3676 /**
3677  * Scale one node.
3678  */
3679 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3681     bool both = false;
3682     Inkscape::NodePath::NodeSide *me, *other;
3684     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3685     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3687     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3688         me = &(n->p);
3689         other = &(n->n);
3690         n->code = NR_CURVETO;
3691     } else if (!n->p.other) {
3692         me = &(n->n);
3693         other = &(n->p);
3694         if (n->n.other)
3695             n->n.other->code = NR_CURVETO;
3696     } else {
3697         if (which > 0) { // right handle
3698             if (xn > xp) {
3699                 me = &(n->n);
3700                 other = &(n->p);
3701                 if (n->n.other)
3702                     n->n.other->code = NR_CURVETO;
3703             } else {
3704                 me = &(n->p);
3705                 other = &(n->n);
3706                 n->code = NR_CURVETO;
3707             }
3708         } else if (which < 0){ // left handle
3709             if (xn <= xp) {
3710                 me = &(n->n);
3711                 other = &(n->p);
3712                 if (n->n.other)
3713                     n->n.other->code = NR_CURVETO;
3714             } else {
3715                 me = &(n->p);
3716                 other = &(n->n);
3717                 n->code = NR_CURVETO;
3718             }
3719         } else { // both handles
3720             me = &(n->n);
3721             other = &(n->p);
3722             both = true;
3723             n->code = NR_CURVETO;
3724             if (n->n.other)
3725                 n->n.other->code = NR_CURVETO;
3726         }
3727     }
3729     Radial rme(me->pos - n->pos);
3730     Radial rother(other->pos - n->pos);
3732     rme.r += grow;
3733     if (rme.r < 0) rme.r = 0;
3734     if (rme.a == HUGE_VAL) {
3735         if (me->other) { // if direction is unknown, initialize it towards the next node
3736             Radial rme_next(me->other->pos - n->pos);
3737             rme.a = rme_next.a;
3738         } else { // if there's no next, initialize to 0
3739             rme.a = 0;
3740         }
3741     }
3742     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3743         rother.r += grow;
3744         if (rother.r < 0) rother.r = 0;
3745         if (rother.a == HUGE_VAL) {
3746             rother.a = rme.a + M_PI;
3747         }
3748     }
3750     me->pos = n->pos + NR::Point(rme);
3752     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3753         other->pos = n->pos + NR::Point(rother);
3754     }
3756     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3757     // so here we just move all the knots without emitting move signals, for speed
3758     sp_node_update_handles(n, false);
3761 /**
3762  * Scale selected nodes.
3763  */
3764 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3766     if (!nodepath || !nodepath->selected) return;
3768     if (g_list_length(nodepath->selected) == 1) {
3769         // scale handles of the single selected node
3770         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3771         node_scale_one (n, grow, which);
3772     } else {
3773         // scale nodes as an "object":
3775         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3776         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3777         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3778             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3779             box.expandTo (n->pos); // contain all selected nodes
3780         }
3782         double scale = (box.maxExtent() + grow)/box.maxExtent();
3784         NR::Point scale_center;
3785         if (Inkscape::NodePath::Path::active_node == NULL)
3786             scale_center = box.midpoint();
3787         else
3788             scale_center = Inkscape::NodePath::Path::active_node->pos;
3790         NR::Matrix t =
3791             NR::Matrix (NR::translate(-scale_center)) *
3792             NR::Matrix (NR::scale(scale, scale)) *
3793             NR::Matrix (NR::translate(scale_center));
3795         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3796             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3797             n->pos *= t;
3798             n->n.pos *= t;
3799             n->p.pos *= t;
3800             sp_node_update_handles(n, false);
3801         }
3802     }
3804     sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
3807 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3809     if (!nodepath) return;
3810     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3813 /**
3814  * Flip selected nodes horizontally/vertically.
3815  */
3816 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis, NR::Maybe<NR::Point> center)
3818     if (!nodepath || !nodepath->selected) return;
3820     if (g_list_length(nodepath->selected) == 1 && !center) {
3821         // flip handles of the single selected node
3822         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3823         double temp = n->p.pos[axis];
3824         n->p.pos[axis] = n->n.pos[axis];
3825         n->n.pos[axis] = temp;
3826         sp_node_update_handles(n, false);
3827     } else {
3828         // scale nodes as an "object":
3830         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3831         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3832         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3833             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3834             box.expandTo (n->pos); // contain all selected nodes
3835         }
3837         if (!center) {
3838             center = box.midpoint();
3839         }
3840         NR::Matrix t =
3841             NR::Matrix (NR::translate(- *center)) *
3842             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3843             NR::Matrix (NR::translate(*center));
3845         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3846             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3847             n->pos *= t;
3848             n->n.pos *= t;
3849             n->p.pos *= t;
3850             sp_node_update_handles(n, false);
3851         }
3852     }
3854     sp_nodepath_update_repr(nodepath, _("Flip nodes"));
3857 //-----------------------------------------------
3858 /**
3859  * Return new subpath under given nodepath.
3860  */
3861 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3863     g_assert(nodepath);
3864     g_assert(nodepath->desktop);
3866    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3868     s->nodepath = nodepath;
3869     s->closed = FALSE;
3870     s->nodes = NULL;
3871     s->first = NULL;
3872     s->last = NULL;
3874     // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
3875     // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
3876     nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
3878     return s;
3881 /**
3882  * Destroy nodes in subpath, then subpath itself.
3883  */
3884 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3886     g_assert(subpath);
3887     g_assert(subpath->nodepath);
3888     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3890     while (subpath->nodes) {
3891         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3892     }
3894     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3896     g_free(subpath);
3899 /**
3900  * Link head to tail in subpath.
3901  */
3902 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3904     g_assert(!sp->closed);
3905     g_assert(sp->last != sp->first);
3906     g_assert(sp->first->code == NR_MOVETO);
3908     sp->closed = TRUE;
3910     //Link the head to the tail
3911     sp->first->p.other = sp->last;
3912     sp->last->n.other  = sp->first;
3913     sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
3914     sp->first          = sp->last;
3916     //Remove the extra end node
3917     sp_nodepath_node_destroy(sp->last->n.other);
3920 /**
3921  * Open closed (loopy) subpath at node.
3922  */
3923 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3925     g_assert(sp->closed);
3926     g_assert(n->subpath == sp);
3927     g_assert(sp->first == sp->last);
3929     /* We create new startpoint, current node will become last one */
3931    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3932                                                 &n->pos, &n->pos, &n->n.pos);
3935     sp->closed        = FALSE;
3937     //Unlink to make a head and tail
3938     sp->first         = new_path;
3939     sp->last          = n;
3940     n->n.other        = NULL;
3941     new_path->p.other = NULL;
3944 /**
3945  * Returns area in triangle given by points; may be negative.
3946  */
3947 inline double
3948 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3950     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]);
3953 /**
3954  * Return new node in subpath with given properties.
3955  * \param pos Position of node.
3956  * \param ppos Handle position in previous direction
3957  * \param npos Handle position in previous direction
3958  */
3959 Inkscape::NodePath::Node *
3960 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)
3962     g_assert(sp);
3963     g_assert(sp->nodepath);
3964     g_assert(sp->nodepath->desktop);
3966     if (nodechunk == NULL)
3967         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3969     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3971     n->subpath  = sp;
3973     if (type != Inkscape::NodePath::NODE_NONE) {
3974         // use the type from sodipodi:nodetypes
3975         n->type = type;
3976     } else {
3977         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3978             // points are (almost) collinear
3979             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3980                 // endnode, or a node with a retracted handle
3981                 n->type = Inkscape::NodePath::NODE_CUSP;
3982             } else {
3983                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3984             }
3985         } else {
3986             n->type = Inkscape::NodePath::NODE_CUSP;
3987         }
3988     }
3990     n->code     = code;
3991     n->selected = FALSE;
3992     n->pos      = *pos;
3993     n->p.pos    = *ppos;
3994     n->n.pos    = *npos;
3996     n->dragging_out = NULL;
3998     Inkscape::NodePath::Node *prev;
3999     if (next) {
4000         //g_assert(g_list_find(sp->nodes, next));
4001         prev = next->p.other;
4002     } else {
4003         prev = sp->last;
4004     }
4006     if (prev)
4007         prev->n.other = n;
4008     else
4009         sp->first = n;
4011     if (next)
4012         next->p.other = n;
4013     else
4014         sp->last = n;
4016     n->p.other = prev;
4017     n->n.other = next;
4019     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"));
4020     sp_knot_set_position(n->knot, pos, 0);
4022     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
4023     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
4024     n->knot->setAnchor (GTK_ANCHOR_CENTER);
4025     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
4026     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
4027     sp_knot_update_ctrl(n->knot);
4029     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
4030     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
4031     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
4032     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
4033     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
4034     sp_knot_show(n->knot);
4036     // We only create handle knots and lines on demand
4037     n->p.knot = NULL;
4038     n->p.line = NULL;
4039     n->n.knot = NULL;
4040     n->n.line = NULL;
4042     sp->nodes = g_list_prepend(sp->nodes, n);
4044     return n;
4047 /**
4048  * Destroy node and its knots, link neighbors in subpath.
4049  */
4050 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
4052     g_assert(node);
4053     g_assert(node->subpath);
4054     g_assert(SP_IS_KNOT(node->knot));
4056    Inkscape::NodePath::SubPath *sp = node->subpath;
4058     if (node->selected) { // first, deselect
4059         g_assert(g_list_find(node->subpath->nodepath->selected, node));
4060         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
4061     }
4063     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
4065     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
4066     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
4067     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
4068     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
4069     g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
4070     g_object_unref(G_OBJECT(node->knot));
4072     if (node->p.knot) {
4073         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4074         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4075         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4076         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4077         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4078         g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4079         g_object_unref(G_OBJECT(node->p.knot));
4080         node->p.knot = NULL;
4081     }
4083     if (node->n.knot) {
4084         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
4085         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
4086         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
4087         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
4088         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
4089         g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
4090         g_object_unref(G_OBJECT(node->n.knot));
4091         node->n.knot = NULL;
4092     }
4094     if (node->p.line)
4095         gtk_object_destroy(GTK_OBJECT(node->p.line));
4096     if (node->n.line)
4097         gtk_object_destroy(GTK_OBJECT(node->n.line));
4099     if (sp->nodes) { // there are others nodes on the subpath
4100         if (sp->closed) {
4101             if (sp->first == node) {
4102                 g_assert(sp->last == node);
4103                 sp->first = node->n.other;
4104                 sp->last = sp->first;
4105             }
4106             node->p.other->n.other = node->n.other;
4107             node->n.other->p.other = node->p.other;
4108         } else {
4109             if (sp->first == node) {
4110                 sp->first = node->n.other;
4111                 sp->first->code = NR_MOVETO;
4112             }
4113             if (sp->last == node) sp->last = node->p.other;
4114             if (node->p.other) node->p.other->n.other = node->n.other;
4115             if (node->n.other) node->n.other->p.other = node->p.other;
4116         }
4117     } else { // this was the last node on subpath
4118         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
4119     }
4121     g_mem_chunk_free(nodechunk, node);
4124 /**
4125  * Returns one of the node's two sides.
4126  * \param which Indicates which side.
4127  * \return Pointer to previous node side if which==-1, next if which==1.
4128  */
4129 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
4131     g_assert(node);
4133     switch (which) {
4134         case -1:
4135             return &node->p;
4136         case 1:
4137             return &node->n;
4138         default:
4139             break;
4140     }
4142     g_assert_not_reached();
4144     return NULL;
4147 /**
4148  * Return the other side of the node, given one of its sides.
4149  */
4150 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
4152     g_assert(node);
4154     if (me == &node->p) return &node->n;
4155     if (me == &node->n) return &node->p;
4157     g_assert_not_reached();
4159     return NULL;
4162 /**
4163  * Return NRPathcode on the given side of the node.
4164  */
4165 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
4167     g_assert(node);
4169     if (me == &node->p) {
4170         if (node->p.other) return (NRPathcode)node->code;
4171         return NR_MOVETO;
4172     }
4174     if (me == &node->n) {
4175         if (node->n.other) return (NRPathcode)node->n.other->code;
4176         return NR_MOVETO;
4177     }
4179     g_assert_not_reached();
4181     return NR_END;
4184 /**
4185  * Return node with the given index
4186  */
4187 Inkscape::NodePath::Node *
4188 sp_nodepath_get_node_by_index(int index)
4190     Inkscape::NodePath::Node *e = NULL;
4192     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
4193     if (!nodepath) {
4194         return e;
4195     }
4197     //find segment
4198     for (GList *l = nodepath->subpaths; l ; l=l->next) {
4200         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
4201         int n = g_list_length(sp->nodes);
4202         if (sp->closed) {
4203             n++;
4204         }
4206         //if the piece belongs to this subpath grab it
4207         //otherwise move onto the next subpath
4208         if (index < n) {
4209             e = sp->first;
4210             for (int i = 0; i < index; ++i) {
4211                 e = e->n.other;
4212             }
4213             break;
4214         } else {
4215             if (sp->closed) {
4216                 index -= (n+1);
4217             } else {
4218                 index -= n;
4219             }
4220         }
4221     }
4223     return e;
4226 /**
4227  * Returns plain text meaning of node type.
4228  */
4229 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
4231     unsigned retracted = 0;
4232     bool endnode = false;
4234     for (int which = -1; which <= 1; which += 2) {
4235         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
4236         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
4237             retracted ++;
4238         if (!side->other)
4239             endnode = true;
4240     }
4242     if (retracted == 0) {
4243         if (endnode) {
4244                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
4245                 return _("end node");
4246         } else {
4247             switch (node->type) {
4248                 case Inkscape::NodePath::NODE_CUSP:
4249                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
4250                     return _("cusp");
4251                 case Inkscape::NodePath::NODE_SMOOTH:
4252                     // TRANSLATORS: "smooth" is an adjective here
4253                     return _("smooth");
4254                 case Inkscape::NodePath::NODE_SYMM:
4255                     return _("symmetric");
4256             }
4257         }
4258     } else if (retracted == 1) {
4259         if (endnode) {
4260             // TRANSLATORS: "end" is an adjective here (NOT a verb)
4261             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
4262         } else {
4263             return _("one handle retracted (drag with <b>Shift</b> to extend)");
4264         }
4265     } else {
4266         return _("both handles retracted (drag with <b>Shift</b> to extend)");
4267     }
4269     return NULL;
4272 /**
4273  * Handles content of statusbar as long as node tool is active.
4274  */
4275 void
4276 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
4278     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");
4279     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
4281     gint total_nodes = sp_nodepath_get_node_count(nodepath);
4282     gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
4283     gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
4284     gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
4286     SPDesktop *desktop = NULL;
4287     if (nodepath) {
4288         desktop = nodepath->desktop;
4289     } else {
4290         desktop = SP_ACTIVE_DESKTOP;
4291     }
4293     SPEventContext *ec = desktop->event_context;
4294     if (!ec) return;
4295     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
4296     if (!mc) return;
4298     if (selected_nodes == 0) {
4299         Inkscape::Selection *sel = desktop->selection;
4300         if (!sel || sel->isEmpty()) {
4301             mc->setF(Inkscape::NORMAL_MESSAGE,
4302                      _("Select a single object to edit its nodes or handles."));
4303         } else {
4304             if (nodepath) {
4305             mc->setF(Inkscape::NORMAL_MESSAGE,
4306                      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.",
4307                               "<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.",
4308                               total_nodes),
4309                      total_nodes);
4310             } else {
4311                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
4312                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
4313                 } else {
4314                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
4315                 }
4316             }
4317         }
4318     } else if (nodepath && selected_nodes == 1) {
4319         mc->setF(Inkscape::NORMAL_MESSAGE,
4320                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
4321                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
4322                           total_nodes),
4323                  selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
4324     } else {
4325         if (selected_subpaths > 1) {
4326             mc->setF(Inkscape::NORMAL_MESSAGE,
4327                      ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4328                               "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
4329                               total_nodes),
4330                      selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
4331         } else {
4332             mc->setF(Inkscape::NORMAL_MESSAGE,
4333                      ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
4334                               "<b>%i</b> of <b>%i</b> nodes selected. %s.",
4335                               total_nodes),
4336                      selected_nodes, total_nodes, when_selected);
4337         }
4338     }
4342 /*
4343   Local Variables:
4344   mode:c++
4345   c-file-style:"stroustrup"
4346   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
4347   indent-tabs-mode:nil
4348   fill-column:99
4349   End:
4350 */
4351 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :