Code

8c08b17c9c7f37ea786ba82f6e7a82627094da0c
[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  * This code is in public domain
11  */
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
17 #include <gdk/gdkkeysyms.h>
18 #include "display/curve.h"
19 #include "display/sp-ctrlline.h"
20 #include "display/sodipodi-ctrl.h"
21 #include <glibmm/i18n.h>
22 #include "libnr/n-art-bpath.h"
23 #include "helper/units.h"
24 #include "knot.h"
25 #include "inkscape.h"
26 #include "document.h"
27 #include "sp-namedview.h"
28 #include "desktop.h"
29 #include "desktop-handles.h"
30 #include "snap.h"
31 #include "message-stack.h"
32 #include "message-context.h"
33 #include "node-context.h"
34 #include "selection-chemistry.h"
35 #include "selection.h"
36 #include "xml/repr.h"
37 #include "prefs-utils.h"
38 #include "sp-metrics.h"
39 #include "sp-path.h"
40 #include <libnr/nr-matrix-ops.h>
41 #include "splivarot.h"
42 #include "svg/svg.h"
44 class NR::Matrix;
46 /// \todo
47 /// evil evil evil. FIXME: conflict of two different Path classes!
48 /// There is a conflict in the namespace between two classes named Path.
49 /// #include "sp-flowtext.h"
50 /// #include "sp-flowregion.h"
52 #define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
53 #define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
54 GType sp_flowregion_get_type (void);
55 #define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
56 #define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
57 GType sp_flowtext_get_type (void);
58 // end evil workaround
60 #include "helper/stlport.h"
63 /// \todo fixme: Implement these via preferences */
65 #define NODE_FILL          0xbfbfbf00
66 #define NODE_STROKE        0x000000ff
67 #define NODE_FILL_HI       0xff000000
68 #define NODE_STROKE_HI     0x000000ff
69 #define NODE_FILL_SEL      0x0000ffff
70 #define NODE_STROKE_SEL    0x000000ff
71 #define NODE_FILL_SEL_HI   0xff000000
72 #define NODE_STROKE_SEL_HI 0x000000ff
73 #define KNOT_FILL          0xffffffff
74 #define KNOT_STROKE        0x000000ff
75 #define KNOT_FILL_HI       0xff000000
76 #define KNOT_STROKE_HI     0x000000ff
78 static GMemChunk *nodechunk = NULL;
80 /* Creation from object */
82 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t);
83 static gchar *parse_nodetypes(gchar const *types, gint length);
85 /* Object updating */
87 static void stamp_repr(Inkscape::NodePath::Path *np);
88 static SPCurve *create_curve(Inkscape::NodePath::Path *np);
89 static gchar *create_typestr(Inkscape::NodePath::Path *np);
91 static void sp_node_ensure_ctrls(Inkscape::NodePath::Node *node);
93 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
95 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
97 /* Control knot placement, if node or other knot is moved */
99 static void sp_node_adjust_knot(Inkscape::NodePath::Node *node, gint which_adjust);
100 static void sp_node_adjust_knots(Inkscape::NodePath::Node *node);
102 /* Knot event handlers */
104 static void node_clicked(SPKnot *knot, guint state, gpointer data);
105 static void node_grabbed(SPKnot *knot, guint state, gpointer data);
106 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
107 static gboolean node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
108 static void node_ctrl_clicked(SPKnot *knot, guint state, gpointer data);
109 static void node_ctrl_grabbed(SPKnot *knot, guint state, gpointer data);
110 static void node_ctrl_ungrabbed(SPKnot *knot, guint state, gpointer data);
111 static gboolean node_ctrl_request(SPKnot *knot, NR::Point *p, guint state, gpointer data);
112 static void node_ctrl_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data);
113 static gboolean node_ctrl_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
115 /* Constructors and destructors */
117 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
118 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
119 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
120 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
121 static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
122                                          NR::Point *ppos, NR::Point *pos, NR::Point *npos);
123 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
125 /* Helpers */
127 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
128 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
129 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
131 // active_node indicates mouseover node
132 static Inkscape::NodePath::Node *active_node = NULL;
134 /**
135  * \brief Creates new nodepath from item
136  */
137 Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPItem *item)
139     Inkscape::XML::Node *repr = SP_OBJECT(item)->repr;
141     /** \todo
142      * FIXME: remove this. We don't want to edit paths inside flowtext.
143      * Instead we will build our flowtext with cloned paths, so that the
144      * real paths are outside the flowtext and thus editable as usual.
145      */
146     if (SP_IS_FLOWTEXT(item)) {
147         for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
148             if SP_IS_FLOWREGION(child) {
149                 SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
150                 if (grandchild && SP_IS_PATH(grandchild)) {
151                     item = SP_ITEM(grandchild);
152                     break;
153                 }
154             }
155         }
156     }
158     if (!SP_IS_PATH(item))
159         return NULL;
160     SPPath *path = SP_PATH(item);
161     SPCurve *curve = sp_shape_get_curve(SP_SHAPE(path));
162     if (curve == NULL)
163         return NULL;
165     NArtBpath *bpath = sp_curve_first_bpath(curve);
166     gint length = curve->end;
167     if (length == 0)
168         return NULL; // prevent crash for one-node paths
170     gchar const *nodetypes = repr->attribute("sodipodi:nodetypes");
171     gchar *typestr = parse_nodetypes(nodetypes, length);
173     //Create new nodepath
174     Inkscape::NodePath::Path *np = g_new(Inkscape::NodePath::Path, 1);
175     if (!np)
176         return NULL;
178     // Set defaults
179     np->desktop     = desktop;
180     np->path        = path;
181     np->subpaths    = NULL;
182     np->selected    = NULL;
183     np->nodeContext = NULL; //Let the context that makes this set it
185     // we need to update item's transform from the repr here,
186     // because they may be out of sync when we respond
187     // to a change in repr by regenerating nodepath     --bb
188     sp_object_read_attr(SP_OBJECT(item), "transform");
190     np->i2d  = sp_item_i2d_affine(SP_ITEM(path));
191     np->d2i  = np->i2d.inverse();
192     np->repr = repr;
194     /* Now the bitchy part (lauris) */
196     NArtBpath *b = bpath;
198     while (b->code != NR_END) {
199         b = subpath_from_bpath(np, b, typestr + (b - bpath));
200     }
202     g_free(typestr);
203     sp_curve_unref(curve);
205     return np;
208 /**
209  * Destroys nodepath's subpaths, then itself, also tell context about it.
210  */
211 void sp_nodepath_destroy(Inkscape::NodePath::Path *np) {
213     if (!np)  //soft fail, like delete
214         return;
216     while (np->subpaths) {
217         sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) np->subpaths->data);
218     }
220     //Inform the context that made me, if any, that I am gone.
221     if (np->nodeContext)
222         np->nodeContext->nodepath = NULL;
224     g_assert(!np->selected);
226     np->desktop = NULL;
228     g_free(np);
232 /**
233  *  Return the node count of a given NodeSubPath.
234  */
235 static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
237     if (!subpath)
238         return 0;
239     gint nodeCount = g_list_length(subpath->nodes);
240     return nodeCount;
243 /**
244  *  Return the node count of a given NodePath.
245  */
246 static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
248     if (!np)
249         return 0;
250     gint nodeCount = 0;
251     for (GList *item = np->subpaths ; item ; item=item->next) {
252        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
253         nodeCount += g_list_length(subpath->nodes);
254     }
255     return nodeCount;
259 /**
260  * Clean up a nodepath after editing.
261  *
262  * Currently we are deleting trivial subpaths.
263  */
264 static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
266     GList *badSubPaths = NULL;
268     //Check all subpaths to be >=2 nodes
269     for (GList *l = nodepath->subpaths; l ; l=l->next) {
270        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
271         if (sp_nodepath_subpath_get_node_count(sp)<2)
272             badSubPaths = g_list_append(badSubPaths, sp);
273     }
275     //Delete them.  This second step is because sp_nodepath_subpath_destroy()
276     //also removes the subpath from nodepath->subpaths
277     for (GList *l = badSubPaths; l ; l=l->next) {
278        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
279         sp_nodepath_subpath_destroy(sp);
280     }
282     g_list_free(badSubPaths);
287 /**
288  * \brief Returns true if the argument nodepath and the d attribute in
289  * its repr do not match.
290  *
291  * This may happen if repr was changed in, e.g., XML editor or by undo.
292  *
293  * \todo
294  * UGLY HACK, think how we can eliminate it.
295  */
296 gboolean nodepath_repr_d_changed(Inkscape::NodePath::Path *np, char const *newd)
298     g_assert(np);
300     SPCurve *curve = create_curve(np);
302     gchar *svgpath = sp_svg_write_path(curve->bpath);
304     char const *attr_d = ( newd
305                            ? newd
306                            : SP_OBJECT(np->path)->repr->attribute("d") );
308     gboolean ret;
309     if (attr_d && svgpath)
310         ret = strcmp(attr_d, svgpath);
311     else
312         ret = TRUE;
314     g_free(svgpath);
315     sp_curve_unref(curve);
317     return ret;
320 /**
321  * \brief Returns true if the argument nodepath and the sodipodi:nodetypes
322  * attribute in its repr do not match.
323  *
324  * This may happen if repr was changed in, e.g., the XML editor or by undo.
325  */
326 gboolean nodepath_repr_typestr_changed(Inkscape::NodePath::Path *np, char const *newtypestr)
328     g_assert(np);
329     gchar *typestr = create_typestr(np);
330     char const *attr_typestr = ( newtypestr
331                                  ? newtypestr
332                                  : SP_OBJECT(np->path)->repr->attribute("sodipodi:nodetypes") );
333     gboolean const ret = (attr_typestr && strcmp(attr_typestr, typestr));
335     g_free(typestr);
337     return ret;
340 /**
341  * Create new nodepath from b, make it subpath of np.
342  * \param t The node type.
343  * \todo Fixme: t should be a proper type, rather than gchar
344  */
345 static NArtBpath *subpath_from_bpath(Inkscape::NodePath::Path *np, NArtBpath *b, gchar const *t)
347     NR::Point ppos, pos, npos;
349     g_assert((b->code == NR_MOVETO) || (b->code == NR_MOVETO_OPEN));
351     Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
352     bool const closed = (b->code == NR_MOVETO);
354     pos = NR::Point(b->x3, b->y3) * np->i2d;
355     if (b[1].code == NR_CURVETO) {
356         npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
357     } else {
358         npos = pos;
359     }
360     Inkscape::NodePath::Node *n;
361     n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType) *t, NR_MOVETO, &pos, &pos, &npos);
362     g_assert(sp->first == n);
363     g_assert(sp->last  == n);
365     b++;
366     t++;
367     while ((b->code == NR_CURVETO) || (b->code == NR_LINETO)) {
368         pos = NR::Point(b->x3, b->y3) * np->i2d;
369         if (b->code == NR_CURVETO) {
370             ppos = NR::Point(b->x2, b->y2) * np->i2d;
371         } else {
372             ppos = pos;
373         }
374         if (b[1].code == NR_CURVETO) {
375             npos = NR::Point(b[1].x1, b[1].y1) * np->i2d;
376         } else {
377             npos = pos;
378         }
379         n = sp_nodepath_node_new(sp, NULL, (Inkscape::NodePath::NodeType)*t, b->code, &ppos, &pos, &npos);
380         b++;
381         t++;
382     }
384     if (closed) sp_nodepath_subpath_close(sp);
386     return b;
389 /**
390  * Convert from sodipodi:nodetypes to new style type string.
391  */
392 static gchar *parse_nodetypes(gchar const *types, gint length)
394     g_assert(length > 0);
396     gchar *typestr = g_new(gchar, length + 1);
398     gint pos = 0;
400     if (types) {
401         for (gint i = 0; types[i] && ( i < length ); i++) {
402             while ((types[i] > '\0') && (types[i] <= ' ')) i++;
403             if (types[i] != '\0') {
404                 switch (types[i]) {
405                     case 's':
406                         typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
407                         break;
408                     case 'z':
409                         typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
410                         break;
411                     case 'c':
412                         typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
413                         break;
414                     default:
415                         typestr[pos++] =Inkscape::NodePath::NODE_NONE;
416                         break;
417                 }
418             }
419         }
420     }
422     while (pos < length) typestr[pos++] =Inkscape::NodePath::NODE_NONE;
424     return typestr;
427 /**
428  * Make curve out of path and associate it with it.
429  */
430 static void update_object(Inkscape::NodePath::Path *np)
432     g_assert(np);
434     SPCurve *curve = create_curve(np);
436     sp_shape_set_curve(SP_SHAPE(np->path), curve, TRUE);
438     sp_curve_unref(curve);
441 /**
442  * Update XML path node with data from path object.
443  */
444 static void update_repr_internal(Inkscape::NodePath::Path *np)
446     g_assert(np);
448     Inkscape::XML::Node *repr = SP_OBJECT(np->path)->repr;
450     SPCurve *curve = create_curve(np);
451     gchar *typestr = create_typestr(np);
452     gchar *svgpath = sp_svg_write_path(curve->bpath);
454     repr->setAttribute("d", svgpath);
455     repr->setAttribute("sodipodi:nodetypes", typestr);
457     g_free(svgpath);
458     g_free(typestr);
459     sp_curve_unref(curve);
462 /**
463  * Update XML path node with data from path object, commit changes forever.
464  */
465 static void update_repr(Inkscape::NodePath::Path *np)
467     update_repr_internal(np);
468     sp_document_done(SP_DT_DOCUMENT(np->desktop));
471 /**
472  * Update XML path node with data from path object, commit changes with undo.
473  */
474 static void update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key)
476     update_repr_internal(np);
477     sp_document_maybe_done(SP_DT_DOCUMENT(np->desktop), key);
480 /**
481  * Make duplicate of path, replace corresponding XML node in tree, commit.
482  */
483 static void stamp_repr(Inkscape::NodePath::Path *np)
485     g_assert(np);
487     Inkscape::XML::Node *old_repr = SP_OBJECT(np->path)->repr;
488     Inkscape::XML::Node *new_repr = old_repr->duplicate();
490     // remember the position of the item
491     gint pos = old_repr->position();
492     // remember parent
493     Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
495     SPCurve *curve = create_curve(np);
496     gchar *typestr = create_typestr(np);
498     gchar *svgpath = sp_svg_write_path(curve->bpath);
500     new_repr->setAttribute("d", svgpath);
501     new_repr->setAttribute("sodipodi:nodetypes", typestr);
503     // add the new repr to the parent
504     parent->appendChild(new_repr);
505     // move to the saved position
506     new_repr->setPosition(pos > 0 ? pos : 0);
508     sp_document_done(SP_DT_DOCUMENT(np->desktop));
510     Inkscape::GC::release(new_repr);
511     g_free(svgpath);
512     g_free(typestr);
513     sp_curve_unref(curve);
516 /**
517  * Create curve from path.
518  */
519 static SPCurve *create_curve(Inkscape::NodePath::Path *np)
521     SPCurve *curve = sp_curve_new();
523     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
524        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
525         sp_curve_moveto(curve,
526                         sp->first->pos * np->d2i);
527        Inkscape::NodePath::Node *n = sp->first->n.other;
528         while (n) {
529             NR::Point const end_pt = n->pos * np->d2i;
530             switch (n->code) {
531                 case NR_LINETO:
532                     sp_curve_lineto(curve, end_pt);
533                     break;
534                 case NR_CURVETO:
535                     sp_curve_curveto(curve,
536                                      n->p.other->n.pos * np->d2i,
537                                      n->p.pos * np->d2i,
538                                      end_pt);
539                     break;
540                 default:
541                     g_assert_not_reached();
542                     break;
543             }
544             if (n != sp->last) {
545                 n = n->n.other;
546             } else {
547                 n = NULL;
548             }
549         }
550         if (sp->closed) {
551             sp_curve_closepath(curve);
552         }
553     }
555     return curve;
558 /**
559  * Convert path type string to sodipodi:nodetypes style.
560  */
561 static gchar *create_typestr(Inkscape::NodePath::Path *np)
563     gchar *typestr = g_new(gchar, 32);
564     gint len = 32;
565     gint pos = 0;
567     for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
568        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
570         if (pos >= len) {
571             typestr = g_renew(gchar, typestr, len + 32);
572             len += 32;
573         }
575         typestr[pos++] = 'c';
577        Inkscape::NodePath::Node *n;
578         n = sp->first->n.other;
579         while (n) {
580             gchar code;
582             switch (n->type) {
583                 case Inkscape::NodePath::NODE_CUSP:
584                     code = 'c';
585                     break;
586                 case Inkscape::NodePath::NODE_SMOOTH:
587                     code = 's';
588                     break;
589                 case Inkscape::NodePath::NODE_SYMM:
590                     code = 'z';
591                     break;
592                 default:
593                     g_assert_not_reached();
594                     code = '\0';
595                     break;
596             }
598             if (pos >= len) {
599                 typestr = g_renew(gchar, typestr, len + 32);
600                 len += 32;
601             }
603             typestr[pos++] = code;
605             if (n != sp->last) {
606                 n = n->n.other;
607             } else {
608                 n = NULL;
609             }
610         }
611     }
613     if (pos >= len) {
614         typestr = g_renew(gchar, typestr, len + 1);
615         len += 1;
616     }
618     typestr[pos++] = '\0';
620     return typestr;
623 /**
624  * Returns current path in context.
625  */
626 static Inkscape::NodePath::Path *sp_nodepath_current()
628     if (!SP_ACTIVE_DESKTOP) {
629         return NULL;
630     }
632     SPEventContext *event_context = (SP_ACTIVE_DESKTOP)->event_context;
634     if (!SP_IS_NODE_CONTEXT(event_context)) {
635         return NULL;
636     }
638     return SP_NODE_CONTEXT(event_context)->nodepath;
643 /**
644  \brief Fills node and control positions for three nodes, splitting line
645   marked by end at distance t.
646  */
647 static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
649     g_assert(new_path != NULL);
650     g_assert(end      != NULL);
652     g_assert(end->p.other == new_path);
653    Inkscape::NodePath::Node *start = new_path->p.other;
654     g_assert(start);
656     if (end->code == NR_LINETO) {
657         new_path->type =Inkscape::NodePath::NODE_CUSP;
658         new_path->code = NR_LINETO;
659         new_path->pos  = (t * start->pos + (1 - t) * end->pos);
660     } else {
661         new_path->type =Inkscape::NodePath::NODE_SMOOTH;
662         new_path->code = NR_CURVETO;
663         gdouble s      = 1 - t;
664         for (int dim = 0; dim < 2; dim++) {
665             NR::Coord const f000 = start->pos[dim];
666             NR::Coord const f001 = start->n.pos[dim];
667             NR::Coord const f011 = end->p.pos[dim];
668             NR::Coord const f111 = end->pos[dim];
669             NR::Coord const f00t = s * f000 + t * f001;
670             NR::Coord const f01t = s * f001 + t * f011;
671             NR::Coord const f11t = s * f011 + t * f111;
672             NR::Coord const f0tt = s * f00t + t * f01t;
673             NR::Coord const f1tt = s * f01t + t * f11t;
674             NR::Coord const fttt = s * f0tt + t * f1tt;
675             start->n.pos[dim]    = f00t;
676             new_path->p.pos[dim] = f0tt;
677             new_path->pos[dim]   = fttt;
678             new_path->n.pos[dim] = f1tt;
679             end->p.pos[dim]      = f11t;
680         }
681     }
684 /**
685  * Adds new node on direct line between two nodes, activates handles of all
686  * three nodes.
687  */
688 static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
690     g_assert(end);
691     g_assert(end->subpath);
692     g_assert(g_list_find(end->subpath->nodes, end));
694    Inkscape::NodePath::Node *start = end->p.other;
695     g_assert( start->n.other == end );
696    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
697                                                end,
698                                               Inkscape::NodePath::NODE_SMOOTH,
699                                                (NRPathcode)end->code,
700                                                &start->pos, &start->pos, &start->n.pos);
701     sp_nodepath_line_midpoint(newnode, end, t);
703     sp_node_ensure_ctrls(start);
704     sp_node_ensure_ctrls(newnode);
705     sp_node_ensure_ctrls(end);
707     return newnode;
710 /**
711 \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
712 */
713 static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
715     g_assert(node);
716     g_assert(node->subpath);
717     g_assert(g_list_find(node->subpath->nodes, node));
719    Inkscape::NodePath::SubPath *sp = node->subpath;
720     Inkscape::NodePath::Path *np    = sp->nodepath;
722     if (sp->closed) {
723         sp_nodepath_subpath_open(sp, node);
724         return sp->first;
725     } else {
726         // no break for end nodes
727         if (node == sp->first) return NULL;
728         if (node == sp->last ) return NULL;
730         // create a new subpath
731        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
733         // duplicate the break node as start of the new subpath
734        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL, (Inkscape::NodePath::NodeType)node->type, NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
736         while (node->n.other) { // copy the remaining nodes into the new subpath
737            Inkscape::NodePath::Node *n  = node->n.other;
738            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);
739             if (n->selected) {
740                 sp_nodepath_node_select(nn, TRUE, TRUE); //preserve selection
741             }
742             sp_nodepath_node_destroy(n); // remove the point on the original subpath
743         }
745         return newnode;
746     }
749 /**
750  * Duplicate node and connect to neighbours.
751  */
752 static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
754     g_assert(node);
755     g_assert(node->subpath);
756     g_assert(g_list_find(node->subpath->nodes, node));
758    Inkscape::NodePath::SubPath *sp = node->subpath;
760     NRPathcode code = (NRPathcode) node->code;
761     if (code == NR_MOVETO) { // if node is the endnode,
762         node->code = NR_LINETO; // new one is inserted before it, so change that to line
763     }
765     Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
767     if (!node->n.other || !node->p.other) // if node is an endnode, select it
768         return node;
769     else
770         return newnode; // otherwise select the newly created node
773 static void sp_node_control_mirror_n_to_p(Inkscape::NodePath::Node *node)
775     node->p.pos = (node->pos + (node->pos - node->n.pos));
778 static void sp_node_control_mirror_p_to_n(Inkscape::NodePath::Node *node)
780     node->n.pos = (node->pos + (node->pos - node->p.pos));
783 /**
784  * Change line type at node, with side effects on neighbours.
785  */
786 static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
788     g_assert(end);
789     g_assert(end->subpath);
790     g_assert(end->p.other);
792     if (end->code == static_cast< guint > ( code ) )
793         return;
795    Inkscape::NodePath::Node *start = end->p.other;
797     end->code = code;
799     if (code == NR_LINETO) {
800         if (start->code == NR_LINETO) start->type =Inkscape::NodePath::NODE_CUSP;
801         if (end->n.other) {
802             if (end->n.other->code == NR_LINETO) end->type =Inkscape::NodePath::NODE_CUSP;
803         }
804         sp_node_adjust_knot(start, -1);
805         sp_node_adjust_knot(end, 1);
806     } else {
807         NR::Point delta = end->pos - start->pos;
808         start->n.pos = start->pos + delta / 3;
809         end->p.pos = end->pos - delta / 3;
810         sp_node_adjust_knot(start, 1);
811         sp_node_adjust_knot(end, -1);
812     }
814     sp_node_ensure_ctrls(start);
815     sp_node_ensure_ctrls(end);
818 /**
819  * Change node type, and its handles accordingly.
820  */
821 static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeType type)
823     g_assert(node);
824     g_assert(node->subpath);
826     if (type == static_cast<Inkscape::NodePath::NodeType>(static_cast< guint >(node->type) ) )
827         return node;
829     if ((node->p.other != NULL) && (node->n.other != NULL)) {
830         if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
831             type =Inkscape::NodePath::NODE_CUSP;
832         }
833     }
835     node->type = type;
837     if (node->type == Inkscape::NodePath::NODE_CUSP) {
838         g_object_set(G_OBJECT(node->knot), "shape", SP_KNOT_SHAPE_DIAMOND, "size", 9, NULL);
839     } else {
840         g_object_set(G_OBJECT(node->knot), "shape", SP_KNOT_SHAPE_SQUARE, "size", 7, NULL);
841     }
843     sp_node_adjust_knots(node);
845     sp_nodepath_update_statusbar(node->subpath->nodepath);
847     return node;
850 /**
851  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
852  * adjacent segments from lines to curves.
853 */
854 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
856     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
857         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
858             // convert adjacent segment BEFORE to curve
859             node->code = NR_CURVETO;
860             NR::Point delta;
861             if (node->n.other != NULL)
862                 delta = node->n.other->pos - node->p.other->pos;
863             else
864                 delta = node->pos - node->p.other->pos;
865             node->p.pos = node->pos - delta / 4;
866             sp_node_ensure_ctrls(node);
867         }
869         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
870             // convert adjacent segment AFTER to curve
871             node->n.other->code = NR_CURVETO;
872             NR::Point delta;
873             if (node->p.other != NULL)
874                 delta = node->p.other->pos - node->n.other->pos;
875             else
876                 delta = node->pos - node->n.other->pos;
877             node->n.pos = node->pos - delta / 4;
878             sp_node_ensure_ctrls(node);
879         }
880     }
882     sp_nodepath_set_node_type (node, type);
885 /**
886  * Move node to point, and adjust its and neighbouring handles.
887  */
888 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
890     NR::Point delta = p - node->pos;
891     node->pos = p;
893     node->p.pos += delta;
894     node->n.pos += delta;
896     if (node->p.other) {
897         if (node->code == NR_LINETO) {
898             sp_node_adjust_knot(node, 1);
899             sp_node_adjust_knot(node->p.other, -1);
900         }
901     }
902     if (node->n.other) {
903         if (node->n.other->code == NR_LINETO) {
904             sp_node_adjust_knot(node, -1);
905             sp_node_adjust_knot(node->n.other, 1);
906         }
907     }
909     sp_node_ensure_ctrls(node);
912 /**
913  * Call sp_node_moveto() for node selection and handle possible snapping.
914  */
915 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
916                                             bool const snap = true)
918     NR::Coord best[2] = { NR_HUGE, NR_HUGE };
919     NR::Point delta(dx, dy);
920     NR::Point best_pt = delta;
922     if (snap) {
923         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
924            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
925             NR::Point p = n->pos + delta;
926             for (int dim = 0; dim < 2; dim++) {
927                 NR::Coord dist = namedview_dim_snap(nodepath->desktop->namedview,
928                                                     Inkscape::Snapper::SNAP_POINT, p,
929                                                     NR::Dim2(dim), nodepath->path);
930                 if (dist < best[dim]) {
931                     best[dim] = dist;
932                     best_pt[dim] = p[dim] - n->pos[dim];
933                 }
934             }
935         }
936     }
938     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
939        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
940         sp_node_moveto(n, n->pos + best_pt);
941     }
943     update_object(nodepath);
946 /**
947  * Move node selection to point, adjust its and neighbouring handles,
948  * handle possible snapping, and commit the change with possible undo.
949  */
950 void
951 sp_node_selected_move(gdouble dx, gdouble dy)
953     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
954     if (!nodepath) return;
956     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
958     if (dx == 0) {
959         update_repr_keyed(nodepath, "node:move:vertical");
960     } else if (dy == 0) {
961         update_repr_keyed(nodepath, "node:move:horizontal");
962     } else {
963         update_repr(nodepath);
964     }
967 /**
968  * Move node selection off screen and commit the change.
969  */
970 void
971 sp_node_selected_move_screen(gdouble dx, gdouble dy)
973     // borrowed from sp_selection_move_screen in selection-chemistry.c
974     // we find out the current zoom factor and divide deltas by it
975     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
977     gdouble zoom = desktop->current_zoom();
978     gdouble zdx = dx / zoom;
979     gdouble zdy = dy / zoom;
981     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
982     if (!nodepath) return;
984     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
986     if (dx == 0) {
987         update_repr_keyed(nodepath, "node:move:vertical");
988     } else if (dy == 0) {
989         update_repr_keyed(nodepath, "node:move:horizontal");
990     } else {
991         update_repr(nodepath);
992     }
995 /** If they don't yet exist, creates knot and line for the given side of the node */
996 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
998     if (!side->knot) {
999         side->knot = sp_knot_new(desktop);
1000         g_object_set(G_OBJECT(side->knot),
1001                      "shape", SP_KNOT_SHAPE_CIRCLE,
1002                      "size", 7,
1003                      "anchor", GTK_ANCHOR_CENTER,
1004                      "fill", KNOT_FILL,
1005                      "fill_mouseover", KNOT_FILL_HI,
1006                      "stroke", KNOT_STROKE,
1007                      "stroke_mouseover", KNOT_STROKE_HI,
1008                      "tip", _("<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"),
1009                      NULL);
1010         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_ctrl_clicked), node);
1011         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_ctrl_grabbed), node);
1012         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_ctrl_ungrabbed), node);
1013         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_ctrl_request), node);
1014         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_ctrl_moved), node);
1015         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_ctrl_event), node);
1016     }
1018     if (!side->line) {
1019         side->line = sp_canvas_item_new(SP_DT_CONTROLS(desktop),
1020                                         SP_TYPE_CTRLLINE, NULL);
1021     }
1024 /**
1025  * Ensure knot on side of node is visible/invisible.
1026  */
1027 static void sp_node_ensure_knot(Inkscape::NodePath::Node *node, gint which, gboolean show_knot)
1029     g_assert(node != NULL);
1031    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1032     NRPathcode code = sp_node_path_code_from_side(node, side);
1034     show_knot = show_knot && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1036     if (show_knot) {
1037         sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1039         if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1040             sp_knot_show(side->knot);
1041         }
1042         sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1043         sp_canvas_item_show(side->line);
1044     } else {
1045         if (side->knot) {
1046             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1047                 sp_knot_hide(side->knot);
1048             }
1049         }
1050         if (side->line) {
1051             sp_canvas_item_hide(side->line);
1052         }
1053     }
1056 /**
1057  * Ensure handles on node and neighbours of node are visible if selected.
1058  */
1059 static void sp_node_ensure_ctrls(Inkscape::NodePath::Node *node)
1061     g_assert(node != NULL);
1063     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1064         sp_knot_show(node->knot);
1065     }
1067     sp_knot_set_position(node->knot, &node->pos, 0);
1069     gboolean show_knots = node->selected;
1070     if (node->p.other != NULL) {
1071         if (node->p.other->selected) show_knots = TRUE;
1072     }
1073     if (node->n.other != NULL) {
1074         if (node->n.other->selected) show_knots = TRUE;
1075     }
1077     sp_node_ensure_knot(node, -1, show_knots);
1078     sp_node_ensure_knot(node, 1, show_knots);
1081 /**
1082  * Call sp_node_ensure_ctrls() for all nodes on subpath.
1083  */
1084 static void sp_nodepath_subpath_ensure_ctrls(Inkscape::NodePath::SubPath *subpath)
1086     g_assert(subpath != NULL);
1088     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1089         sp_node_ensure_ctrls((Inkscape::NodePath::Node *) l->data);
1090     }
1093 /**
1094  * Call sp_nodepath_subpath_ensure_ctrls() for all subpaths of nodepath.
1095  */
1096 static void sp_nodepath_ensure_ctrls(Inkscape::NodePath::Path *nodepath)
1098     g_assert(nodepath != NULL);
1100     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1101         sp_nodepath_subpath_ensure_ctrls((Inkscape::NodePath::SubPath *) l->data);
1102     }
1105 /**
1106  * Adds all selected nodes in nodepath to list.
1107  */
1108 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1110     StlConv<Node *>::list(l, selected);
1111 /// \todo this adds a copying, rework when the selection becomes a stl list
1114 /**
1115  * Align selected nodes on the specified axis.
1116  */
1117 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1119     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1120         return;
1121     }
1123     if ( !nodepath->selected->next ) { // only one node selected
1124         return;
1125     }
1126    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1127     NR::Point dest(pNode->pos);
1128     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1129         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1130         if (pNode) {
1131             dest[axis] = pNode->pos[axis];
1132             sp_node_moveto(pNode, dest);
1133         }
1134     }
1135     if (axis == NR::X) {
1136         update_repr_keyed(nodepath, "node:move:vertical");
1137     } else {
1138         update_repr_keyed(nodepath, "node:move:horizontal");
1139     }
1142 /// Helper struct.
1143 struct NodeSort
1145    Inkscape::NodePath::Node *_node;
1146     NR::Coord _coord;
1147     /// \todo use vectorof pointers instead of calling copy ctor
1148     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1149         _node(node), _coord(node->pos[axis])
1150     {}
1152 };
1154 static bool operator<(NodeSort const &a, NodeSort const &b)
1156     return (a._coord < b._coord);
1159 /**
1160  * Distribute selected nodes on the specified axis.
1161  */
1162 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1164     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1165         return;
1166     }
1168     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1169         return;
1170     }
1172    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1173     std::vector<NodeSort> sorted;
1174     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1175         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1176         if (pNode) {
1177             NodeSort n(pNode, axis);
1178             sorted.push_back(n);
1179             //dest[axis] = pNode->pos[axis];
1180             //sp_node_moveto(pNode, dest);
1181         }
1182     }
1183     std::sort(sorted.begin(), sorted.end());
1184     unsigned int len = sorted.size();
1185     //overall bboxes span
1186     float dist = (sorted.back()._coord -
1187                   sorted.front()._coord);
1188     //new distance between each bbox
1189     float step = (dist) / (len - 1);
1190     float pos = sorted.front()._coord;
1191     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1192           it < sorted.end();
1193           it ++ )
1194     {
1195         NR::Point dest((*it)._node->pos);
1196         dest[axis] = pos;
1197         sp_node_moveto((*it)._node, dest);
1198         pos += step;
1199     }
1201     if (axis == NR::X) {
1202         update_repr_keyed(nodepath, "node:move:horizontal");
1203     } else {
1204         update_repr_keyed(nodepath, "node:move:vertical");
1205     }
1209 /**
1210  * Call sp_nodepath_line_add_node() for all selected segments.
1211  */
1212 void
1213 sp_node_selected_add_node(void)
1215     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1216     if (!nodepath) {
1217         return;
1218     }
1220     GList *nl = NULL;
1222     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1223        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1224         g_assert(t->selected);
1225         if (t->p.other && t->p.other->selected) {
1226             nl = g_list_prepend(nl, t);
1227         }
1228     }
1230     while (nl) {
1231        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1232        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1233         sp_nodepath_node_select(n, TRUE, FALSE);
1234         nl = g_list_remove(nl, t);
1235     }
1237     /** \todo fixme: adjust ? */
1238     sp_nodepath_ensure_ctrls(nodepath);
1240     update_repr(nodepath);
1242     sp_nodepath_update_statusbar(nodepath);
1245 /**
1246  * Select segment nearest to point
1247  */
1248 void
1249 sp_nodepath_select_segment_near_point(SPItem * item, NR::Point p, bool toggle)
1251     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1252     if (!nodepath) {
1253         return;
1254     }
1256     Path::cut_position position = get_nearest_position_on_Path(item, p);
1258     //find segment to segment
1259     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1261     gboolean force = FALSE;
1262     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1263         force = TRUE;
1264     }
1265     sp_nodepath_node_select(e, (gboolean) toggle, force);
1266     if (e->p.other)
1267         sp_nodepath_node_select(e->p.other, TRUE, force);
1269     sp_nodepath_ensure_ctrls(nodepath);
1271     sp_nodepath_update_statusbar(nodepath);
1274 /**
1275  * Add a node nearest to point
1276  */
1277 void
1278 sp_nodepath_add_node_near_point(SPItem * item, NR::Point p)
1280     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1281     if (!nodepath) {
1282         return;
1283     }
1285     Path::cut_position position = get_nearest_position_on_Path(item, p);
1287     //find segment to split
1288     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1290     //don't know why but t seems to flip for lines
1291     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1292         position.t = 1.0 - position.t;
1293     }
1294     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1295     sp_nodepath_node_select(n, FALSE, TRUE);
1297     /* fixme: adjust ? */
1298     sp_nodepath_ensure_ctrls(nodepath);
1300     update_repr(nodepath);
1302     sp_nodepath_update_statusbar(nodepath);
1305 /*
1306  * Adjusts a segment so that t moves by a certain delta for dragging
1307  * converts lines to curves
1308  *
1309  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1310  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1311  */
1312 void
1313 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta, char * key)
1315     /* feel good is an arbitrary parameter that distributes the delta between handles
1316      * if t of the drag point is less than 1/6 distance form the endpoint only
1317      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1318      */
1319     double feel_good;
1320     if (t <= 1.0 / 6.0)
1321         feel_good = 0;
1322     else if (t <= 0.5)
1323         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1324     else if (t <= 5.0 / 6.0)
1325         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1326     else
1327         feel_good = 1;
1329     //if we're dragging a line convert it to a curve
1330     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1331         sp_nodepath_set_line_type(e, NR_CURVETO);
1332     }
1334     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1335     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1336     e->p.other->n.pos += offsetcoord0;
1337     e->p.pos += offsetcoord1;
1339     // adjust controls of adjacent segments where necessary
1340     sp_node_adjust_knot(e,1);
1341     sp_node_adjust_knot(e->p.other,-1);
1343     sp_nodepath_ensure_ctrls(e->subpath->nodepath);
1345     update_repr_keyed(e->subpath->nodepath, key);
1347     sp_nodepath_update_statusbar(e->subpath->nodepath);
1351 /**
1352  * Call sp_nodepath_break() for all selected segments.
1353  */
1354 void sp_node_selected_break()
1356     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1357     if (!nodepath) return;
1359     GList *temp = NULL;
1360     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1361        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1362        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1363         if (nn == NULL) continue; // no break, no new node
1364         temp = g_list_prepend(temp, nn);
1365     }
1367     if (temp) {
1368         sp_nodepath_deselect(nodepath);
1369     }
1370     for (GList *l = temp; l != NULL; l = l->next) {
1371         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1372     }
1374     sp_nodepath_ensure_ctrls(nodepath);
1376     update_repr(nodepath);
1379 /**
1380  * Duplicate the selected node(s).
1381  */
1382 void sp_node_selected_duplicate()
1384     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1385     if (!nodepath) {
1386         return;
1387     }
1389     GList *temp = NULL;
1390     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1391        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1392        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1393         if (nn == NULL) continue; // could not duplicate
1394         temp = g_list_prepend(temp, nn);
1395     }
1397     if (temp) {
1398         sp_nodepath_deselect(nodepath);
1399     }
1400     for (GList *l = temp; l != NULL; l = l->next) {
1401         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1402     }
1404     sp_nodepath_ensure_ctrls(nodepath);
1406     update_repr(nodepath);
1409 /**
1410  *  Join two nodes by merging them into one.
1411  */
1412 void sp_node_selected_join()
1414     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1415     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1417     if (g_list_length(nodepath->selected) != 2) {
1418         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1419         return;
1420     }
1422    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1423    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1425     g_assert(a != b);
1426     g_assert(a->p.other || a->n.other);
1427     g_assert(b->p.other || b->n.other);
1429     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1430         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1431         return;
1432     }
1434     /* a and b are endpoints */
1436     NR::Point c = (a->pos + b->pos) / 2;
1438     if (a->subpath == b->subpath) {
1439        Inkscape::NodePath::SubPath *sp = a->subpath;
1440         sp_nodepath_subpath_close(sp);
1442         sp_nodepath_ensure_ctrls(sp->nodepath);
1444         update_repr(nodepath);
1446         return;
1447     }
1449     /* a and b are separate subpaths */
1450    Inkscape::NodePath::SubPath *sa = a->subpath;
1451    Inkscape::NodePath::SubPath *sb = b->subpath;
1452     NR::Point p;
1453    Inkscape::NodePath::Node *n;
1454     NRPathcode code;
1455     if (a == sa->first) {
1456         p = sa->first->n.pos;
1457         code = (NRPathcode)sa->first->n.other->code;
1458        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1459         n = sa->last;
1460         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1461         n = n->p.other;
1462         while (n) {
1463             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1464             n = n->p.other;
1465             if (n == sa->first) n = NULL;
1466         }
1467         sp_nodepath_subpath_destroy(sa);
1468         sa = t;
1469     } else if (a == sa->last) {
1470         p = sa->last->p.pos;
1471         code = (NRPathcode)sa->last->code;
1472         sp_nodepath_node_destroy(sa->last);
1473     } else {
1474         code = NR_END;
1475         g_assert_not_reached();
1476     }
1478     if (b == sb->first) {
1479         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1480         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1481             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1482         }
1483     } else if (b == sb->last) {
1484         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1485         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1486             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1487         }
1488     } else {
1489         g_assert_not_reached();
1490     }
1491     /* and now destroy sb */
1493     sp_nodepath_subpath_destroy(sb);
1495     sp_nodepath_ensure_ctrls(sa->nodepath);
1497     update_repr(nodepath);
1499     sp_nodepath_update_statusbar(nodepath);
1502 /**
1503  *  Join two nodes by adding a segment between them.
1504  */
1505 void sp_node_selected_join_segment()
1507     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1508     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1510     if (g_list_length(nodepath->selected) != 2) {
1511         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1512         return;
1513     }
1515    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1516    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1518     g_assert(a != b);
1519     g_assert(a->p.other || a->n.other);
1520     g_assert(b->p.other || b->n.other);
1522     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1523         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1524         return;
1525     }
1527     if (a->subpath == b->subpath) {
1528        Inkscape::NodePath::SubPath *sp = a->subpath;
1530         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1531         sp->closed = TRUE;
1533         sp->first->p.other = sp->last;
1534         sp->last->n.other  = sp->first;
1536         sp_node_control_mirror_p_to_n(sp->last);
1537         sp_node_control_mirror_n_to_p(sp->first);
1539         sp->first->code = sp->last->code;
1540         sp->first       = sp->last;
1542         sp_nodepath_ensure_ctrls(sp->nodepath);
1544         update_repr(nodepath);
1546         return;
1547     }
1549     /* a and b are separate subpaths */
1550    Inkscape::NodePath::SubPath *sa = a->subpath;
1551    Inkscape::NodePath::SubPath *sb = b->subpath;
1553    Inkscape::NodePath::Node *n;
1554     NR::Point p;
1555     NRPathcode code;
1556     if (a == sa->first) {
1557         code = (NRPathcode) sa->first->n.other->code;
1558        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1559         n = sa->last;
1560         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1561         for (n = n->p.other; n != NULL; n = n->p.other) {
1562             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1563         }
1564         sp_nodepath_subpath_destroy(sa);
1565         sa = t;
1566     } else if (a == sa->last) {
1567         code = (NRPathcode)sa->last->code;
1568     } else {
1569         code = NR_END;
1570         g_assert_not_reached();
1571     }
1573     if (b == sb->first) {
1574         n = sb->first;
1575         sp_node_control_mirror_p_to_n(sa->last);
1576         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1577         sp_node_control_mirror_n_to_p(sa->last);
1578         for (n = n->n.other; n != NULL; n = n->n.other) {
1579             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1580         }
1581     } else if (b == sb->last) {
1582         n = sb->last;
1583         sp_node_control_mirror_p_to_n(sa->last);
1584         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1585         sp_node_control_mirror_n_to_p(sa->last);
1586         for (n = n->p.other; n != NULL; n = n->p.other) {
1587             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1588         }
1589     } else {
1590         g_assert_not_reached();
1591     }
1592     /* and now destroy sb */
1594     sp_nodepath_subpath_destroy(sb);
1596     sp_nodepath_ensure_ctrls(sa->nodepath);
1598     update_repr(nodepath);
1601 /**
1602  * Delete one or more selected nodes.
1603  */
1604 void sp_node_selected_delete()
1606     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1607     if (!nodepath) return;
1608     if (!nodepath->selected) return;
1610     /** \todo fixme: do it the right way */
1611     while (nodepath->selected) {
1612        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1613         sp_nodepath_node_destroy(node);
1614     }
1617     //clean up the nodepath (such as for trivial subpaths)
1618     sp_nodepath_cleanup(nodepath);
1620     sp_nodepath_ensure_ctrls(nodepath);
1622     // if the entire nodepath is removed, delete the selected object.
1623     if (nodepath->subpaths == NULL ||
1624         sp_nodepath_get_node_count(nodepath) < 2) {
1625         SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
1626         sp_nodepath_destroy(nodepath);
1627         sp_selection_delete();
1628         sp_document_done (document);
1629         return;
1630     }
1632     update_repr(nodepath);
1634     sp_nodepath_update_statusbar(nodepath);
1637 /**
1638  * Delete one or more segments between two selected nodes.
1639  * This is the code for 'split'.
1640  */
1641 void
1642 sp_node_selected_delete_segment(void)
1644    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
1645    Inkscape::NodePath::Node *curr, *next;     //Iterators
1647     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1648     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1650     if (g_list_length(nodepath->selected) != 2) {
1651         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1652                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1653         return;
1654     }
1656     //Selected nodes, not inclusive
1657    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1658    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1660     if ( ( a==b)                       ||  //same node
1661          (a->subpath  != b->subpath )  ||  //not the same path
1662          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
1663          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
1664     {
1665         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1666                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1667         return;
1668     }
1670     //###########################################
1671     //# BEGIN EDITS
1672     //###########################################
1673     //##################################
1674     //# CLOSED PATH
1675     //##################################
1676     if (a->subpath->closed) {
1679         gboolean reversed = FALSE;
1681         //Since we can go in a circle, we need to find the shorter distance.
1682         //  a->b or b->a
1683         start = end = NULL;
1684         int distance    = 0;
1685         int minDistance = 0;
1686         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1687             if (curr==b) {
1688                 //printf("a to b:%d\n", distance);
1689                 start = a;//go from a to b
1690                 end   = b;
1691                 minDistance = distance;
1692                 //printf("A to B :\n");
1693                 break;
1694             }
1695             distance++;
1696         }
1698         //try again, the other direction
1699         distance = 0;
1700         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1701             if (curr==a) {
1702                 //printf("b to a:%d\n", distance);
1703                 if (distance < minDistance) {
1704                     start    = b;  //we go from b to a
1705                     end      = a;
1706                     reversed = TRUE;
1707                     //printf("B to A\n");
1708                 }
1709                 break;
1710             }
1711             distance++;
1712         }
1715         //Copy everything from 'end' to 'start' to a new subpath
1716        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1717         for (curr=end ; curr ; curr=curr->n.other) {
1718             NRPathcode code = (NRPathcode) curr->code;
1719             if (curr == end)
1720                 code = NR_MOVETO;
1721             sp_nodepath_node_new(t, NULL,
1722                                  (Inkscape::NodePath::NodeType)curr->type, code,
1723                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1724             if (curr == start)
1725                 break;
1726         }
1727         sp_nodepath_subpath_destroy(a->subpath);
1730     }
1734     //##################################
1735     //# OPEN PATH
1736     //##################################
1737     else {
1739         //We need to get the direction of the list between A and B
1740         //Can we walk from a to b?
1741         start = end = NULL;
1742         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1743             if (curr==b) {
1744                 start = a;  //did it!  we go from a to b
1745                 end   = b;
1746                 //printf("A to B\n");
1747                 break;
1748             }
1749         }
1750         if (!start) {//didn't work?  let's try the other direction
1751             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1752                 if (curr==a) {
1753                     start = b;  //did it!  we go from b to a
1754                     end   = a;
1755                     //printf("B to A\n");
1756                     break;
1757                 }
1758             }
1759         }
1760         if (!start) {
1761             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1762                                                      _("Cannot find path between nodes."));
1763             return;
1764         }
1768         //Copy everything after 'end' to a new subpath
1769        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1770         for (curr=end ; curr ; curr=curr->n.other) {
1771             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
1772                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1773         }
1775         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
1776         for (curr = start->n.other ; curr  ; curr=next) {
1777             next = curr->n.other;
1778             sp_nodepath_node_destroy(curr);
1779         }
1781     }
1782     //###########################################
1783     //# END EDITS
1784     //###########################################
1786     //clean up the nodepath (such as for trivial subpaths)
1787     sp_nodepath_cleanup(nodepath);
1789     sp_nodepath_ensure_ctrls(nodepath);
1791     update_repr(nodepath);
1793     // if the entire nodepath is removed, delete the selected object.
1794     if (nodepath->subpaths == NULL ||
1795         sp_nodepath_get_node_count(nodepath) < 2) {
1796         sp_nodepath_destroy(nodepath);
1797         sp_selection_delete();
1798         return;
1799     }
1801     sp_nodepath_update_statusbar(nodepath);
1804 /**
1805  * Call sp_nodepath_set_line() for all selected segments.
1806  */
1807 void
1808 sp_node_selected_set_line_type(NRPathcode code)
1810     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1811     if (nodepath == NULL) return;
1813     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1814        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1815         g_assert(n->selected);
1816         if (n->p.other && n->p.other->selected) {
1817             sp_nodepath_set_line_type(n, code);
1818         }
1819     }
1821     update_repr(nodepath);
1824 /**
1825  * Call sp_nodepath_convert_node_type() for all selected nodes.
1826  */
1827 void
1828 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
1830     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1831     if (nodepath == NULL) return;
1833     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1834         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
1835     }
1837     update_repr(nodepath);
1840 /**
1841  * Change select status of node, update its own and neighbour handles.
1842  */
1843 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
1845     node->selected = selected;
1847     if (selected) {
1848         g_object_set(G_OBJECT(node->knot),
1849                      "fill", NODE_FILL_SEL,
1850                      "fill_mouseover", NODE_FILL_SEL_HI,
1851                      "stroke", NODE_STROKE_SEL,
1852                      "stroke_mouseover", NODE_STROKE_SEL_HI,
1853                      "size", (node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9,
1854                      NULL);
1855     } else {
1856         g_object_set(G_OBJECT(node->knot),
1857                      "fill", NODE_FILL,
1858                      "fill_mouseover", NODE_FILL_HI,
1859                      "stroke", NODE_STROKE,
1860                      "stroke_mouseover", NODE_STROKE_HI,
1861                      "size", (node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7,
1862                      NULL);
1863     }
1865     sp_node_ensure_ctrls(node);
1866     if (node->n.other) sp_node_ensure_ctrls(node->n.other);
1867     if (node->p.other) sp_node_ensure_ctrls(node->p.other);
1870 /**
1871 \brief Select a node
1872 \param node     The node to select
1873 \param incremental   If true, add to selection, otherwise deselect others
1874 \param override   If true, always select this node, otherwise toggle selected status
1875 */
1876 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
1878     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
1880     if (incremental) {
1881         if (override) {
1882             if (!g_list_find(nodepath->selected, node)) {
1883                 nodepath->selected = g_list_append(nodepath->selected, node);
1884             }
1885             sp_node_set_selected(node, TRUE);
1886         } else { // toggle
1887             if (node->selected) {
1888                 g_assert(g_list_find(nodepath->selected, node));
1889                 nodepath->selected = g_list_remove(nodepath->selected, node);
1890             } else {
1891                 g_assert(!g_list_find(nodepath->selected, node));
1892                 nodepath->selected = g_list_append(nodepath->selected, node);
1893             }
1894             sp_node_set_selected(node, !node->selected);
1895         }
1896     } else {
1897         sp_nodepath_deselect(nodepath);
1898         nodepath->selected = g_list_append(nodepath->selected, node);
1899         sp_node_set_selected(node, TRUE);
1900     }
1902     sp_nodepath_update_statusbar(nodepath);
1906 /**
1907 \brief Deselect all nodes in the nodepath
1908 */
1909 void
1910 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
1912     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1914     while (nodepath->selected) {
1915         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
1916         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
1917     }
1918     sp_nodepath_update_statusbar(nodepath);
1921 /**
1922 \brief Select or invert selection of all nodes in the nodepath
1923 */
1924 void
1925 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
1927     if (!nodepath) return;
1929     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1930        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1931         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1932            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1933            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
1934         }
1935     }
1938 /**
1939  * If nothing selected, does the same as sp_nodepath_select_all();
1940  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
1941  * (i.e., similar to "select all in layer", with the "selected" subpaths
1942  * being treated as "layers" in the path).
1943  */
1944 void
1945 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
1947     if (!nodepath) return;
1949     if (g_list_length (nodepath->selected) == 0) {
1950         sp_nodepath_select_all (nodepath, invert);
1951         return;
1952     }
1954     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
1955     GSList *subpaths = NULL;
1957     for (GList *l = copy; l != NULL; l = l->next) {
1958         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1959         Inkscape::NodePath::SubPath *subpath = n->subpath;
1960         if (!g_slist_find (subpaths, subpath))
1961             subpaths = g_slist_prepend (subpaths, subpath);
1962     }
1964     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
1965         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
1966         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1967             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1968             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
1969         }
1970     }
1972     g_slist_free (subpaths);
1973     g_list_free (copy);
1976 /**
1977  * \brief Select the node after the last selected; if none is selected,
1978  * select the first within path.
1979  */
1980 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
1982     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1984    Inkscape::NodePath::Node *last = NULL;
1985     if (nodepath->selected) {
1986         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1987            Inkscape::NodePath::SubPath *subpath, *subpath_next;
1988             subpath = (Inkscape::NodePath::SubPath *) spl->data;
1989             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1990                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1991                 if (node->selected) {
1992                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
1993                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
1994                             if (spl->next) { // there's a next subpath
1995                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
1996                                 last = subpath_next->first;
1997                             } else if (spl->prev) { // there's a previous subpath
1998                                 last = NULL; // to be set later to the first node of first subpath
1999                             } else {
2000                                 last = node->n.other;
2001                             }
2002                         } else {
2003                             last = node->n.other;
2004                         }
2005                     } else {
2006                         if (node->n.other) {
2007                             last = node->n.other;
2008                         } else {
2009                             if (spl->next) { // there's a next subpath
2010                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2011                                 last = subpath_next->first;
2012                             } else if (spl->prev) { // there's a previous subpath
2013                                 last = NULL; // to be set later to the first node of first subpath
2014                             } else {
2015                                 last = (Inkscape::NodePath::Node *) subpath->first;
2016                             }
2017                         }
2018                     }
2019                 }
2020             }
2021         }
2022         sp_nodepath_deselect(nodepath);
2023     }
2025     if (last) { // there's at least one more node after selected
2026         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2027     } else { // no more nodes, select the first one in first subpath
2028        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2029         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2030     }
2033 /**
2034  * \brief Select the node before the first selected; if none is selected,
2035  * select the last within path
2036  */
2037 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2039     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2041    Inkscape::NodePath::Node *last = NULL;
2042     if (nodepath->selected) {
2043         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2044            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2045             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2046                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2047                 if (node->selected) {
2048                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2049                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2050                             if (spl->prev) { // there's a prev subpath
2051                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2052                                 last = subpath_prev->last;
2053                             } else if (spl->next) { // there's a next subpath
2054                                 last = NULL; // to be set later to the last node of last subpath
2055                             } else {
2056                                 last = node->p.other;
2057                             }
2058                         } else {
2059                             last = node->p.other;
2060                         }
2061                     } else {
2062                         if (node->p.other) {
2063                             last = node->p.other;
2064                         } else {
2065                             if (spl->prev) { // there's a prev subpath
2066                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2067                                 last = subpath_prev->last;
2068                             } else if (spl->next) { // there's a next subpath
2069                                 last = NULL; // to be set later to the last node of last subpath
2070                             } else {
2071                                 last = (Inkscape::NodePath::Node *) subpath->last;
2072                             }
2073                         }
2074                     }
2075                 }
2076             }
2077         }
2078         sp_nodepath_deselect(nodepath);
2079     }
2081     if (last) { // there's at least one more node before selected
2082         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2083     } else { // no more nodes, select the last one in last subpath
2084         GList *spl = g_list_last(nodepath->subpaths);
2085        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2086         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2087     }
2090 /**
2091  * \brief Select all nodes that are within the rectangle.
2092  */
2093 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2095     if (!incremental) {
2096         sp_nodepath_deselect(nodepath);
2097     }
2099     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2100        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2101         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2102            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2104             if (b.contains(node->pos)) {
2105                 sp_nodepath_node_select(node, TRUE, TRUE);
2106             }
2107         }
2108     }
2111 /**
2112 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2113 */
2114 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2116     if (!nodepath->selected) {
2117         return NULL;
2118     }
2120     GList *r = NULL;
2121     guint i = 0;
2122     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2123        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2124         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2125            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2126             i++;
2127             if (node->selected) {
2128                 r = g_list_append(r, GINT_TO_POINTER(i));
2129             }
2130         }
2131     }
2132     return r;
2135 /**
2136 \brief  Restores selection by selecting nodes whose positions are in the list
2137 */
2138 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2140     sp_nodepath_deselect(nodepath);
2142     guint i = 0;
2143     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2144        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2145         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2146            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2147             i++;
2148             if (g_list_find(r, GINT_TO_POINTER(i))) {
2149                 sp_nodepath_node_select(node, TRUE, TRUE);
2150             }
2151         }
2152     }
2156 /**
2157 \brief Adjusts control point according to node type and line code.
2158 */
2159 static void sp_node_adjust_knot(Inkscape::NodePath::Node *node, gint which_adjust)
2161     double len, otherlen, linelen;
2163     g_assert(node);
2165    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2166    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2168     /** \todo fixme: */
2169     if (me->other == NULL) return;
2170     if (other->other == NULL) return;
2172     /* I have line */
2174     NRPathcode mecode, ocode;
2175     if (which_adjust == 1) {
2176         mecode = (NRPathcode)me->other->code;
2177         ocode = (NRPathcode)node->code;
2178     } else {
2179         mecode = (NRPathcode)node->code;
2180         ocode = (NRPathcode)other->other->code;
2181     }
2183     if (mecode == NR_LINETO) return;
2185     /* I am curve */
2187     if (other->other == NULL) return;
2189     /* Other has line */
2191     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2193     NR::Point delta;
2194     if (ocode == NR_LINETO) {
2195         /* other is lineto, we are either smooth or symm */
2196        Inkscape::NodePath::Node *othernode = other->other;
2197         len = NR::L2(me->pos - node->pos);
2198         delta = node->pos - othernode->pos;
2199         linelen = NR::L2(delta);
2200         if (linelen < 1e-18) return;
2202         me->pos = node->pos + (len / linelen)*delta;
2204         sp_node_ensure_ctrls(node);
2205         return;
2206     }
2208     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2210         me->pos = 2 * node->pos - other->pos;
2212         sp_node_ensure_ctrls(node);
2213         return;
2214     }
2216     /* We are smooth */
2218     len = NR::L2(me->pos - node->pos);
2219     delta = other->pos - node->pos;
2220     otherlen = NR::L2(delta);
2221     if (otherlen < 1e-18) return;
2223     me->pos = node->pos - (len / otherlen) * delta;
2225     sp_node_ensure_ctrls(node);
2228 /**
2229  \brief Adjusts control point according to node type and line code
2230  */
2231 static void sp_node_adjust_knots(Inkscape::NodePath::Node *node)
2233     g_assert(node);
2235     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2237     /* we are either smooth or symm */
2239     if (node->p.other == NULL) return;
2241     if (node->n.other == NULL) return;
2243     if (node->code == NR_LINETO) {
2244         if (node->n.other->code == NR_LINETO) return;
2245         sp_node_adjust_knot(node, 1);
2246         sp_node_ensure_ctrls(node);
2247         return;
2248     }
2250     if (node->n.other->code == NR_LINETO) {
2251         if (node->code == NR_LINETO) return;
2252         sp_node_adjust_knot(node, -1);
2253         sp_node_ensure_ctrls(node);
2254         return;
2255     }
2257     /* both are curves */
2259     NR::Point const delta( node->n.pos - node->p.pos );
2261     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2262         node->p.pos = node->pos - delta / 2;
2263         node->n.pos = node->pos + delta / 2;
2264         sp_node_ensure_ctrls(node);
2265         return;
2266     }
2268     /* We are smooth */
2270     double plen = NR::L2(node->p.pos - node->pos);
2271     if (plen < 1e-18) return;
2272     double nlen = NR::L2(node->n.pos - node->pos);
2273     if (nlen < 1e-18) return;
2274     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2275     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2276     sp_node_ensure_ctrls(node);
2279 /**
2280  * Knot events handler callback.
2281  */
2282 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2284     gboolean ret = FALSE;
2285     switch (event->type) {
2286         case GDK_ENTER_NOTIFY:
2287             active_node = n;
2288             break;
2289         case GDK_LEAVE_NOTIFY:
2290             active_node = NULL;
2291             break;
2292         case GDK_KEY_PRESS:
2293             switch (get_group0_keyval (&event->key)) {
2294                 case GDK_space:
2295                     if (event->key.state & GDK_BUTTON1_MASK) {
2296                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2297                         stamp_repr(nodepath);
2298                         ret = TRUE;
2299                     }
2300                     break;
2301                 default:
2302                     break;
2303             }
2304             break;
2305         default:
2306             break;
2307     }
2309     return ret;
2312 /**
2313  * Handle keypress on node; directly called.
2314  */
2315 gboolean node_key(GdkEvent *event)
2317     Inkscape::NodePath::Path *np;
2319     // there is no way to verify nodes so set active_node to nil when deleting!!
2320     if (active_node == NULL) return FALSE;
2322     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2323         gint ret = FALSE;
2324         switch (get_group0_keyval (&event->key)) {
2325             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2326             case GDK_BackSpace:
2327                 np = active_node->subpath->nodepath;
2328                 sp_nodepath_node_destroy(active_node);
2329                 update_repr(np);
2330                 active_node = NULL;
2331                 ret = TRUE;
2332                 break;
2333             case GDK_c:
2334                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2335                 ret = TRUE;
2336                 break;
2337             case GDK_s:
2338                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2339                 ret = TRUE;
2340                 break;
2341             case GDK_y:
2342                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2343                 ret = TRUE;
2344                 break;
2345             case GDK_b:
2346                 sp_nodepath_node_break(active_node);
2347                 ret = TRUE;
2348                 break;
2349         }
2350         return ret;
2351     }
2352     return FALSE;
2355 /**
2356  * Mouseclick on node callback.
2357  */
2358 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2360    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2362     if (state & GDK_CONTROL_MASK) {
2363         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2365         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2366             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2367                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2368             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2369                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2370             } else {
2371                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2372             }
2373             update_repr(nodepath);
2374             sp_nodepath_update_statusbar(nodepath);
2376         } else { //ctrl+alt+click: delete node
2377             sp_nodepath_node_destroy(n);
2378             //clean up the nodepath (such as for trivial subpaths)
2379             sp_nodepath_cleanup(nodepath);
2381             // if the entire nodepath is removed, delete the selected object.
2382             if (nodepath->subpaths == NULL ||
2383                 sp_nodepath_get_node_count(nodepath) < 2) {
2384                 SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
2385                 sp_nodepath_destroy(nodepath);
2386                 sp_selection_delete();
2387                 sp_document_done (document);
2389             } else {
2390                 sp_nodepath_ensure_ctrls(nodepath);
2391                 update_repr(nodepath);
2392                 sp_nodepath_update_statusbar(nodepath);
2393             }
2394         }
2396     } else {
2397         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2398     }
2401 /**
2402  * Mouse grabbed node callback.
2403  */
2404 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2406    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2408     n->origin = knot->pos;
2410     if (!n->selected) {
2411         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2412     }
2415 /**
2416  * Mouse ungrabbed node callback.
2417  */
2418 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2420    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2422    n->dragging_out = NULL;
2424    update_repr(n->subpath->nodepath);
2427 /**
2428  * The point on a line, given by its angle, closest to the given point.
2429  * \param p  A point.
2430  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2431  * \param closest  Pointer to the point struct where the result is stored.
2432  * \todo FIXME: use dot product perhaps?
2433  */
2434 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2436     if (a == HUGE_VAL) { // vertical
2437         *closest = NR::Point(0, (*p)[NR::Y]);
2438     } else {
2439         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2440         (*closest)[NR::Y] = a * (*closest)[NR::X];
2441     }
2444 /**
2445  * Distance from the point to a line given by its angle.
2446  * \param p  A point.
2447  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2448  */
2449 static double point_line_distance(NR::Point *p, double a)
2451     NR::Point c;
2452     point_line_closest(p, a, &c);
2453     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]));
2456 /**
2457  * Callback for node "request" signal.
2458  * \todo fixme: This goes to "moved" event? (lauris)
2459  */
2460 static gboolean
2461 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2463     double yn, xn, yp, xp;
2464     double an, ap, na, pa;
2465     double d_an, d_ap, d_na, d_pa;
2466     gboolean collinear = FALSE;
2467     NR::Point c;
2468     NR::Point pr;
2470    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2472    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2473    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2475        NR::Point mouse = (*p);
2477        if (!n->dragging_out) {
2478            // This is the first drag-out event; find out which handle to drag out
2479            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2480            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2482            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2483                return FALSE;
2485            Inkscape::NodePath::NodeSide *opposite;
2486            if (appr_p > appr_n) { // closer to p
2487                n->dragging_out = &n->p;
2488                opposite = &n->n;
2489                n->code = NR_CURVETO;
2490            } else if (appr_p < appr_n) { // closer to n
2491                n->dragging_out = &n->n;
2492                opposite = &n->p;
2493                n->n.other->code = NR_CURVETO;
2494            } else { // p and n nodes are the same
2495                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2496                    n->dragging_out = &n->p;
2497                    opposite = &n->n;
2498                    n->code = NR_CURVETO;
2499                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2500                    n->dragging_out = &n->n;
2501                    opposite = &n->p;
2502                    n->n.other->code = NR_CURVETO;
2503                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2504                    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);
2505                    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);
2506                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2507                        n->dragging_out = &n->n;
2508                        opposite = &n->p;
2509                        n->n.other->code = NR_CURVETO;
2510                    } else { // closer to other's n handle
2511                        n->dragging_out = &n->p;
2512                        opposite = &n->n;
2513                        n->code = NR_CURVETO;
2514                    }
2515                }
2516            }
2518            // if there's another handle, make sure the one we drag out starts parallel to it
2519            if (opposite->pos != n->pos) {
2520                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2521            }
2523            // knots might not be created yet!
2524            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2525            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2526        }
2528        // pass this on to the handle-moved callback
2529        node_ctrl_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2530        sp_node_ensure_ctrls(n);
2531        return TRUE;
2532    }
2534     if (state & GDK_CONTROL_MASK) { // constrained motion
2536         // calculate relative distances of handles
2537         // n handle:
2538         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2539         xn = n->n.pos[NR::X] - n->pos[NR::X];
2540         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2541         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2542             if (n->n.other) { // if there is the next point
2543                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2544                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2545                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2546             }
2547         }
2548         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2549         if (yn < 0) { xn = -xn; yn = -yn; }
2551         // p handle:
2552         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2553         xp = n->p.pos[NR::X] - n->pos[NR::X];
2554         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2555         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2556             if (n->p.other) {
2557                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2558                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2559                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2560             }
2561         }
2562         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2563         if (yp < 0) { xp = -xp; yp = -yp; }
2565         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2566             // sliding on handles, only if at least one of the handles is non-vertical
2567             // (otherwise it's the same as ctrl+drag anyway)
2569             // calculate angles of the control handles
2570             if (xn == 0) {
2571                 if (yn == 0) { // no handle, consider it the continuation of the other one
2572                     an = 0;
2573                     collinear = TRUE;
2574                 }
2575                 else an = 0; // vertical; set the angle to horizontal
2576             } else an = yn/xn;
2578             if (xp == 0) {
2579                 if (yp == 0) { // no handle, consider it the continuation of the other one
2580                     ap = an;
2581                 }
2582                 else ap = 0; // vertical; set the angle to horizontal
2583             } else  ap = yp/xp;
2585             if (collinear) an = ap;
2587             // angles of the perpendiculars; HUGE_VAL means vertical
2588             if (an == 0) na = HUGE_VAL; else na = -1/an;
2589             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2591             //g_print("an %g    ap %g\n", an, ap);
2593             // mouse point relative to the node's original pos
2594             pr = (*p) - n->origin;
2596             // distances to the four lines (two handles and two perpendiculars)
2597             d_an = point_line_distance(&pr, an);
2598             d_na = point_line_distance(&pr, na);
2599             d_ap = point_line_distance(&pr, ap);
2600             d_pa = point_line_distance(&pr, pa);
2602             // find out which line is the closest, save its closest point in c
2603             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2604                 point_line_closest(&pr, an, &c);
2605             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2606                 point_line_closest(&pr, ap, &c);
2607             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2608                 point_line_closest(&pr, na, &c);
2609             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2610                 point_line_closest(&pr, pa, &c);
2611             }
2613             // move the node to the closest point
2614             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2615                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2616                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2618         } else {  // constraining to hor/vert
2620             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2621                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2622             } else { // snap to vert
2623                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2624             }
2625         }
2626     } else { // move freely
2627         sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2628                                         (*p)[NR::X] - n->pos[NR::X],
2629                                         (*p)[NR::Y] - n->pos[NR::Y],
2630                                         (state & GDK_SHIFT_MASK) == 0);
2631     }
2633     n->subpath->nodepath->desktop->scroll_to_point(p);
2635     return TRUE;
2638 /**
2639  * Node handle clicked callback.
2640  */
2641 static void node_ctrl_clicked(SPKnot *knot, guint state, gpointer data)
2643    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2645     if (state & GDK_CONTROL_MASK) { // "delete" handle
2646         if (n->p.knot == knot) {
2647             n->p.pos = n->pos;
2648         } else if (n->n.knot == knot) {
2649             n->n.pos = n->pos;
2650         }
2651         sp_node_ensure_ctrls(n);
2652         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2653         update_repr(nodepath);
2654         sp_nodepath_update_statusbar(nodepath);
2656     } else { // just select or add to selection, depending in Shift
2657         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2658     }
2661 /**
2662  * Node handle grabbed callback.
2663  */
2664 static void node_ctrl_grabbed(SPKnot *knot, guint state, gpointer data)
2666    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2668     if (!n->selected) {
2669         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2670     }
2672     // remember the origin of the control
2673     if (n->p.knot == knot) {
2674         n->p.origin = n->p.pos - n->pos;
2675     } else if (n->n.knot == knot) {
2676         n->n.origin = n->n.pos - n->pos;
2677     } else {
2678         g_assert_not_reached();
2679     }
2683 /**
2684  * Node handle ungrabbed callback.
2685  */
2686 static void node_ctrl_ungrabbed(SPKnot *knot, guint state, gpointer data)
2688    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2690     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
2691     if (n->p.knot == knot) {
2692         n->p.origin.a = 0;
2693         sp_knot_set_position(knot, &n->p.pos, state);
2694     } else if (n->n.knot == knot) {
2695         n->n.origin.a = 0;
2696         sp_knot_set_position(knot, &n->n.pos, state);
2697     } else {
2698         g_assert_not_reached();
2699     }
2701     update_repr(n->subpath->nodepath);
2704 /**
2705  * Node handle "request" signal callback.
2706  */
2707 static gboolean node_ctrl_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2709     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2711     Inkscape::NodePath::NodeSide *me, *opposite;
2712     gint which;
2713     if (n->p.knot == knot) {
2714         me = &n->p;
2715         opposite = &n->n;
2716         which = -1;
2717     } else if (n->n.knot == knot) {
2718         me = &n->n;
2719         opposite = &n->p;
2720         which = 1;
2721     } else {
2722         me = opposite = NULL;
2723         which = 0;
2724         g_assert_not_reached();
2725     }
2727     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
2729     SnapManager const m(n->subpath->nodepath->desktop->namedview);
2731     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
2732         /* We are smooth node adjacent with line */
2733         NR::Point const delta = *p - n->pos;
2734         NR::Coord const len = NR::L2(delta);
2735         Inkscape::NodePath::Node *othernode = opposite->other;
2736         NR::Point const ndelta = n->pos - othernode->pos;
2737         NR::Coord const linelen = NR::L2(ndelta);
2738         if (len > NR_EPSILON && linelen > NR_EPSILON) {
2739             NR::Coord const scal = dot(delta, ndelta) / linelen;
2740             (*p) = n->pos + (scal / linelen) * ndelta;
2741         }
2742         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, ndelta, NULL).getPoint();
2743     } else {
2744         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
2745     }
2747     sp_node_adjust_knot(n, -which);
2749     return FALSE;
2752 /**
2753  * Node handle moved callback.
2754  */
2755 static void node_ctrl_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2757    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2759    Inkscape::NodePath::NodeSide *me;
2760    Inkscape::NodePath::NodeSide *other;
2761     if (n->p.knot == knot) {
2762         me = &n->p;
2763         other = &n->n;
2764     } else if (n->n.knot == knot) {
2765         me = &n->n;
2766         other = &n->p;
2767     } else {
2768         me = NULL;
2769         other = NULL;
2770         g_assert_not_reached();
2771     }
2773     // calculate radial coordinates of the grabbed control, other control, and the mouse point
2774     Radial rme(me->pos - n->pos);
2775     Radial rother(other->pos - n->pos);
2776     Radial rnew(*p - n->pos);
2778     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
2779         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
2780         /* 0 interpreted as "no snapping". */
2782         // The closest PI/snaps angle, starting from zero.
2783         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
2784         if (me->origin.a == HUGE_VAL) {
2785             // ortho doesn't exist: original control was zero length.
2786             rnew.a = a_snapped;
2787         } else {
2788             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
2789              * its opposite and perpendiculars). */
2790             double const a_ortho = me->origin.a + floor((rnew.a - me->origin.a)/(M_PI/2) + 0.5) * (M_PI/2);
2792             // Snap to the closest.
2793             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
2794                        ? a_snapped
2795                        : a_ortho );
2796         }
2797     }
2799     if (state & GDK_MOD1_MASK) {
2800         // lock handle length
2801         rnew.r = me->origin.r;
2802     }
2804     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
2805         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
2806         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
2807         rother.a += rnew.a - rme.a;
2808         other->pos = NR::Point(rother) + n->pos;
2809         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
2810         sp_knot_set_position(other->knot, &other->pos, 0);
2811     }
2813     me->pos = NR::Point(rnew) + n->pos;
2814     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
2816     // this is what sp_knot_set_position does, but without emitting the signal:
2817     // we cannot emit a "moved" signal because we're now processing it
2818     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
2820     knot->desktop->set_coordinate_status(me->pos);
2822     update_object(n->subpath->nodepath);
2824     /* status text */
2825     SPDesktop *desktop = n->subpath->nodepath->desktop;
2826     if (!desktop) return;
2827     SPEventContext *ec = desktop->event_context;
2828     if (!ec) return;
2829     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
2830     if (!mc) return;
2832     double degrees = 180 / M_PI * rnew.a;
2833     if (degrees > 180) degrees -= 360;
2834     if (degrees < -180) degrees += 360;
2835     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
2836         degrees = angle_to_compass (degrees);
2838     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
2840     mc->setF(Inkscape::NORMAL_MESSAGE,
2841          _("<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);
2843     g_string_free(length, TRUE);
2846 /**
2847  * Node handle event callback.
2848  */
2849 static gboolean node_ctrl_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2851     gboolean ret = FALSE;
2852     switch (event->type) {
2853         case GDK_KEY_PRESS:
2854             switch (get_group0_keyval (&event->key)) {
2855                 case GDK_space:
2856                     if (event->key.state & GDK_BUTTON1_MASK) {
2857                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2858                         stamp_repr(nodepath);
2859                         ret = TRUE;
2860                     }
2861                     break;
2862                 default:
2863                     break;
2864             }
2865             break;
2866         default:
2867             break;
2868     }
2870     return ret;
2873 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
2874                                  Radial &rme, Radial &rother, gboolean const both)
2876     rme.a += angle;
2877     if ( both
2878          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2879          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2880     {
2881         rother.a += angle;
2882     }
2885 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
2886                                         Radial &rme, Radial &rother, gboolean const both)
2888     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
2890     gdouble r;
2891     if ( both
2892          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2893          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2894     {
2895         r = MAX(rme.r, rother.r);
2896     } else {
2897         r = rme.r;
2898     }
2900     gdouble const weird_angle = atan2(norm_angle, r);
2901 /* Bulia says norm_angle is just the visible distance that the
2902  * object's end must travel on the screen.  Left as 'angle' for want of
2903  * a better name.*/
2905     rme.a += weird_angle;
2906     if ( both
2907          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2908          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2909     {
2910         rother.a += weird_angle;
2911     }
2914 /**
2915  * Rotate one node.
2916  */
2917 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
2919     Inkscape::NodePath::NodeSide *me, *other;
2920     bool both = false;
2922     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
2923     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
2925     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
2926         me = &(n->p);
2927         other = &(n->n);
2928     } else if (!n->p.other) {
2929         me = &(n->n);
2930         other = &(n->p);
2931     } else {
2932         if (which > 0) { // right handle
2933             if (xn > xp) {
2934                 me = &(n->n);
2935                 other = &(n->p);
2936             } else {
2937                 me = &(n->p);
2938                 other = &(n->n);
2939             }
2940         } else if (which < 0){ // left handle
2941             if (xn <= xp) {
2942                 me = &(n->n);
2943                 other = &(n->p);
2944             } else {
2945                 me = &(n->p);
2946                 other = &(n->n);
2947             }
2948         } else { // both handles
2949             me = &(n->n);
2950             other = &(n->p);
2951             both = true;
2952         }
2953     }
2955     Radial rme(me->pos - n->pos);
2956     Radial rother(other->pos - n->pos);
2958     if (screen) {
2959         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
2960     } else {
2961         node_rotate_one_internal (*n, angle, rme, rother, both);
2962     }
2964     me->pos = n->pos + NR::Point(rme);
2966     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
2967         other->pos =  n->pos + NR::Point(rother);
2968     }
2970     sp_node_ensure_ctrls(n);
2973 /**
2974  * Rotate selected nodes.
2975  */
2976 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
2978     if (!nodepath || !nodepath->selected) return;
2980     if (g_list_length(nodepath->selected) == 1) {
2981        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
2982         node_rotate_one (n, angle, which, screen);
2983     } else {
2984        // rotate as an object:
2986         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
2987         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
2988         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2989             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2990             box.expandTo (n->pos); // contain all selected nodes
2991         }
2993         gdouble rot;
2994         if (screen) {
2995             gdouble const zoom = nodepath->desktop->current_zoom();
2996             gdouble const zmove = angle / zoom;
2997             gdouble const r = NR::L2(box.max() - box.midpoint());
2998             rot = atan2(zmove, r);
2999         } else {
3000             rot = angle;
3001         }
3003         NR::Matrix t =
3004             NR::Matrix (NR::translate(-box.midpoint())) *
3005             NR::Matrix (NR::rotate(rot)) *
3006             NR::Matrix (NR::translate(box.midpoint()));
3008         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3009             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3010             n->pos *= t;
3011             n->n.pos *= t;
3012             n->p.pos *= t;
3013             sp_node_ensure_ctrls(n);
3014         }
3015     }
3017     update_object(nodepath);
3018     /// \todo fixme: use _keyed
3019     update_repr(nodepath);
3022 /**
3023  * Scale one node.
3024  */
3025 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3027     bool both = false;
3028     Inkscape::NodePath::NodeSide *me, *other;
3030     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3031     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3033     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3034         me = &(n->p);
3035         other = &(n->n);
3036         n->code = NR_CURVETO;
3037     } else if (!n->p.other) {
3038         me = &(n->n);
3039         other = &(n->p);
3040         if (n->n.other)
3041             n->n.other->code = NR_CURVETO;
3042     } else {
3043         if (which > 0) { // right handle
3044             if (xn > xp) {
3045                 me = &(n->n);
3046                 other = &(n->p);
3047                 if (n->n.other)
3048                     n->n.other->code = NR_CURVETO;
3049             } else {
3050                 me = &(n->p);
3051                 other = &(n->n);
3052                 n->code = NR_CURVETO;
3053             }
3054         } else if (which < 0){ // left handle
3055             if (xn <= xp) {
3056                 me = &(n->n);
3057                 other = &(n->p);
3058                 if (n->n.other)
3059                     n->n.other->code = NR_CURVETO;
3060             } else {
3061                 me = &(n->p);
3062                 other = &(n->n);
3063                 n->code = NR_CURVETO;
3064             }
3065         } else { // both handles
3066             me = &(n->n);
3067             other = &(n->p);
3068             both = true;
3069             n->code = NR_CURVETO;
3070             if (n->n.other)
3071                 n->n.other->code = NR_CURVETO;
3072         }
3073     }
3075     Radial rme(me->pos - n->pos);
3076     Radial rother(other->pos - n->pos);
3078     rme.r += grow;
3079     if (rme.r < 0) rme.r = 0;
3080     if (rme.a == HUGE_VAL) {
3081         if (me->other) { // if direction is unknown, initialize it towards the next node
3082             Radial rme_next(me->other->pos - n->pos);
3083             rme.a = rme_next.a;
3084         } else { // if there's no next, initialize to 0
3085             rme.a = 0;
3086         }
3087     }
3088     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3089         rother.r += grow;
3090         if (rother.r < 0) rother.r = 0;
3091         if (rother.a == HUGE_VAL) {
3092             rother.a = rme.a + M_PI;
3093         }
3094     }
3096     me->pos = n->pos + NR::Point(rme);
3098     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3099         other->pos = n->pos + NR::Point(rother);
3100     }
3102     sp_node_ensure_ctrls(n);
3105 /**
3106  * Scale selected nodes.
3107  */
3108 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3110     if (!nodepath || !nodepath->selected) return;
3112     if (g_list_length(nodepath->selected) == 1) {
3113         // scale handles of the single selected node
3114         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3115         node_scale_one (n, grow, which);
3116     } else {
3117         // scale nodes as an "object":
3119         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3120         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3121         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3122             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3123             box.expandTo (n->pos); // contain all selected nodes
3124         }
3126         double scale = (box.maxExtent() + grow)/box.maxExtent();
3128         NR::Matrix t =
3129             NR::Matrix (NR::translate(-box.midpoint())) *
3130             NR::Matrix (NR::scale(scale, scale)) *
3131             NR::Matrix (NR::translate(box.midpoint()));
3133         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3134             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3135             n->pos *= t;
3136             n->n.pos *= t;
3137             n->p.pos *= t;
3138             sp_node_ensure_ctrls(n);
3139         }
3140     }
3142     update_object(nodepath);
3143     /// \todo fixme: use _keyed
3144     update_repr(nodepath);
3147 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3149     if (!nodepath) return;
3150     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3153 /**
3154  * Flip selected nodes horizontally/vertically.
3155  */
3156 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3158     if (!nodepath || !nodepath->selected) return;
3160     if (g_list_length(nodepath->selected) == 1) {
3161         // flip handles of the single selected node
3162         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3163         double temp = n->p.pos[axis];
3164         n->p.pos[axis] = n->n.pos[axis];
3165         n->n.pos[axis] = temp;
3166         sp_node_ensure_ctrls(n);
3167     } else {
3168         // scale nodes as an "object":
3170         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3171         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3172         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3173             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3174             box.expandTo (n->pos); // contain all selected nodes
3175         }
3177         NR::Matrix t =
3178             NR::Matrix (NR::translate(-box.midpoint())) *
3179             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3180             NR::Matrix (NR::translate(box.midpoint()));
3182         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3183             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3184             n->pos *= t;
3185             n->n.pos *= t;
3186             n->p.pos *= t;
3187             sp_node_ensure_ctrls(n);
3188         }
3189     }
3191     update_object(nodepath);
3192     /// \todo fixme: use _keyed
3193     update_repr(nodepath);
3196 //-----------------------------------------------
3197 /**
3198  * Return new subpath under given nodepath.
3199  */
3200 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3202     g_assert(nodepath);
3203     g_assert(nodepath->desktop);
3205    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3207     s->nodepath = nodepath;
3208     s->closed = FALSE;
3209     s->nodes = NULL;
3210     s->first = NULL;
3211     s->last = NULL;
3213     // do not use prepend here because:
3214     // if you have a path like "subpath_1 subpath_2 ... subpath_k" in the svg, you end up with
3215     // subpath_k -> ... ->subpath_1 in the nodepath structure. thus the i-th node of the svg is not
3216     // the i-th node in the nodepath (only if there are multiple subpaths)
3217     // note that the problem only arise when called from subpath_from_bpath(), since for all the other
3218     // cases, the repr is updated after the call to sp_nodepath_subpath_new()
3219     nodepath->subpaths = g_list_append /*g_list_prepend*/ (nodepath->subpaths, s);
3221     return s;
3224 /**
3225  * Destroy nodes in subpath, then subpath itself.
3226  */
3227 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3229     g_assert(subpath);
3230     g_assert(subpath->nodepath);
3231     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3233     while (subpath->nodes) {
3234         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3235     }
3237     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3239     g_free(subpath);
3242 /**
3243  * Link head to tail in subpath.
3244  */
3245 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3247     g_assert(!sp->closed);
3248     g_assert(sp->last != sp->first);
3249     g_assert(sp->first->code == NR_MOVETO);
3251     sp->closed = TRUE;
3253     //Link the head to the tail
3254     sp->first->p.other = sp->last;
3255     sp->last->n.other  = sp->first;
3256     sp->last->n.pos    = sp->first->n.pos;
3257     sp->first          = sp->last;
3259     //Remove the extra end node
3260     sp_nodepath_node_destroy(sp->last->n.other);
3263 /**
3264  * Open closed (loopy) subpath at node.
3265  */
3266 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3268     g_assert(sp->closed);
3269     g_assert(n->subpath == sp);
3270     g_assert(sp->first == sp->last);
3272     /* We create new startpoint, current node will become last one */
3274    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3275                                                 &n->pos, &n->pos, &n->n.pos);
3278     sp->closed        = FALSE;
3280     //Unlink to make a head and tail
3281     sp->first         = new_path;
3282     sp->last          = n;
3283     n->n.other        = NULL;
3284     new_path->p.other = NULL;
3287 /**
3288  * Returns area in triangle given by points; may be negative.
3289  */
3290 inline double
3291 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3293     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]);
3296 /**
3297  * Return new node in subpath with given properties.
3298  * \param pos Position of node.
3299  * \param ppos Handle position in previous direction
3300  * \param npos Handle position in previous direction
3301  */
3302 Inkscape::NodePath::Node *
3303 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)
3305     g_assert(sp);
3306     g_assert(sp->nodepath);
3307     g_assert(sp->nodepath->desktop);
3309     if (nodechunk == NULL)
3310         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3312     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3314     n->subpath  = sp;
3316     if (type != Inkscape::NodePath::NODE_NONE) {
3317         // use the type from sodipodi:nodetypes
3318         n->type = type;
3319     } else {
3320         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3321             // points are (almost) collinear
3322             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3323                 // endnode, or a node with a retracted handle
3324                 n->type = Inkscape::NodePath::NODE_CUSP;
3325             } else {
3326                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3327             }
3328         } else {
3329             n->type = Inkscape::NodePath::NODE_CUSP;
3330         }
3331     }
3333     n->code     = code;
3334     n->selected = FALSE;
3335     n->pos      = *pos;
3336     n->p.pos    = *ppos;
3337     n->n.pos    = *npos;
3339     n->dragging_out = NULL;
3341     Inkscape::NodePath::Node *prev;
3342     if (next) {
3343         //g_assert(g_list_find(sp->nodes, next));
3344         prev = next->p.other;
3345     } else {
3346         prev = sp->last;
3347     }
3349     if (prev)
3350         prev->n.other = n;
3351     else
3352         sp->first = n;
3354     if (next)
3355         next->p.other = n;
3356     else
3357         sp->last = n;
3359     n->p.other = prev;
3360     n->n.other = next;
3362     n->knot = sp_knot_new(sp->nodepath->desktop);
3363     sp_knot_set_position(n->knot, pos, 0);
3364     g_object_set(G_OBJECT(n->knot),
3365                  "anchor", GTK_ANCHOR_CENTER,
3366                  "fill", NODE_FILL,
3367                  "fill_mouseover", NODE_FILL_HI,
3368                  "stroke", NODE_STROKE,
3369                  "stroke_mouseover", NODE_STROKE_HI,
3370                  "tip", _("<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"),
3371                  "shape", (n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE,
3372                  "size", (n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7,
3373                  NULL);
3375     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3376     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3377     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3378     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3379     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3380     sp_knot_show(n->knot);
3382     // We only create side knots and lines on demand
3383     n->p.knot = NULL;
3384     n->p.line = NULL;
3385     n->n.knot = NULL;
3386     n->n.line = NULL;
3388     sp->nodes = g_list_prepend(sp->nodes, n);
3390     return n;
3393 /**
3394  * Destroy node and its knots, link neighbors in subpath.
3395  */
3396 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3398     g_assert(node);
3399     g_assert(node->subpath);
3400     g_assert(SP_IS_KNOT(node->knot));
3401 //    g_assert(g_list_find(node->subpath->nodes, node));
3403    Inkscape::NodePath::SubPath *sp = node->subpath;
3405     if (node->selected) { // first, deselect
3406         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3407         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3408     }
3410     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3412     g_object_unref(G_OBJECT(node->knot));
3413     if (node->p.knot)
3414         g_object_unref(G_OBJECT(node->p.knot));
3415     if (node->n.knot)
3416         g_object_unref(G_OBJECT(node->n.knot));
3418     if (node->p.line)
3419         gtk_object_destroy(GTK_OBJECT(node->p.line));
3420     if (node->n.line)
3421         gtk_object_destroy(GTK_OBJECT(node->n.line));
3423     if (sp->nodes) { // there are others nodes on the subpath
3424         if (sp->closed) {
3425             if (sp->first == node) {
3426                 g_assert(sp->last == node);
3427                 sp->first = node->n.other;
3428                 sp->last = sp->first;
3429             }
3430             node->p.other->n.other = node->n.other;
3431             node->n.other->p.other = node->p.other;
3432         } else {
3433             if (sp->first == node) {
3434                 sp->first = node->n.other;
3435                 sp->first->code = NR_MOVETO;
3436             }
3437             if (sp->last == node) sp->last = node->p.other;
3438             if (node->p.other) node->p.other->n.other = node->n.other;
3439             if (node->n.other) node->n.other->p.other = node->p.other;
3440         }
3441     } else { // this was the last node on subpath
3442         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3443     }
3445     g_mem_chunk_free(nodechunk, node);
3448 /**
3449  * Returns one of the node's two knots (node sides).
3450  * \param which Indicates which side.
3451  * \return Pointer to previous node side if which==-1, next if which==1.
3452  */
3453 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3455     g_assert(node);
3457     switch (which) {
3458         case -1:
3459             return &node->p;
3460         case 1:
3461             return &node->n;
3462         default:
3463             break;
3464     }
3466     g_assert_not_reached();
3468     return NULL;
3471 /**
3472  * Return knot on other side of node.
3473  */
3474 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3476     g_assert(node);
3478     if (me == &node->p) return &node->n;
3479     if (me == &node->n) return &node->p;
3481     g_assert_not_reached();
3483     return NULL;
3486 /**
3487  * Return NRPathcode on this knot's side of the node.
3488  */
3489 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3491     g_assert(node);
3493     if (me == &node->p) {
3494         if (node->p.other) return (NRPathcode)node->code;
3495         return NR_MOVETO;
3496     }
3498     if (me == &node->n) {
3499         if (node->n.other) return (NRPathcode)node->n.other->code;
3500         return NR_MOVETO;
3501     }
3503     g_assert_not_reached();
3505     return NR_END;
3508 /**
3509  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3510  */
3511 Inkscape::NodePath::Node *
3512 sp_nodepath_get_node_by_index(int index)
3514     Inkscape::NodePath::Node *e = NULL;
3516     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3517     if (!nodepath) {
3518         return e;
3519     }
3521     //find segment
3522     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3524         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3525         int n = g_list_length(sp->nodes);
3526         if (sp->closed) {
3527             n++;
3528         }
3530         //if the piece belongs to this subpath grab it
3531         //otherwise move onto the next subpath
3532         if (index < n) {
3533             e = sp->first;
3534             for (int i = 0; i < index; ++i) {
3535                 e = e->n.other;
3536             }
3537             break;
3538         } else {
3539             if (sp->closed) {
3540                 index -= (n+1);
3541             } else {
3542                 index -= n;
3543             }
3544         }
3545     }
3547     return e;
3550 /**
3551  * Returns plain text meaning of node type.
3552  */
3553 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3555     unsigned retracted = 0;
3556     bool endnode = false;
3558     for (int which = -1; which <= 1; which += 2) {
3559         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3560         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3561             retracted ++;
3562         if (!side->other)
3563             endnode = true;
3564     }
3566     if (retracted == 0) {
3567         if (endnode) {
3568                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3569                 return _("end node");
3570         } else {
3571             switch (node->type) {
3572                 case Inkscape::NodePath::NODE_CUSP:
3573                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3574                     return _("cusp");
3575                 case Inkscape::NodePath::NODE_SMOOTH:
3576                     // TRANSLATORS: "smooth" is an adjective here
3577                     return _("smooth");
3578                 case Inkscape::NodePath::NODE_SYMM:
3579                     return _("symmetric");
3580             }
3581         }
3582     } else if (retracted == 1) {
3583         if (endnode) {
3584             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3585             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3586         } else {
3587             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3588         }
3589     } else {
3590         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3591     }
3593     return NULL;
3596 /**
3597  * Handles content of statusbar as long as node tool is active.
3598  */
3599 void
3600 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3602     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>arrow</b> keys to move nodes");
3603     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3605     gint total = 0;
3606     gint selected = 0;
3607     SPDesktop *desktop = NULL;
3609     if (nodepath) {
3610         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3611             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3612             total += g_list_length(subpath->nodes);
3613         }
3614         selected = g_list_length(nodepath->selected);
3615         desktop = nodepath->desktop;
3616     } else {
3617         desktop = SP_ACTIVE_DESKTOP;
3618     }
3620     SPEventContext *ec = desktop->event_context;
3621     if (!ec) return;
3622     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3623     if (!mc) return;
3625     if (selected == 0) {
3626         Inkscape::Selection *sel = desktop->selection;
3627         if (!sel || sel->isEmpty()) {
3628             mc->setF(Inkscape::NORMAL_MESSAGE,
3629                      _("Select a single object to edit its nodes or handles."));
3630         } else {
3631             if (nodepath) {
3632             mc->setF(Inkscape::NORMAL_MESSAGE,
3633                      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.",
3634                               "<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.",
3635                               total),
3636                      total);
3637             } else {
3638                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3639                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3640                 } else {
3641                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3642                 }
3643             }
3644         }
3645     } else if (nodepath && selected == 1) {
3646         mc->setF(Inkscape::NORMAL_MESSAGE,
3647                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3648                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3649                           total),
3650                  selected, total, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3651     } else {
3652         mc->setF(Inkscape::NORMAL_MESSAGE,
3653                  ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
3654                           "<b>%i</b> of <b>%i</b> nodes selected. %s.",
3655                           total),
3656                  selected, total, when_selected);
3657     }
3661 /*
3662   Local Variables:
3663   mode:c++
3664   c-file-style:"stroustrup"
3665   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3666   indent-tabs-mode:nil
3667   fill-column:99
3668   End:
3669 */
3670 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :