Code

Switched pen and pencil toobars to stock GTK+ toolbars
[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(gdouble dx, gdouble dy)
1233     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1234     if (!nodepath) return;
1236     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
1238     if (dx == 0) {
1239         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1240     } else if (dy == 0) {
1241         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1242     } else {
1243         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1244     }
1247 /**
1248  * Move node selection off screen and commit the change.
1249  */
1250 void
1251 sp_node_selected_move_screen(gdouble dx, gdouble dy)
1253     // borrowed from sp_selection_move_screen in selection-chemistry.c
1254     // we find out the current zoom factor and divide deltas by it
1255     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1257     gdouble zoom = desktop->current_zoom();
1258     gdouble zdx = dx / zoom;
1259     gdouble zdy = dy / zoom;
1261     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1262     if (!nodepath) return;
1264     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
1266     if (dx == 0) {
1267         sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
1268     } else if (dy == 0) {
1269         sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
1270     } else {
1271         sp_nodepath_update_repr(nodepath, _("Move nodes"));
1272     }
1275 /** If they don't yet exist, creates knot and line for the given side of the node */
1276 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1278     if (!side->knot) {
1279         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"));
1281         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1282         side->knot->setSize (7);
1283         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1284         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1285         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1286         sp_knot_update_ctrl(side->knot);
1288         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
1289         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
1290         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
1291         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
1292         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
1293         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
1294     }
1296     if (!side->line) {
1297         side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
1298                                         SP_TYPE_CTRLLINE, NULL);
1299     }
1302 /**
1303  * Ensure the given handle of the node is visible/invisible, update its screen position
1304  */
1305 static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
1307     g_assert(node != NULL);
1309    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1310     NRPathcode code = sp_node_path_code_from_side(node, side);
1312     show_handle = show_handle && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1314     if (show_handle) {
1315         if (!side->knot) { // No handle knot at all
1316             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1317             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1318             side->knot->pos = side->pos;
1319             if (side->knot->item) 
1320                 SP_CTRL(side->knot->item)->moveto(side->pos);
1321             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1322             sp_knot_show(side->knot);
1323         } else {
1324             if (side->knot->pos != side->pos) { // only if it's really moved
1325                 if (fire_move_signals) {
1326                     sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1327                 } else {
1328                     sp_knot_moveto(side->knot, &side->pos);
1329                     sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1330                 }
1331             }
1332             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1333                 sp_knot_show(side->knot);
1334             }
1335         }
1336         sp_canvas_item_show(side->line);
1337     } else {
1338         if (side->knot) {
1339             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1340                 sp_knot_hide(side->knot);
1341             }
1342         }
1343         if (side->line) {
1344             sp_canvas_item_hide(side->line);
1345         }
1346     }
1349 /**
1350  * Ensure the node itself is visible, its handles and those of the neighbours of the node are
1351  * visible if selected, update their screen positions. If fire_move_signals, move the node and its
1352  * handles so that the corresponding signals are fired, callbacks are activated, and curve is
1353  * updated; otherwise, just move the knots silently (used in batch moves).
1354  */
1355 static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
1357     g_assert(node != NULL);
1359     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1360         sp_knot_show(node->knot);
1361     }
1363     if (node->knot->pos != node->pos) { // visible knot is in a different position, need to update
1364         if (fire_move_signals)
1365             sp_knot_set_position(node->knot, &node->pos, 0);
1366         else 
1367             sp_knot_moveto(node->knot, &node->pos);
1368     }
1370     gboolean show_handles = node->selected;
1371     if (node->p.other != NULL) {
1372         if (node->p.other->selected) show_handles = TRUE;
1373     }
1374     if (node->n.other != NULL) {
1375         if (node->n.other->selected) show_handles = TRUE;
1376     }
1378     if (node->subpath->nodepath->show_handles == false)
1379         show_handles = FALSE;
1381     sp_node_update_handle(node, -1, show_handles, fire_move_signals);
1382     sp_node_update_handle(node, 1, show_handles, fire_move_signals);
1385 /**
1386  * Call sp_node_update_handles() for all nodes on subpath.
1387  */
1388 static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
1390     g_assert(subpath != NULL);
1392     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1393         sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
1394     }
1397 /**
1398  * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
1399  */
1400 static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
1402     g_assert(nodepath != NULL);
1404     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1405         sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
1406     }
1409 void
1410 sp_nodepath_show_handles(bool show)
1412     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1413     if (nodepath == NULL) return;
1415     nodepath->show_handles = show;
1416     sp_nodepath_update_handles(nodepath);
1419 /**
1420  * Adds all selected nodes in nodepath to list.
1421  */
1422 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1424     StlConv<Node *>::list(l, selected);
1425 /// \todo this adds a copying, rework when the selection becomes a stl list
1428 /**
1429  * Align selected nodes on the specified axis.
1430  */
1431 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1433     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1434         return;
1435     }
1437     if ( !nodepath->selected->next ) { // only one node selected
1438         return;
1439     }
1440    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1441     NR::Point dest(pNode->pos);
1442     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1443         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1444         if (pNode) {
1445             dest[axis] = pNode->pos[axis];
1446             sp_node_moveto(pNode, dest);
1447         }
1448     }
1450     sp_nodepath_update_repr(nodepath, _("Align nodes"));
1453 /// Helper struct.
1454 struct NodeSort
1456    Inkscape::NodePath::Node *_node;
1457     NR::Coord _coord;
1458     /// \todo use vectorof pointers instead of calling copy ctor
1459     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1460         _node(node), _coord(node->pos[axis])
1461     {}
1463 };
1465 static bool operator<(NodeSort const &a, NodeSort const &b)
1467     return (a._coord < b._coord);
1470 /**
1471  * Distribute selected nodes on the specified axis.
1472  */
1473 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1475     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1476         return;
1477     }
1479     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1480         return;
1481     }
1483    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1484     std::vector<NodeSort> sorted;
1485     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1486         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1487         if (pNode) {
1488             NodeSort n(pNode, axis);
1489             sorted.push_back(n);
1490             //dest[axis] = pNode->pos[axis];
1491             //sp_node_moveto(pNode, dest);
1492         }
1493     }
1494     std::sort(sorted.begin(), sorted.end());
1495     unsigned int len = sorted.size();
1496     //overall bboxes span
1497     float dist = (sorted.back()._coord -
1498                   sorted.front()._coord);
1499     //new distance between each bbox
1500     float step = (dist) / (len - 1);
1501     float pos = sorted.front()._coord;
1502     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1503           it < sorted.end();
1504           it ++ )
1505     {
1506         NR::Point dest((*it)._node->pos);
1507         dest[axis] = pos;
1508         sp_node_moveto((*it)._node, dest);
1509         pos += step;
1510     }
1512     sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
1516 /**
1517  * Call sp_nodepath_line_add_node() for all selected segments.
1518  */
1519 void
1520 sp_node_selected_add_node(void)
1522     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1523     if (!nodepath) {
1524         return;
1525     }
1527     GList *nl = NULL;
1529     int n_added = 0;
1531     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1532        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1533         g_assert(t->selected);
1534         if (t->p.other && t->p.other->selected) {
1535             nl = g_list_prepend(nl, t);
1536         }
1537     }
1539     while (nl) {
1540        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1541        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1542        sp_nodepath_node_select(n, TRUE, FALSE);
1543        n_added ++;
1544        nl = g_list_remove(nl, t);
1545     }
1547     /** \todo fixme: adjust ? */
1548     sp_nodepath_update_handles(nodepath);
1550     if (n_added > 1) {
1551         sp_nodepath_update_repr(nodepath, _("Add nodes"));
1552     } else if (n_added > 0) {
1553         sp_nodepath_update_repr(nodepath, _("Add node"));
1554     }
1556     sp_nodepath_update_statusbar(nodepath);
1559 /**
1560  * Select segment nearest to point
1561  */
1562 void
1563 sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p, bool toggle)
1565     if (!nodepath) {
1566         return;
1567     }
1569     sp_nodepath_ensure_livarot_path(nodepath);
1570     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1571     if (!maybe_position) {
1572         return;
1573     }
1574     Path::cut_position position = *maybe_position;
1576     //find segment to segment
1577     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1579     //fixme: this can return NULL, so check before proceeding.
1580     g_return_if_fail(e != NULL);
1581     
1582     gboolean force = FALSE;
1583     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1584         force = TRUE;
1585     }
1586     sp_nodepath_node_select(e, (gboolean) toggle, force);
1587     if (e->p.other)
1588         sp_nodepath_node_select(e->p.other, TRUE, force);
1590     sp_nodepath_update_handles(nodepath);
1592     sp_nodepath_update_statusbar(nodepath);
1595 /**
1596  * Add a node nearest to point
1597  */
1598 void
1599 sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, NR::Point p)
1601     if (!nodepath) {
1602         return;
1603     }
1605     sp_nodepath_ensure_livarot_path(nodepath);
1606     NR::Maybe<Path::cut_position> maybe_position = get_nearest_position_on_Path(nodepath->livarot_path, p);
1607     if (!maybe_position) {
1608         return;
1609     }
1610     Path::cut_position position = *maybe_position;
1612     //find segment to split
1613     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1615     //don't know why but t seems to flip for lines
1616     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1617         position.t = 1.0 - position.t;
1618     }
1619     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1620     sp_nodepath_node_select(n, FALSE, TRUE);
1622     /* fixme: adjust ? */
1623     sp_nodepath_update_handles(nodepath);
1625     sp_nodepath_update_repr(nodepath, _("Add node"));
1627     sp_nodepath_update_statusbar(nodepath);
1630 /*
1631  * Adjusts a segment so that t moves by a certain delta for dragging
1632  * converts lines to curves
1633  *
1634  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1635  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1636  */
1637 void
1638 sp_nodepath_curve_drag(int node, double t, NR::Point delta)
1640     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(node);
1642     //fixme: e and e->p can be NULL, so check for those before proceeding
1643     g_return_if_fail(e != NULL);
1644     g_return_if_fail(&e->p != NULL);
1645     
1646     /* feel good is an arbitrary parameter that distributes the delta between handles
1647      * if t of the drag point is less than 1/6 distance form the endpoint only
1648      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1649      */
1650     double feel_good;
1651     if (t <= 1.0 / 6.0)
1652         feel_good = 0;
1653     else if (t <= 0.5)
1654         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1655     else if (t <= 5.0 / 6.0)
1656         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1657     else
1658         feel_good = 1;
1660     //if we're dragging a line convert it to a curve
1661     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1662         sp_nodepath_set_line_type(e, NR_CURVETO);
1663     }
1665     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1666     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1667     e->p.other->n.pos += offsetcoord0;
1668     e->p.pos += offsetcoord1;
1670     // adjust handles of adjacent nodes where necessary
1671     sp_node_adjust_handle(e,1);
1672     sp_node_adjust_handle(e->p.other,-1);
1674     sp_nodepath_update_handles(e->subpath->nodepath);
1676     update_object(e->subpath->nodepath);
1678     sp_nodepath_update_statusbar(e->subpath->nodepath);
1682 /**
1683  * Call sp_nodepath_break() for all selected segments.
1684  */
1685 void sp_node_selected_break()
1687     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1688     if (!nodepath) return;
1690     GList *temp = NULL;
1691     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1692        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1693        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1694         if (nn == NULL) continue; // no break, no new node
1695         temp = g_list_prepend(temp, nn);
1696     }
1698     if (temp) {
1699         sp_nodepath_deselect(nodepath);
1700     }
1701     for (GList *l = temp; l != NULL; l = l->next) {
1702         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1703     }
1705     sp_nodepath_update_handles(nodepath);
1707     sp_nodepath_update_repr(nodepath, _("Break path"));
1710 /**
1711  * Duplicate the selected node(s).
1712  */
1713 void sp_node_selected_duplicate()
1715     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1716     if (!nodepath) {
1717         return;
1718     }
1720     GList *temp = NULL;
1721     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1722        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1723        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1724         if (nn == NULL) continue; // could not duplicate
1725         temp = g_list_prepend(temp, nn);
1726     }
1728     if (temp) {
1729         sp_nodepath_deselect(nodepath);
1730     }
1731     for (GList *l = temp; l != NULL; l = l->next) {
1732         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1733     }
1735     sp_nodepath_update_handles(nodepath);
1737     sp_nodepath_update_repr(nodepath, _("Duplicate node"));
1740 /**
1741  *  Join two nodes by merging them into one.
1742  */
1743 void sp_node_selected_join()
1745     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1746     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1748     if (g_list_length(nodepath->selected) != 2) {
1749         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1750         return;
1751     }
1753    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1754    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1756     g_assert(a != b);
1757     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1758         // someone tried to join an orphan node (i.e. a single-node subpath).
1759         // this is not worth an error message, just fail silently.
1760         return;
1761     }
1763     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1764         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1765         return;
1766     }
1768     /* a and b are endpoints */
1770     NR::Point c;
1771     if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
1772         c = a->pos;
1773     } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
1774         c = b->pos;
1775     } else {
1776         c = (a->pos + b->pos) / 2;
1777     }
1779     if (a->subpath == b->subpath) {
1780        Inkscape::NodePath::SubPath *sp = a->subpath;
1781         sp_nodepath_subpath_close(sp);
1782         sp_node_moveto (sp->first, c);
1784         sp_nodepath_update_handles(sp->nodepath);
1785         sp_nodepath_update_repr(nodepath, _("Close subpath"));
1786         return;
1787     }
1789     /* a and b are separate subpaths */
1790    Inkscape::NodePath::SubPath *sa = a->subpath;
1791    Inkscape::NodePath::SubPath *sb = b->subpath;
1792     NR::Point p;
1793    Inkscape::NodePath::Node *n;
1794     NRPathcode code;
1795     if (a == sa->first) {
1796         p = sa->first->n.pos;
1797         code = (NRPathcode)sa->first->n.other->code;
1798        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1799         n = sa->last;
1800         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1801         n = n->p.other;
1802         while (n) {
1803             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1804             n = n->p.other;
1805             if (n == sa->first) n = NULL;
1806         }
1807         sp_nodepath_subpath_destroy(sa);
1808         sa = t;
1809     } else if (a == sa->last) {
1810         p = sa->last->p.pos;
1811         code = (NRPathcode)sa->last->code;
1812         sp_nodepath_node_destroy(sa->last);
1813     } else {
1814         code = NR_END;
1815         g_assert_not_reached();
1816     }
1818     if (b == sb->first) {
1819         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1820         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1821             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1822         }
1823     } else if (b == sb->last) {
1824         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1825         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1826             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1827         }
1828     } else {
1829         g_assert_not_reached();
1830     }
1831     /* and now destroy sb */
1833     sp_nodepath_subpath_destroy(sb);
1835     sp_nodepath_update_handles(sa->nodepath);
1837     sp_nodepath_update_repr(nodepath, _("Join nodes"));
1839     sp_nodepath_update_statusbar(nodepath);
1842 /**
1843  *  Join two nodes by adding a segment between them.
1844  */
1845 void sp_node_selected_join_segment()
1847     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1848     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1850     if (g_list_length(nodepath->selected) != 2) {
1851         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1852         return;
1853     }
1855    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1856    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1858     g_assert(a != b);
1859     if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) { 
1860         // someone tried to join an orphan node (i.e. a single-node subpath).
1861         // this is not worth an error message, just fail silently.
1862         return;
1863     }
1865     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1866         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1867         return;
1868     }
1870     if (a->subpath == b->subpath) {
1871        Inkscape::NodePath::SubPath *sp = a->subpath;
1873         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1874         sp->closed = TRUE;
1876         sp->first->p.other = sp->last;
1877         sp->last->n.other  = sp->first;
1879         sp_node_handle_mirror_p_to_n(sp->last);
1880         sp_node_handle_mirror_n_to_p(sp->first);
1882         sp->first->code = sp->last->code;
1883         sp->first       = sp->last;
1885         sp_nodepath_update_handles(sp->nodepath);
1887         sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
1889         return;
1890     }
1892     /* a and b are separate subpaths */
1893    Inkscape::NodePath::SubPath *sa = a->subpath;
1894    Inkscape::NodePath::SubPath *sb = b->subpath;
1896    Inkscape::NodePath::Node *n;
1897     NR::Point p;
1898     NRPathcode code;
1899     if (a == sa->first) {
1900         code = (NRPathcode) sa->first->n.other->code;
1901        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1902         n = sa->last;
1903         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1904         for (n = n->p.other; n != NULL; n = n->p.other) {
1905             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1906         }
1907         sp_nodepath_subpath_destroy(sa);
1908         sa = t;
1909     } else if (a == sa->last) {
1910         code = (NRPathcode)sa->last->code;
1911     } else {
1912         code = NR_END;
1913         g_assert_not_reached();
1914     }
1916     if (b == sb->first) {
1917         n = sb->first;
1918         sp_node_handle_mirror_p_to_n(sa->last);
1919         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1920         sp_node_handle_mirror_n_to_p(sa->last);
1921         for (n = n->n.other; n != NULL; n = n->n.other) {
1922             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1923         }
1924     } else if (b == sb->last) {
1925         n = sb->last;
1926         sp_node_handle_mirror_p_to_n(sa->last);
1927         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1928         sp_node_handle_mirror_n_to_p(sa->last);
1929         for (n = n->p.other; n != NULL; n = n->p.other) {
1930             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1931         }
1932     } else {
1933         g_assert_not_reached();
1934     }
1935     /* and now destroy sb */
1937     sp_nodepath_subpath_destroy(sb);
1939     sp_nodepath_update_handles(sa->nodepath);
1941     sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
1944 /**
1945  * Delete one or more selected nodes and preserve the shape of the path as much as possible.
1946  */
1947 void sp_node_delete_preserve(GList *nodes_to_delete)
1949     GSList *nodepaths = NULL;
1950     
1951     while (nodes_to_delete) {
1952         Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
1953         Inkscape::NodePath::SubPath *sp = node->subpath;
1954         Inkscape::NodePath::Path *nodepath = sp->nodepath;
1955         Inkscape::NodePath::Node *sample_cursor = NULL;
1956         Inkscape::NodePath::Node *sample_end = NULL;
1957         Inkscape::NodePath::Node *delete_cursor = node;
1958         bool just_delete = false;
1959         
1960         //find the start of this contiguous selection
1961         //move left to the first node that is not selected
1962         //or the start of the non-closed path
1963         for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
1964             delete_cursor = curr;
1965         }
1967         //just delete at the beginning of an open path
1968         if (!delete_cursor->p.other) {
1969             sample_cursor = delete_cursor;
1970             just_delete = true;
1971         } else {
1972             sample_cursor = delete_cursor->p.other;
1973         }
1974         
1975         //calculate points for each segment
1976         int rate = 5;
1977         float period = 1.0 / rate;
1978         std::vector<NR::Point> data;
1979         if (!just_delete) {
1980             data.push_back(sample_cursor->pos);
1981             for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
1982                 //just delete at the end of an open path
1983                 if (!sp->closed && curr == sp->last) {
1984                     just_delete = true;
1985                     break;
1986                 }
1987                 
1988                 //sample points on the contiguous selected segment
1989                 NR::Point *bez;
1990                 bez = new NR::Point [4];
1991                 bez[0] = curr->pos;
1992                 bez[1] = curr->n.pos;
1993                 bez[2] = curr->n.other->p.pos;
1994                 bez[3] = curr->n.other->pos;
1995                 for (int i=1; i<rate; i++) {
1996                     gdouble t = i * period;
1997                     NR::Point p = bezier_pt(3, bez, t);
1998                     data.push_back(p);
1999                 }
2000                 data.push_back(curr->n.other->pos);
2002                 sample_end = curr->n.other;
2003                 //break if we've come full circle or hit the end of the selection
2004                 if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
2005                     break;
2006                 }
2007             }
2008         }
2010         if (!just_delete) {
2011             //calculate the best fitting single segment and adjust the endpoints
2012             NR::Point *adata;
2013             adata = new NR::Point [data.size()];
2014             copy(data.begin(), data.end(), adata);
2015             
2016             NR::Point *bez;
2017             bez = new NR::Point [4];
2018             //would decreasing error create a better fitting approximation?
2019             gdouble error = 1.0;
2020             gint ret;
2021             ret = sp_bezier_fit_cubic (bez, adata, data.size(), error);
2023             //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
2024             //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
2025             //the resulting nodes behave as expected.
2026             sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
2027             sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
2028             
2029             //adjust endpoints
2030             sample_cursor->n.pos = bez[1];
2031             sample_end->p.pos = bez[2];
2032         }
2033        
2034         //destroy this contiguous selection
2035         while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
2036             Inkscape::NodePath::Node *temp = delete_cursor;
2037             if (delete_cursor->n.other == delete_cursor) {
2038                 // delete_cursor->n points to itself, which means this is the last node on a closed subpath
2039                 delete_cursor = NULL; 
2040             } else {
2041                 delete_cursor = delete_cursor->n.other;
2042             }
2043             nodes_to_delete = g_list_remove(nodes_to_delete, temp);
2044             sp_nodepath_node_destroy(temp);
2045         }
2047         sp_nodepath_update_handles(nodepath);
2049         if (!g_slist_find(nodepaths, nodepath))
2050             nodepaths = g_slist_prepend (nodepaths, nodepath);
2051     }
2053     for (GSList *i = nodepaths; i; i = i->next) {
2054         // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
2055         // different nodepaths will give us one undo event per nodepath
2056         Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
2058         // if the entire nodepath is removed, delete the selected object.
2059         if (nodepath->subpaths == NULL ||
2060             //FIXME: a closed path CAN legally have one node, it's only an open one which must be
2061             //at least 2
2062             sp_nodepath_get_node_count(nodepath) < 2) {
2063             SPDocument *document = sp_desktop_document (nodepath->desktop);
2064             //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
2065             //delete this nodepath's object, not the entire selection! (though at this time, this
2066             //does not matter)
2067             sp_selection_delete();
2068             sp_document_done (document, SP_VERB_CONTEXT_NODE,
2069                               _("Delete nodes"));
2070         } else {
2071             sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
2072             sp_nodepath_update_statusbar(nodepath);
2073         }
2074     }
2076     g_slist_free (nodepaths);
2079 /**
2080  * Delete one or more selected nodes.
2081  */
2082 void sp_node_selected_delete()
2084     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2085     if (!nodepath) return;
2086     if (!nodepath->selected) return;
2088     /** \todo fixme: do it the right way */
2089     while (nodepath->selected) {
2090        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
2091         sp_nodepath_node_destroy(node);
2092     }
2095     //clean up the nodepath (such as for trivial subpaths)
2096     sp_nodepath_cleanup(nodepath);
2098     sp_nodepath_update_handles(nodepath);
2100     // if the entire nodepath is removed, delete the selected object.
2101     if (nodepath->subpaths == NULL ||
2102         sp_nodepath_get_node_count(nodepath) < 2) {
2103         SPDocument *document = sp_desktop_document (nodepath->desktop);
2104         sp_selection_delete();
2105         sp_document_done (document, SP_VERB_CONTEXT_NODE, 
2106                           _("Delete nodes"));
2107         return;
2108     }
2110     sp_nodepath_update_repr(nodepath, _("Delete nodes"));
2112     sp_nodepath_update_statusbar(nodepath);
2115 /**
2116  * Delete one or more segments between two selected nodes.
2117  * This is the code for 'split'.
2118  */
2119 void
2120 sp_node_selected_delete_segment(void)
2122    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
2123    Inkscape::NodePath::Node *curr, *next;     //Iterators
2125     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2126     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2128     if (g_list_length(nodepath->selected) != 2) {
2129         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2130                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2131         return;
2132     }
2134     //Selected nodes, not inclusive
2135    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
2136    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
2138     if ( ( a==b)                       ||  //same node
2139          (a->subpath  != b->subpath )  ||  //not the same path
2140          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
2141          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
2142     {
2143         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2144                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
2145         return;
2146     }
2148     //###########################################
2149     //# BEGIN EDITS
2150     //###########################################
2151     //##################################
2152     //# CLOSED PATH
2153     //##################################
2154     if (a->subpath->closed) {
2157         gboolean reversed = FALSE;
2159         //Since we can go in a circle, we need to find the shorter distance.
2160         //  a->b or b->a
2161         start = end = NULL;
2162         int distance    = 0;
2163         int minDistance = 0;
2164         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2165             if (curr==b) {
2166                 //printf("a to b:%d\n", distance);
2167                 start = a;//go from a to b
2168                 end   = b;
2169                 minDistance = distance;
2170                 //printf("A to B :\n");
2171                 break;
2172             }
2173             distance++;
2174         }
2176         //try again, the other direction
2177         distance = 0;
2178         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2179             if (curr==a) {
2180                 //printf("b to a:%d\n", distance);
2181                 if (distance < minDistance) {
2182                     start    = b;  //we go from b to a
2183                     end      = a;
2184                     reversed = TRUE;
2185                     //printf("B to A\n");
2186                 }
2187                 break;
2188             }
2189             distance++;
2190         }
2193         //Copy everything from 'end' to 'start' to a new subpath
2194        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2195         for (curr=end ; curr ; curr=curr->n.other) {
2196             NRPathcode code = (NRPathcode) curr->code;
2197             if (curr == end)
2198                 code = NR_MOVETO;
2199             sp_nodepath_node_new(t, NULL,
2200                                  (Inkscape::NodePath::NodeType)curr->type, code,
2201                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2202             if (curr == start)
2203                 break;
2204         }
2205         sp_nodepath_subpath_destroy(a->subpath);
2208     }
2212     //##################################
2213     //# OPEN PATH
2214     //##################################
2215     else {
2217         //We need to get the direction of the list between A and B
2218         //Can we walk from a to b?
2219         start = end = NULL;
2220         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
2221             if (curr==b) {
2222                 start = a;  //did it!  we go from a to b
2223                 end   = b;
2224                 //printf("A to B\n");
2225                 break;
2226             }
2227         }
2228         if (!start) {//didn't work?  let's try the other direction
2229             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
2230                 if (curr==a) {
2231                     start = b;  //did it!  we go from b to a
2232                     end   = a;
2233                     //printf("B to A\n");
2234                     break;
2235                 }
2236             }
2237         }
2238         if (!start) {
2239             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
2240                                                      _("Cannot find path between nodes."));
2241             return;
2242         }
2246         //Copy everything after 'end' to a new subpath
2247        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
2248         for (curr=end ; curr ; curr=curr->n.other) {
2249             NRPathcode code = (NRPathcode) curr->code;
2250             if (curr == end)
2251                 code = NR_MOVETO;
2252             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
2253                                  &curr->p.pos, &curr->pos, &curr->n.pos);
2254         }
2256         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
2257         for (curr = start->n.other ; curr  ; curr=next) {
2258             next = curr->n.other;
2259             sp_nodepath_node_destroy(curr);
2260         }
2262     }
2263     //###########################################
2264     //# END EDITS
2265     //###########################################
2267     //clean up the nodepath (such as for trivial subpaths)
2268     sp_nodepath_cleanup(nodepath);
2270     sp_nodepath_update_handles(nodepath);
2272     sp_nodepath_update_repr(nodepath, _("Delete segment"));
2274     sp_nodepath_update_statusbar(nodepath);
2277 /**
2278  * Call sp_nodepath_set_line() for all selected segments.
2279  */
2280 void
2281 sp_node_selected_set_line_type(NRPathcode code)
2283     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2284     if (nodepath == NULL) return;
2286     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2287        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2288         g_assert(n->selected);
2289         if (n->p.other && n->p.other->selected) {
2290             sp_nodepath_set_line_type(n, code);
2291         }
2292     }
2294     sp_nodepath_update_repr(nodepath, _("Change segment type"));
2297 /**
2298  * Call sp_nodepath_convert_node_type() for all selected nodes.
2299  */
2300 void
2301 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
2303     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
2304     if (nodepath == NULL) return;
2306     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2307         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
2308     }
2310     sp_nodepath_update_repr(nodepath, _("Change node type"));
2313 /**
2314  * Change select status of node, update its own and neighbour handles.
2315  */
2316 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
2318     node->selected = selected;
2320     if (selected) {
2321         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
2322         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
2323         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
2324         sp_knot_update_ctrl(node->knot);
2325     } else {
2326         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
2327         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
2328         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
2329         sp_knot_update_ctrl(node->knot);
2330     }
2332     sp_node_update_handles(node);
2333     if (node->n.other) sp_node_update_handles(node->n.other);
2334     if (node->p.other) sp_node_update_handles(node->p.other);
2337 /**
2338 \brief Select a node
2339 \param node     The node to select
2340 \param incremental   If true, add to selection, otherwise deselect others
2341 \param override   If true, always select this node, otherwise toggle selected status
2342 */
2343 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
2345     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
2347     if (incremental) {
2348         if (override) {
2349             if (!g_list_find(nodepath->selected, node)) {
2350                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2351             }
2352             sp_node_set_selected(node, TRUE);
2353         } else { // toggle
2354             if (node->selected) {
2355                 g_assert(g_list_find(nodepath->selected, node));
2356                 nodepath->selected = g_list_remove(nodepath->selected, node);
2357             } else {
2358                 g_assert(!g_list_find(nodepath->selected, node));
2359                 nodepath->selected = g_list_prepend(nodepath->selected, node);
2360             }
2361             sp_node_set_selected(node, !node->selected);
2362         }
2363     } else {
2364         sp_nodepath_deselect(nodepath);
2365         nodepath->selected = g_list_prepend(nodepath->selected, node);
2366         sp_node_set_selected(node, TRUE);
2367     }
2369     sp_nodepath_update_statusbar(nodepath);
2373 /**
2374 \brief Deselect all nodes in the nodepath
2375 */
2376 void
2377 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
2379     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2381     while (nodepath->selected) {
2382         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
2383         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
2384     }
2385     sp_nodepath_update_statusbar(nodepath);
2388 /**
2389 \brief Select or invert selection of all nodes in the nodepath
2390 */
2391 void
2392 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
2394     if (!nodepath) return;
2396     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2397        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2398         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2399            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2400            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
2401         }
2402     }
2405 /**
2406  * If nothing selected, does the same as sp_nodepath_select_all();
2407  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
2408  * (i.e., similar to "select all in layer", with the "selected" subpaths
2409  * being treated as "layers" in the path).
2410  */
2411 void
2412 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
2414     if (!nodepath) return;
2416     if (g_list_length (nodepath->selected) == 0) {
2417         sp_nodepath_select_all (nodepath, invert);
2418         return;
2419     }
2421     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
2422     GSList *subpaths = NULL;
2424     for (GList *l = copy; l != NULL; l = l->next) {
2425         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2426         Inkscape::NodePath::SubPath *subpath = n->subpath;
2427         if (!g_slist_find (subpaths, subpath))
2428             subpaths = g_slist_prepend (subpaths, subpath);
2429     }
2431     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
2432         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
2433         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2434             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2435             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
2436         }
2437     }
2439     g_slist_free (subpaths);
2440     g_list_free (copy);
2443 /**
2444  * \brief Select the node after the last selected; if none is selected,
2445  * select the first within path.
2446  */
2447 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
2449     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2451    Inkscape::NodePath::Node *last = NULL;
2452     if (nodepath->selected) {
2453         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2454            Inkscape::NodePath::SubPath *subpath, *subpath_next;
2455             subpath = (Inkscape::NodePath::SubPath *) spl->data;
2456             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2457                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2458                 if (node->selected) {
2459                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
2460                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
2461                             if (spl->next) { // there's a next subpath
2462                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2463                                 last = subpath_next->first;
2464                             } else if (spl->prev) { // there's a previous subpath
2465                                 last = NULL; // to be set later to the first node of first subpath
2466                             } else {
2467                                 last = node->n.other;
2468                             }
2469                         } else {
2470                             last = node->n.other;
2471                         }
2472                     } else {
2473                         if (node->n.other) {
2474                             last = node->n.other;
2475                         } else {
2476                             if (spl->next) { // there's a next subpath
2477                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2478                                 last = subpath_next->first;
2479                             } else if (spl->prev) { // there's a previous subpath
2480                                 last = NULL; // to be set later to the first node of first subpath
2481                             } else {
2482                                 last = (Inkscape::NodePath::Node *) subpath->first;
2483                             }
2484                         }
2485                     }
2486                 }
2487             }
2488         }
2489         sp_nodepath_deselect(nodepath);
2490     }
2492     if (last) { // there's at least one more node after selected
2493         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2494     } else { // no more nodes, select the first one in first subpath
2495        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2496         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2497     }
2500 /**
2501  * \brief Select the node before the first selected; if none is selected,
2502  * select the last within path
2503  */
2504 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2506     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2508    Inkscape::NodePath::Node *last = NULL;
2509     if (nodepath->selected) {
2510         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2511            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2512             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2513                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2514                 if (node->selected) {
2515                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2516                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2517                             if (spl->prev) { // there's a prev subpath
2518                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2519                                 last = subpath_prev->last;
2520                             } else if (spl->next) { // there's a next subpath
2521                                 last = NULL; // to be set later to the last node of last subpath
2522                             } else {
2523                                 last = node->p.other;
2524                             }
2525                         } else {
2526                             last = node->p.other;
2527                         }
2528                     } else {
2529                         if (node->p.other) {
2530                             last = node->p.other;
2531                         } else {
2532                             if (spl->prev) { // there's a prev subpath
2533                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2534                                 last = subpath_prev->last;
2535                             } else if (spl->next) { // there's a next subpath
2536                                 last = NULL; // to be set later to the last node of last subpath
2537                             } else {
2538                                 last = (Inkscape::NodePath::Node *) subpath->last;
2539                             }
2540                         }
2541                     }
2542                 }
2543             }
2544         }
2545         sp_nodepath_deselect(nodepath);
2546     }
2548     if (last) { // there's at least one more node before selected
2549         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2550     } else { // no more nodes, select the last one in last subpath
2551         GList *spl = g_list_last(nodepath->subpaths);
2552        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2553         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2554     }
2557 /**
2558  * \brief Select all nodes that are within the rectangle.
2559  */
2560 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2562     if (!incremental) {
2563         sp_nodepath_deselect(nodepath);
2564     }
2566     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2567        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2568         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2569            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2571             if (b.contains(node->pos)) {
2572                 sp_nodepath_node_select(node, TRUE, TRUE);
2573             }
2574         }
2575     }
2579 void
2580 nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2582     g_assert (n);
2583     g_assert (nodepath);
2584     g_assert (n->subpath->nodepath == nodepath);
2586     if (g_list_length (nodepath->selected) == 0) {
2587         if (grow > 0) {
2588             sp_nodepath_node_select(n, TRUE, TRUE);
2589         }
2590         return;
2591     }
2593     if (g_list_length (nodepath->selected) == 1) {
2594         if (grow < 0) {
2595             sp_nodepath_deselect (nodepath);
2596             return;
2597         }
2598     }
2600         double n_sel_range = 0, p_sel_range = 0;
2601             Inkscape::NodePath::Node *farthest_n_node = n;
2602             Inkscape::NodePath::Node *farthest_p_node = n;
2604         // Calculate ranges
2605         {
2606             double n_range = 0, p_range = 0;
2607             bool n_going = true, p_going = true;
2608             Inkscape::NodePath::Node *n_node = n;
2609             Inkscape::NodePath::Node *p_node = n;
2610             do {
2611                 // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
2612                 if (n_node && n_going)
2613                     n_node = n_node->n.other;
2614                 if (n_node == NULL) {
2615                     n_going = false;
2616                 } else {
2617                     n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
2618                     if (n_node->selected) {
2619                         n_sel_range = n_range;
2620                         farthest_n_node = n_node;
2621                     }
2622                     if (n_node == p_node) {
2623                         n_going = false;
2624                         p_going = false;
2625                     }
2626                 }
2627                 if (p_node && p_going)
2628                     p_node = p_node->p.other;
2629                 if (p_node == NULL) {
2630                     p_going = false;
2631                 } else {
2632                     p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
2633                     if (p_node->selected) {
2634                         p_sel_range = p_range;
2635                         farthest_p_node = p_node;
2636                     }
2637                     if (p_node == n_node) {
2638                         n_going = false;
2639                         p_going = false;
2640                     }
2641                 }
2642             } while (n_going || p_going);
2643         }
2645     if (grow > 0) {
2646         if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
2647                 sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
2648         } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
2649                 sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
2650         }
2651     } else {
2652         if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
2653                 sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
2654         } else if (farthest_p_node && farthest_p_node->selected) {
2655                 sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
2656         }
2657     }
2660 void
2661 nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
2663     g_assert (n);
2664     g_assert (nodepath);
2665     g_assert (n->subpath->nodepath == nodepath);
2667     if (g_list_length (nodepath->selected) == 0) {
2668         if (grow > 0) {
2669             sp_nodepath_node_select(n, TRUE, TRUE);
2670         }
2671         return;
2672     }
2674     if (g_list_length (nodepath->selected) == 1) {
2675         if (grow < 0) {
2676             sp_nodepath_deselect (nodepath);
2677             return;
2678         }
2679     }
2681     Inkscape::NodePath::Node *farthest_selected = NULL;
2682     double farthest_dist = 0;
2684     Inkscape::NodePath::Node *closest_unselected = NULL;
2685     double closest_dist = NR_HUGE;
2687     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2688        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2689         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2690            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2691            if (node == n)
2692                continue;
2693            if (node->selected) {
2694                if (NR::L2(node->pos - n->pos) > farthest_dist) {
2695                    farthest_dist = NR::L2(node->pos - n->pos);
2696                    farthest_selected = node;
2697                }
2698            } else {
2699                if (NR::L2(node->pos - n->pos) < closest_dist) {
2700                    closest_dist = NR::L2(node->pos - n->pos);
2701                    closest_unselected = node;
2702                }
2703            }
2704         }
2705     }
2707     if (grow > 0) {
2708         if (closest_unselected) {
2709             sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
2710         }
2711     } else {
2712         if (farthest_selected) {
2713             sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
2714         }
2715     }
2719 /**
2720 \brief  Saves all nodes' and handles' current positions in their origin members
2721 */
2722 void
2723 sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
2725     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2726        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2727         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2728            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
2729            n->origin = n->pos;
2730            n->p.origin = n->p.pos;
2731            n->n.origin = n->n.pos;
2732         }
2733     }
2734
2736 /**
2737 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2738 */
2739 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2741     if (!nodepath->selected) {
2742         return NULL;
2743     }
2745     GList *r = NULL;
2746     guint i = 0;
2747     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2748        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2749         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2750            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2751             i++;
2752             if (node->selected) {
2753                 r = g_list_append(r, GINT_TO_POINTER(i));
2754             }
2755         }
2756     }
2757     return r;
2760 /**
2761 \brief  Restores selection by selecting nodes whose positions are in the list
2762 */
2763 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2765     sp_nodepath_deselect(nodepath);
2767     guint i = 0;
2768     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2769        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2770         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2771            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2772             i++;
2773             if (g_list_find(r, GINT_TO_POINTER(i))) {
2774                 sp_nodepath_node_select(node, TRUE, TRUE);
2775             }
2776         }
2777     }
2781 /**
2782 \brief Adjusts handle according to node type and line code.
2783 */
2784 static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
2786     double len, otherlen, linelen;
2788     g_assert(node);
2790    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2791    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2793     /** \todo fixme: */
2794     if (me->other == NULL) return;
2795     if (other->other == NULL) return;
2797     /* I have line */
2799     NRPathcode mecode, ocode;
2800     if (which_adjust == 1) {
2801         mecode = (NRPathcode)me->other->code;
2802         ocode = (NRPathcode)node->code;
2803     } else {
2804         mecode = (NRPathcode)node->code;
2805         ocode = (NRPathcode)other->other->code;
2806     }
2808     if (mecode == NR_LINETO) return;
2810     /* I am curve */
2812     if (other->other == NULL) return;
2814     /* Other has line */
2816     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2818     NR::Point delta;
2819     if (ocode == NR_LINETO) {
2820         /* other is lineto, we are either smooth or symm */
2821        Inkscape::NodePath::Node *othernode = other->other;
2822         len = NR::L2(me->pos - node->pos);
2823         delta = node->pos - othernode->pos;
2824         linelen = NR::L2(delta);
2825         if (linelen < 1e-18) 
2826             return;
2827         me->pos = node->pos + (len / linelen)*delta;
2828         return;
2829     }
2831     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2833         me->pos = 2 * node->pos - other->pos;
2834         return;
2835     }
2837     /* We are smooth */
2839     len = NR::L2(me->pos - node->pos);
2840     delta = other->pos - node->pos;
2841     otherlen = NR::L2(delta);
2842     if (otherlen < 1e-18) return;
2844     me->pos = node->pos - (len / otherlen) * delta;
2847 /**
2848  \brief Adjusts both handles according to node type and line code
2849  */
2850 static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
2852     g_assert(node);
2854     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2856     /* we are either smooth or symm */
2858     if (node->p.other == NULL) return;
2860     if (node->n.other == NULL) return;
2862     if (node->code == NR_LINETO) {
2863         if (node->n.other->code == NR_LINETO) return;
2864         sp_node_adjust_handle(node, 1);
2865         return;
2866     }
2868     if (node->n.other->code == NR_LINETO) {
2869         if (node->code == NR_LINETO) return;
2870         sp_node_adjust_handle(node, -1);
2871         return;
2872     }
2874     /* both are curves */
2875     NR::Point const delta( node->n.pos - node->p.pos );
2877     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2878         node->p.pos = node->pos - delta / 2;
2879         node->n.pos = node->pos + delta / 2;
2880         return;
2881     }
2883     /* We are smooth */
2884     double plen = NR::L2(node->p.pos - node->pos);
2885     if (plen < 1e-18) return;
2886     double nlen = NR::L2(node->n.pos - node->pos);
2887     if (nlen < 1e-18) return;
2888     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2889     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2892 /**
2893  * Node event callback.
2894  */
2895 static gboolean node_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n)
2897     gboolean ret = FALSE;
2898     switch (event->type) {
2899         case GDK_ENTER_NOTIFY:
2900             Inkscape::NodePath::Path::active_node = n;
2901             break;
2902         case GDK_LEAVE_NOTIFY:
2903             Inkscape::NodePath::Path::active_node = NULL;
2904             break;
2905         case GDK_SCROLL:
2906             if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
2907                 switch (event->scroll.direction) {
2908                     case GDK_SCROLL_UP:
2909                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2910                         break;
2911                     case GDK_SCROLL_DOWN:
2912                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2913                         break;
2914                     default:
2915                         break;
2916                 }
2917                 ret = TRUE;
2918             } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
2919                 switch (event->scroll.direction) {
2920                     case GDK_SCROLL_UP:
2921                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2922                         break;
2923                     case GDK_SCROLL_DOWN:
2924                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2925                         break;
2926                     default:
2927                         break;
2928                 }
2929                 ret = TRUE;
2930             }
2931             break;
2932         case GDK_KEY_PRESS:
2933             switch (get_group0_keyval (&event->key)) {
2934                 case GDK_space:
2935                     if (event->key.state & GDK_BUTTON1_MASK) {
2936                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2937                         stamp_repr(nodepath);
2938                         ret = TRUE;
2939                     }
2940                     break;
2941                 case GDK_Page_Up:
2942                     if (event->key.state & GDK_CONTROL_MASK) {
2943                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
2944                     } else {
2945                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
2946                     }
2947                     break;
2948                 case GDK_Page_Down:
2949                     if (event->key.state & GDK_CONTROL_MASK) {
2950                         nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
2951                     } else {
2952                         nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
2953                     }
2954                     break;
2955                 default:
2956                     break;
2957             }
2958             break;
2959         default:
2960             break;
2961     }
2963     return ret;
2966 /**
2967  * Handle keypress on node; directly called.
2968  */
2969 gboolean node_key(GdkEvent *event)
2971     Inkscape::NodePath::Path *np;
2973     // there is no way to verify nodes so set active_node to nil when deleting!!
2974     if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
2976     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2977         gint ret = FALSE;
2978         switch (get_group0_keyval (&event->key)) {
2979             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2980             case GDK_BackSpace:
2981                 np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
2982                 sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
2983                 sp_nodepath_update_repr(np, _("Delete node"));
2984                 Inkscape::NodePath::Path::active_node = NULL;
2985                 ret = TRUE;
2986                 break;
2987             case GDK_c:
2988                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
2989                 ret = TRUE;
2990                 break;
2991             case GDK_s:
2992                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
2993                 ret = TRUE;
2994                 break;
2995             case GDK_y:
2996                 sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
2997                 ret = TRUE;
2998                 break;
2999             case GDK_b:
3000                 sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
3001                 ret = TRUE;
3002                 break;
3003         }
3004         return ret;
3005     }
3006     return FALSE;
3009 /**
3010  * Mouseclick on node callback.
3011  */
3012 static void node_clicked(SPKnot *knot, guint state, gpointer data)
3014    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3016     if (state & GDK_CONTROL_MASK) {
3017         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3019         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
3020             if (n->type == Inkscape::NodePath::NODE_CUSP) {
3021                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
3022             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
3023                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
3024             } else {
3025                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
3026             }
3027             sp_nodepath_update_repr(nodepath, _("Change node type"));
3028             sp_nodepath_update_statusbar(nodepath);
3030         } else { //ctrl+alt+click: delete node
3031             GList *node_to_delete = NULL;
3032             node_to_delete = g_list_append(node_to_delete, n);
3033             sp_node_delete_preserve(node_to_delete);
3034         }
3036     } else {
3037         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3038     }
3041 /**
3042  * Mouse grabbed node callback.
3043  */
3044 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
3046    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3048     if (!n->selected) {
3049         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3050     }
3052     n->is_dragging = true;
3053     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3055     sp_nodepath_remember_origins (n->subpath->nodepath);
3058 /**
3059  * Mouse ungrabbed node callback.
3060  */
3061 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
3063    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3065    n->dragging_out = NULL;
3066    n->is_dragging = false;
3067    sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
3069    sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
3072 /**
3073  * The point on a line, given by its angle, closest to the given point.
3074  * \param p  A point.
3075  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3076  * \param closest  Pointer to the point struct where the result is stored.
3077  * \todo FIXME: use dot product perhaps?
3078  */
3079 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
3081     if (a == HUGE_VAL) { // vertical
3082         *closest = NR::Point(0, (*p)[NR::Y]);
3083     } else {
3084         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
3085         (*closest)[NR::Y] = a * (*closest)[NR::X];
3086     }
3089 /**
3090  * Distance from the point to a line given by its angle.
3091  * \param p  A point.
3092  * \param a  Angle of the line; it is assumed to go through coordinate origin.
3093  */
3094 static double point_line_distance(NR::Point *p, double a)
3096     NR::Point c;
3097     point_line_closest(p, a, &c);
3098     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]));
3101 /**
3102  * Callback for node "request" signal.
3103  * \todo fixme: This goes to "moved" event? (lauris)
3104  */
3105 static gboolean
3106 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3108     double yn, xn, yp, xp;
3109     double an, ap, na, pa;
3110     double d_an, d_ap, d_na, d_pa;
3111     gboolean collinear = FALSE;
3112     NR::Point c;
3113     NR::Point pr;
3115    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3117    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
3118    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
3120        NR::Point mouse = (*p);
3122        if (!n->dragging_out) {
3123            // This is the first drag-out event; find out which handle to drag out
3124            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
3125            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
3127            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
3128                return FALSE;
3130            Inkscape::NodePath::NodeSide *opposite;
3131            if (appr_p > appr_n) { // closer to p
3132                n->dragging_out = &n->p;
3133                opposite = &n->n;
3134                n->code = NR_CURVETO;
3135            } else if (appr_p < appr_n) { // closer to n
3136                n->dragging_out = &n->n;
3137                opposite = &n->p;
3138                n->n.other->code = NR_CURVETO;
3139            } else { // p and n nodes are the same
3140                if (n->n.pos != n->pos) { // n handle already dragged, drag p
3141                    n->dragging_out = &n->p;
3142                    opposite = &n->n;
3143                    n->code = NR_CURVETO;
3144                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
3145                    n->dragging_out = &n->n;
3146                    opposite = &n->p;
3147                    n->n.other->code = NR_CURVETO;
3148                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
3149                    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);
3150                    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);
3151                    if (appr_other_p > appr_other_n) { // closer to other's p handle
3152                        n->dragging_out = &n->n;
3153                        opposite = &n->p;
3154                        n->n.other->code = NR_CURVETO;
3155                    } else { // closer to other's n handle
3156                        n->dragging_out = &n->p;
3157                        opposite = &n->n;
3158                        n->code = NR_CURVETO;
3159                    }
3160                }
3161            }
3163            // if there's another handle, make sure the one we drag out starts parallel to it
3164            if (opposite->pos != n->pos) {
3165                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
3166            }
3168            // knots might not be created yet!
3169            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
3170            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
3171        }
3173        // pass this on to the handle-moved callback
3174        node_handle_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
3175        sp_node_update_handles(n);
3176        return TRUE;
3177    }
3179     if (state & GDK_CONTROL_MASK) { // constrained motion
3181         // calculate relative distances of handles
3182         // n handle:
3183         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
3184         xn = n->n.pos[NR::X] - n->pos[NR::X];
3185         // if there's no n handle (straight line), see if we can use the direction to the next point on path
3186         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
3187             if (n->n.other) { // if there is the next point
3188                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
3189                     yn = n->n.other->origin[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
3190                     xn = n->n.other->origin[NR::X] - n->origin[NR::X];
3191             }
3192         }
3193         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
3194         if (yn < 0) { xn = -xn; yn = -yn; }
3196         // p handle:
3197         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
3198         xp = n->p.pos[NR::X] - n->pos[NR::X];
3199         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
3200         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
3201             if (n->p.other) {
3202                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
3203                     yp = n->p.other->origin[NR::Y] - n->origin[NR::Y];
3204                     xp = n->p.other->origin[NR::X] - n->origin[NR::X];
3205             }
3206         }
3207         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
3208         if (yp < 0) { xp = -xp; yp = -yp; }
3210         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
3211             // sliding on handles, only if at least one of the handles is non-vertical
3212             // (otherwise it's the same as ctrl+drag anyway)
3214             // calculate angles of the handles
3215             if (xn == 0) {
3216                 if (yn == 0) { // no handle, consider it the continuation of the other one
3217                     an = 0;
3218                     collinear = TRUE;
3219                 }
3220                 else an = 0; // vertical; set the angle to horizontal
3221             } else an = yn/xn;
3223             if (xp == 0) {
3224                 if (yp == 0) { // no handle, consider it the continuation of the other one
3225                     ap = an;
3226                 }
3227                 else ap = 0; // vertical; set the angle to horizontal
3228             } else  ap = yp/xp;
3230             if (collinear) an = ap;
3232             // angles of the perpendiculars; HUGE_VAL means vertical
3233             if (an == 0) na = HUGE_VAL; else na = -1/an;
3234             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
3236             // mouse point relative to the node's original pos
3237             pr = (*p) - n->origin;
3239             // distances to the four lines (two handles and two perpendiculars)
3240             d_an = point_line_distance(&pr, an);
3241             d_na = point_line_distance(&pr, na);
3242             d_ap = point_line_distance(&pr, ap);
3243             d_pa = point_line_distance(&pr, pa);
3245             // find out which line is the closest, save its closest point in c
3246             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
3247                 point_line_closest(&pr, an, &c);
3248             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
3249                 point_line_closest(&pr, ap, &c);
3250             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
3251                 point_line_closest(&pr, na, &c);
3252             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
3253                 point_line_closest(&pr, pa, &c);
3254             }
3256             // move the node to the closest point
3257             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3258                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
3259                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
3261         } else {  // constraining to hor/vert
3263             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
3264                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
3265             } else { // snap to vert
3266                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
3267             }
3268         }
3269     } else { // move freely
3270         if (n->is_dragging) {
3271             if (state & GDK_MOD1_MASK) { // sculpt
3272                 sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, (*p) - n->origin);
3273             } else {
3274                 sp_nodepath_selected_nodes_move(n->subpath->nodepath,
3275                                             (*p)[NR::X] - n->pos[NR::X],
3276                                             (*p)[NR::Y] - n->pos[NR::Y],
3277                                             (state & GDK_SHIFT_MASK) == 0);
3278             }
3279         }
3280     }
3282     n->subpath->nodepath->desktop->scroll_to_point(p);
3284     return TRUE;
3287 /**
3288  * Node handle clicked callback.
3289  */
3290 static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
3292    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3294     if (state & GDK_CONTROL_MASK) { // "delete" handle
3295         if (n->p.knot == knot) {
3296             n->p.pos = n->pos;
3297         } else if (n->n.knot == knot) {
3298             n->n.pos = n->pos;
3299         }
3300         sp_node_update_handles(n);
3301         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3302         sp_nodepath_update_repr(nodepath, _("Retract handle"));
3303         sp_nodepath_update_statusbar(nodepath);
3305     } else { // just select or add to selection, depending in Shift
3306         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3307     }
3310 /**
3311  * Node handle grabbed callback.
3312  */
3313 static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
3315    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3317     if (!n->selected) {
3318         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
3319     }
3321     // remember the origin point of the handle
3322     if (n->p.knot == knot) {
3323         n->p.origin_radial = n->p.pos - n->pos;
3324     } else if (n->n.knot == knot) {
3325         n->n.origin_radial = n->n.pos - n->pos;
3326     } else {
3327         g_assert_not_reached();
3328     }
3330     sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
3333 /**
3334  * Node handle ungrabbed callback.
3335  */
3336 static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
3338    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3340     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
3341     if (n->p.knot == knot) {
3342         n->p.origin_radial.a = 0;
3343         sp_knot_set_position(knot, &n->p.pos, state);
3344     } else if (n->n.knot == knot) {
3345         n->n.origin_radial.a = 0;
3346         sp_knot_set_position(knot, &n->n.pos, state);
3347     } else {
3348         g_assert_not_reached();
3349     }
3351     sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
3354 /**
3355  * Node handle "request" signal callback.
3356  */
3357 static gboolean node_handle_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3359     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3361     Inkscape::NodePath::NodeSide *me, *opposite;
3362     gint which;
3363     if (n->p.knot == knot) {
3364         me = &n->p;
3365         opposite = &n->n;
3366         which = -1;
3367     } else if (n->n.knot == knot) {
3368         me = &n->n;
3369         opposite = &n->p;
3370         which = 1;
3371     } else {
3372         me = opposite = NULL;
3373         which = 0;
3374         g_assert_not_reached();
3375     }
3377     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
3379     SnapManager const &m = n->subpath->nodepath->desktop->namedview->snap_manager;
3381     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
3382         /* We are smooth node adjacent with line */
3383         NR::Point const delta = *p - n->pos;
3384         NR::Coord const len = NR::L2(delta);
3385         Inkscape::NodePath::Node *othernode = opposite->other;
3386         NR::Point const ndelta = n->pos - othernode->pos;
3387         NR::Coord const linelen = NR::L2(ndelta);
3388         if (len > NR_EPSILON && linelen > NR_EPSILON) {
3389             NR::Coord const scal = dot(delta, ndelta) / linelen;
3390             (*p) = n->pos + (scal / linelen) * ndelta;
3391         }
3392         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, Inkscape::Snapper::ConstraintLine(*p, ndelta), NULL).getPoint();
3393     } else {
3394         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
3395     }
3397     sp_node_adjust_handle(n, -which);
3399     return FALSE;
3402 /**
3403  * Node handle moved callback.
3404  */
3405 static void node_handle_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
3407    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
3409    Inkscape::NodePath::NodeSide *me;
3410    Inkscape::NodePath::NodeSide *other;
3411     if (n->p.knot == knot) {
3412         me = &n->p;
3413         other = &n->n;
3414     } else if (n->n.knot == knot) {
3415         me = &n->n;
3416         other = &n->p;
3417     } else {
3418         me = NULL;
3419         other = NULL;
3420         g_assert_not_reached();
3421     }
3423     // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
3424     Radial rme(me->pos - n->pos);
3425     Radial rother(other->pos - n->pos);
3426     Radial rnew(*p - n->pos);
3428     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
3429         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
3430         /* 0 interpreted as "no snapping". */
3432         // The closest PI/snaps angle, starting from zero.
3433         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
3434         if (me->origin_radial.a == HUGE_VAL) {
3435             // ortho doesn't exist: original handle was zero length.
3436             rnew.a = a_snapped;
3437         } else {
3438             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
3439              * its opposite and perpendiculars). */
3440             double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
3442             // Snap to the closest.
3443             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
3444                        ? a_snapped
3445                        : a_ortho );
3446         }
3447     }
3449     if (state & GDK_MOD1_MASK) {
3450         // lock handle length
3451         rnew.r = me->origin_radial.r;
3452     }
3454     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
3455         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && (fabs(rme.a - rnew.a) > 0.001 || n->type ==Inkscape::NodePath::NODE_SYMM)) {
3456         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
3457         rother.a += rnew.a - rme.a;
3458         other->pos = NR::Point(rother) + n->pos;
3459         if (other->knot) {
3460             sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
3461             sp_knot_moveto(other->knot, &other->pos);
3462         }
3463     }
3465     me->pos = NR::Point(rnew) + n->pos;
3466     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
3468     // move knot, but without emitting the signal:
3469     // we cannot emit a "moved" signal because we're now processing it
3470     sp_knot_moveto(me->knot, &(me->pos));
3472     update_object(n->subpath->nodepath);
3474     /* status text */
3475     SPDesktop *desktop = n->subpath->nodepath->desktop;
3476     if (!desktop) return;
3477     SPEventContext *ec = desktop->event_context;
3478     if (!ec) return;
3479     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3480     if (!mc) return;
3482     double degrees = 180 / M_PI * rnew.a;
3483     if (degrees > 180) degrees -= 360;
3484     if (degrees < -180) degrees += 360;
3485     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
3486         degrees = angle_to_compass (degrees);
3488     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
3490     mc->setF(Inkscape::NORMAL_MESSAGE,
3491          _("<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);
3493     g_string_free(length, TRUE);
3496 /**
3497  * Node handle event callback.
3498  */
3499 static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
3501     gboolean ret = FALSE;
3502     switch (event->type) {
3503         case GDK_KEY_PRESS:
3504             switch (get_group0_keyval (&event->key)) {
3505                 case GDK_space:
3506                     if (event->key.state & GDK_BUTTON1_MASK) {
3507                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
3508                         stamp_repr(nodepath);
3509                         ret = TRUE;
3510                     }
3511                     break;
3512                 default:
3513                     break;
3514             }
3515             break;
3516         case GDK_ENTER_NOTIFY:
3517             // we use an experimentally determined threshold that seems to work fine
3518             if (NR::L2(n->pos - knot->pos) < 0.75)
3519                 Inkscape::NodePath::Path::active_node = n;
3520             break;
3521         case GDK_LEAVE_NOTIFY:
3522             // we use an experimentally determined threshold that seems to work fine
3523             if (NR::L2(n->pos - knot->pos) < 0.75)
3524                 Inkscape::NodePath::Path::active_node = NULL;
3525             break;
3526         default:
3527             break;
3528     }
3530     return ret;
3533 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
3534                                  Radial &rme, Radial &rother, gboolean const both)
3536     rme.a += angle;
3537     if ( both
3538          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3539          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3540     {
3541         rother.a += angle;
3542     }
3545 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
3546                                         Radial &rme, Radial &rother, gboolean const both)
3548     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
3550     gdouble r;
3551     if ( both
3552          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3553          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3554     {
3555         r = MAX(rme.r, rother.r);
3556     } else {
3557         r = rme.r;
3558     }
3560     gdouble const weird_angle = atan2(norm_angle, r);
3561 /* Bulia says norm_angle is just the visible distance that the
3562  * object's end must travel on the screen.  Left as 'angle' for want of
3563  * a better name.*/
3565     rme.a += weird_angle;
3566     if ( both
3567          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
3568          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
3569     {
3570         rother.a += weird_angle;
3571     }
3574 /**
3575  * Rotate one node.
3576  */
3577 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
3579     Inkscape::NodePath::NodeSide *me, *other;
3580     bool both = false;
3582     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3583     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3585     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3586         me = &(n->p);
3587         other = &(n->n);
3588     } else if (!n->p.other) {
3589         me = &(n->n);
3590         other = &(n->p);
3591     } else {
3592         if (which > 0) { // right handle
3593             if (xn > xp) {
3594                 me = &(n->n);
3595                 other = &(n->p);
3596             } else {
3597                 me = &(n->p);
3598                 other = &(n->n);
3599             }
3600         } else if (which < 0){ // left handle
3601             if (xn <= xp) {
3602                 me = &(n->n);
3603                 other = &(n->p);
3604             } else {
3605                 me = &(n->p);
3606                 other = &(n->n);
3607             }
3608         } else { // both handles
3609             me = &(n->n);
3610             other = &(n->p);
3611             both = true;
3612         }
3613     }
3615     Radial rme(me->pos - n->pos);
3616     Radial rother(other->pos - n->pos);
3618     if (screen) {
3619         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
3620     } else {
3621         node_rotate_one_internal (*n, angle, rme, rother, both);
3622     }
3624     me->pos = n->pos + NR::Point(rme);
3626     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
3627         other->pos =  n->pos + NR::Point(rother);
3628     }
3630     // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
3631     // so here we just move all the knots without emitting move signals, for speed
3632     sp_node_update_handles(n, false);
3635 /**
3636  * Rotate selected nodes.
3637  */
3638 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
3640     if (!nodepath || !nodepath->selected) return;
3642     if (g_list_length(nodepath->selected) == 1) {
3643        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3644         node_rotate_one (n, angle, which, screen);
3645     } else {
3646        // rotate as an object:
3648         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3649         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3650         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3651             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3652             box.expandTo (n->pos); // contain all selected nodes
3653         }
3655         gdouble rot;
3656         if (screen) {
3657             gdouble const zoom = nodepath->desktop->current_zoom();
3658             gdouble const zmove = angle / zoom;
3659             gdouble const r = NR::L2(box.max() - box.midpoint());
3660             rot = atan2(zmove, r);
3661         } else {
3662             rot = angle;
3663         }
3665         NR::Matrix t =
3666             NR::Matrix (NR::translate(-box.midpoint())) *
3667             NR::Matrix (NR::rotate(rot)) *
3668             NR::Matrix (NR::translate(box.midpoint()));
3670         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3671             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3672             n->pos *= t;
3673             n->n.pos *= t;
3674             n->p.pos *= t;
3675             sp_node_update_handles(n, false);
3676         }
3677     }
3679     sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
3682 /**
3683  * Scale one node.
3684  */
3685 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3687     bool both = false;
3688     Inkscape::NodePath::NodeSide *me, *other;
3690     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3691     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3693     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3694         me = &(n->p);
3695         other = &(n->n);
3696         n->code = NR_CURVETO;
3697     } else if (!n->p.other) {
3698         me = &(n->n);
3699         other = &(n->p);
3700         if (n->n.other)
3701             n->n.other->code = NR_CURVETO;
3702     } else {
3703         if (which > 0) { // right handle
3704             if (xn > xp) {
3705                 me = &(n->n);
3706                 other = &(n->p);
3707                 if (n->n.other)
3708                     n->n.other->code = NR_CURVETO;
3709             } else {
3710                 me = &(n->p);
3711                 other = &(n->n);
3712                 n->code = NR_CURVETO;
3713             }
3714         } else if (which < 0){ // left handle
3715             if (xn <= xp) {
3716                 me = &(n->n);
3717                 other = &(n->p);
3718                 if (n->n.other)
3719                     n->n.other->code = NR_CURVETO;
3720             } else {
3721                 me = &(n->p);
3722                 other = &(n->n);
3723                 n->code = NR_CURVETO;
3724             }
3725         } else { // both handles
3726             me = &(n->n);
3727             other = &(n->p);
3728             both = true;
3729             n->code = NR_CURVETO;
3730             if (n->n.other)
3731                 n->n.other->code = NR_CURVETO;
3732         }
3733     }
3735     Radial rme(me->pos - n->pos);
3736     Radial rother(other->pos - n->pos);
3738     rme.r += grow;
3739     if (rme.r < 0) rme.r = 0;
3740     if (rme.a == HUGE_VAL) {
3741         if (me->other) { // if direction is unknown, initialize it towards the next node
3742             Radial rme_next(me->other->pos - n->pos);
3743             rme.a = rme_next.a;
3744         } else { // if there's no next, initialize to 0
3745             rme.a = 0;
3746         }
3747     }
3748     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3749         rother.r += grow;
3750         if (rother.r < 0) rother.r = 0;
3751         if (rother.a == HUGE_VAL) {
3752             rother.a = rme.a + M_PI;
3753         }
3754     }
3756     me->pos = n->pos + NR::Point(rme);
3758     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3759         other->pos = n->pos + NR::Point(rother);
3760     }
3762     // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
3763     // so here we just move all the knots without emitting move signals, for speed
3764     sp_node_update_handles(n, false);
3767 /**
3768  * Scale selected nodes.
3769  */
3770 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3772     if (!nodepath || !nodepath->selected) return;
3774     if (g_list_length(nodepath->selected) == 1) {
3775         // scale handles of the single selected node
3776         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3777         node_scale_one (n, grow, which);
3778     } else {
3779         // scale nodes as an "object":
3781         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3782         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3783         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3784             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3785             box.expandTo (n->pos); // contain all selected nodes
3786         }
3788         double scale = (box.maxExtent() + grow)/box.maxExtent();
3790         NR::Matrix t =
3791             NR::Matrix (NR::translate(-box.midpoint())) *
3792             NR::Matrix (NR::scale(scale, scale)) *
3793             NR::Matrix (NR::translate(box.midpoint()));
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 :