Code

e8a118646bcebd708ac1dca20002a27e741aa215
[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         node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
839         node->knot->setSize (9);
840         sp_knot_update_ctrl(node->knot);
841     } else {
842         node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
843         node->knot->setSize (7);
844         sp_knot_update_ctrl(node->knot);
845     }
847     sp_node_adjust_knots(node);
849     sp_nodepath_update_statusbar(node->subpath->nodepath);
851     return node;
854 /**
855  * Same as sp_nodepath_set_node_type(), but also converts, if necessary,
856  * adjacent segments from lines to curves.
857 */
858 void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
860     if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
861         if ((node->p.other != NULL) && (node->code == NR_LINETO || node->pos == node->p.pos)) {
862             // convert adjacent segment BEFORE to curve
863             node->code = NR_CURVETO;
864             NR::Point delta;
865             if (node->n.other != NULL)
866                 delta = node->n.other->pos - node->p.other->pos;
867             else
868                 delta = node->pos - node->p.other->pos;
869             node->p.pos = node->pos - delta / 4;
870             sp_node_ensure_ctrls(node);
871         }
873         if ((node->n.other != NULL) && (node->n.other->code == NR_LINETO || node->pos == node->n.pos)) {
874             // convert adjacent segment AFTER to curve
875             node->n.other->code = NR_CURVETO;
876             NR::Point delta;
877             if (node->p.other != NULL)
878                 delta = node->p.other->pos - node->n.other->pos;
879             else
880                 delta = node->pos - node->n.other->pos;
881             node->n.pos = node->pos - delta / 4;
882             sp_node_ensure_ctrls(node);
883         }
884     }
886     sp_nodepath_set_node_type (node, type);
889 /**
890  * Move node to point, and adjust its and neighbouring handles.
891  */
892 void sp_node_moveto(Inkscape::NodePath::Node *node, NR::Point p)
894     NR::Point delta = p - node->pos;
895     node->pos = p;
897     node->p.pos += delta;
898     node->n.pos += delta;
900     if (node->p.other) {
901         if (node->code == NR_LINETO) {
902             sp_node_adjust_knot(node, 1);
903             sp_node_adjust_knot(node->p.other, -1);
904         }
905     }
906     if (node->n.other) {
907         if (node->n.other->code == NR_LINETO) {
908             sp_node_adjust_knot(node, -1);
909             sp_node_adjust_knot(node->n.other, 1);
910         }
911     }
913     sp_node_ensure_ctrls(node);
916 /**
917  * Call sp_node_moveto() for node selection and handle possible snapping.
918  */
919 static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, NR::Coord dx, NR::Coord dy,
920                                             bool const snap = true)
922     NR::Coord best[2] = { NR_HUGE, NR_HUGE };
923     NR::Point delta(dx, dy);
924     NR::Point best_pt = delta;
926     if (snap) {
927         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
928            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
929             NR::Point p = n->pos + delta;
930             for (int dim = 0; dim < 2; dim++) {
931                 NR::Coord dist = namedview_dim_snap(nodepath->desktop->namedview,
932                                                     Inkscape::Snapper::SNAP_POINT, p,
933                                                     NR::Dim2(dim), nodepath->path);
934                 if (dist < best[dim]) {
935                     best[dim] = dist;
936                     best_pt[dim] = p[dim] - n->pos[dim];
937                 }
938             }
939         }
940     }
942     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
943        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
944         sp_node_moveto(n, n->pos + best_pt);
945     }
947     update_object(nodepath);
950 /**
951  * Move node selection to point, adjust its and neighbouring handles,
952  * handle possible snapping, and commit the change with possible undo.
953  */
954 void
955 sp_node_selected_move(gdouble dx, gdouble dy)
957     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
958     if (!nodepath) return;
960     sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
962     if (dx == 0) {
963         update_repr_keyed(nodepath, "node:move:vertical");
964     } else if (dy == 0) {
965         update_repr_keyed(nodepath, "node:move:horizontal");
966     } else {
967         update_repr(nodepath);
968     }
971 /**
972  * Move node selection off screen and commit the change.
973  */
974 void
975 sp_node_selected_move_screen(gdouble dx, gdouble dy)
977     // borrowed from sp_selection_move_screen in selection-chemistry.c
978     // we find out the current zoom factor and divide deltas by it
979     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
981     gdouble zoom = desktop->current_zoom();
982     gdouble zdx = dx / zoom;
983     gdouble zdy = dy / zoom;
985     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
986     if (!nodepath) return;
988     sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
990     if (dx == 0) {
991         update_repr_keyed(nodepath, "node:move:vertical");
992     } else if (dy == 0) {
993         update_repr_keyed(nodepath, "node:move:horizontal");
994     } else {
995         update_repr(nodepath);
996     }
999 /** If they don't yet exist, creates knot and line for the given side of the node */
1000 static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
1002     if (!side->knot) {
1003         side->knot = sp_knot_new(desktop, _("<b>Node handle</b>: drag to shape the curve; with <b>Ctrl</b> to snap angle; with <b>Alt</b> to lock length; with <b>Shift</b> to rotate both handles"));
1005         side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
1006         side->knot->setSize (7);
1007         side->knot->setAnchor (GTK_ANCHOR_CENTER);
1008         side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
1009         side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
1010         sp_knot_update_ctrl(side->knot);
1012         g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_ctrl_clicked), node);
1013         g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_ctrl_grabbed), node);
1014         g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_ctrl_ungrabbed), node);
1015         g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_ctrl_request), node);
1016         g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_ctrl_moved), node);
1017         g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_ctrl_event), node);
1018     }
1020     if (!side->line) {
1021         side->line = sp_canvas_item_new(SP_DT_CONTROLS(desktop),
1022                                         SP_TYPE_CTRLLINE, NULL);
1023     }
1026 /**
1027  * Ensure knot on side of node is visible/invisible.
1028  */
1029 static void sp_node_ensure_knot(Inkscape::NodePath::Node *node, gint which, gboolean show_knot)
1031     g_assert(node != NULL);
1033    Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
1034     NRPathcode code = sp_node_path_code_from_side(node, side);
1036     show_knot = show_knot && (code == NR_CURVETO) && (NR::L2(side->pos - node->pos) > 1e-6);
1038     if (show_knot) {
1039         if (!side->knot) {
1040             sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
1041             // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
1042             side->knot->pos = side->pos;
1043             if (side->knot->item) SP_CTRL(side->knot->item)->moveto(side->pos);
1044             sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
1045             sp_knot_show(side->knot);
1046         } else {
1047             if (side->knot->pos != side->pos) { // only if it's really moved
1048                 sp_knot_set_position(side->knot, &side->pos, 0); // this will set coords of the line as well
1049             }
1050             if (!SP_KNOT_IS_VISIBLE(side->knot)) {
1051                 sp_knot_show(side->knot);
1052             }
1053         }
1054         sp_canvas_item_show(side->line);
1055     } else {
1056         if (side->knot) {
1057             if (SP_KNOT_IS_VISIBLE(side->knot)) {
1058                 sp_knot_hide(side->knot);
1059             }
1060         }
1061         if (side->line) {
1062             sp_canvas_item_hide(side->line);
1063         }
1064     }
1067 /**
1068  * Ensure handles on node and neighbours of node are visible if selected.
1069  */
1070 static void sp_node_ensure_ctrls(Inkscape::NodePath::Node *node)
1072     g_assert(node != NULL);
1074     if (!SP_KNOT_IS_VISIBLE(node->knot)) {
1075         sp_knot_show(node->knot);
1076     }
1078     sp_knot_set_position(node->knot, &node->pos, 0);
1080     gboolean show_knots = node->selected;
1081     if (node->p.other != NULL) {
1082         if (node->p.other->selected) show_knots = TRUE;
1083     }
1084     if (node->n.other != NULL) {
1085         if (node->n.other->selected) show_knots = TRUE;
1086     }
1088     sp_node_ensure_knot(node, -1, show_knots);
1089     sp_node_ensure_knot(node, 1, show_knots);
1092 /**
1093  * Call sp_node_ensure_ctrls() for all nodes on subpath.
1094  */
1095 static void sp_nodepath_subpath_ensure_ctrls(Inkscape::NodePath::SubPath *subpath)
1097     g_assert(subpath != NULL);
1099     for (GList *l = subpath->nodes; l != NULL; l = l->next) {
1100         sp_node_ensure_ctrls((Inkscape::NodePath::Node *) l->data);
1101     }
1104 /**
1105  * Call sp_nodepath_subpath_ensure_ctrls() for all subpaths of nodepath.
1106  */
1107 static void sp_nodepath_ensure_ctrls(Inkscape::NodePath::Path *nodepath)
1109     g_assert(nodepath != NULL);
1111     for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
1112         sp_nodepath_subpath_ensure_ctrls((Inkscape::NodePath::SubPath *) l->data);
1113     }
1116 /**
1117  * Adds all selected nodes in nodepath to list.
1118  */
1119 void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
1121     StlConv<Node *>::list(l, selected);
1122 /// \todo this adds a copying, rework when the selection becomes a stl list
1125 /**
1126  * Align selected nodes on the specified axis.
1127  */
1128 void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1130     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1131         return;
1132     }
1134     if ( !nodepath->selected->next ) { // only one node selected
1135         return;
1136     }
1137    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1138     NR::Point dest(pNode->pos);
1139     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1140         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1141         if (pNode) {
1142             dest[axis] = pNode->pos[axis];
1143             sp_node_moveto(pNode, dest);
1144         }
1145     }
1146     if (axis == NR::X) {
1147         update_repr_keyed(nodepath, "node:move:vertical");
1148     } else {
1149         update_repr_keyed(nodepath, "node:move:horizontal");
1150     }
1153 /// Helper struct.
1154 struct NodeSort
1156    Inkscape::NodePath::Node *_node;
1157     NR::Coord _coord;
1158     /// \todo use vectorof pointers instead of calling copy ctor
1159     NodeSort(Inkscape::NodePath::Node *node, NR::Dim2 axis) :
1160         _node(node), _coord(node->pos[axis])
1161     {}
1163 };
1165 static bool operator<(NodeSort const &a, NodeSort const &b)
1167     return (a._coord < b._coord);
1170 /**
1171  * Distribute selected nodes on the specified axis.
1172  */
1173 void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
1175     if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
1176         return;
1177     }
1179     if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
1180         return;
1181     }
1183    Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
1184     std::vector<NodeSort> sorted;
1185     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1186         pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
1187         if (pNode) {
1188             NodeSort n(pNode, axis);
1189             sorted.push_back(n);
1190             //dest[axis] = pNode->pos[axis];
1191             //sp_node_moveto(pNode, dest);
1192         }
1193     }
1194     std::sort(sorted.begin(), sorted.end());
1195     unsigned int len = sorted.size();
1196     //overall bboxes span
1197     float dist = (sorted.back()._coord -
1198                   sorted.front()._coord);
1199     //new distance between each bbox
1200     float step = (dist) / (len - 1);
1201     float pos = sorted.front()._coord;
1202     for ( std::vector<NodeSort> ::iterator it(sorted.begin());
1203           it < sorted.end();
1204           it ++ )
1205     {
1206         NR::Point dest((*it)._node->pos);
1207         dest[axis] = pos;
1208         sp_node_moveto((*it)._node, dest);
1209         pos += step;
1210     }
1212     if (axis == NR::X) {
1213         update_repr_keyed(nodepath, "node:move:horizontal");
1214     } else {
1215         update_repr_keyed(nodepath, "node:move:vertical");
1216     }
1220 /**
1221  * Call sp_nodepath_line_add_node() for all selected segments.
1222  */
1223 void
1224 sp_node_selected_add_node(void)
1226     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1227     if (!nodepath) {
1228         return;
1229     }
1231     GList *nl = NULL;
1233     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1234        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
1235         g_assert(t->selected);
1236         if (t->p.other && t->p.other->selected) {
1237             nl = g_list_prepend(nl, t);
1238         }
1239     }
1241     while (nl) {
1242        Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
1243        Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
1244         sp_nodepath_node_select(n, TRUE, FALSE);
1245         nl = g_list_remove(nl, t);
1246     }
1248     /** \todo fixme: adjust ? */
1249     sp_nodepath_ensure_ctrls(nodepath);
1251     update_repr(nodepath);
1253     sp_nodepath_update_statusbar(nodepath);
1256 /**
1257  * Select segment nearest to point
1258  */
1259 void
1260 sp_nodepath_select_segment_near_point(SPItem * item, NR::Point p, bool toggle)
1262     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1263     if (!nodepath) {
1264         return;
1265     }
1267     Path::cut_position position = get_nearest_position_on_Path(item, p);
1269     //find segment to segment
1270     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1272     gboolean force = FALSE;
1273     if (!(e->selected && (!e->p.other || e->p.other->selected))) {
1274         force = TRUE;
1275     }
1276     sp_nodepath_node_select(e, (gboolean) toggle, force);
1277     if (e->p.other)
1278         sp_nodepath_node_select(e->p.other, TRUE, force);
1280     sp_nodepath_ensure_ctrls(nodepath);
1282     sp_nodepath_update_statusbar(nodepath);
1285 /**
1286  * Add a node nearest to point
1287  */
1288 void
1289 sp_nodepath_add_node_near_point(SPItem * item, NR::Point p)
1291     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1292     if (!nodepath) {
1293         return;
1294     }
1296     Path::cut_position position = get_nearest_position_on_Path(item, p);
1298     //find segment to split
1299     Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(position.piece);
1301     //don't know why but t seems to flip for lines
1302     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
1303         position.t = 1.0 - position.t;
1304     }
1305     Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, position.t);
1306     sp_nodepath_node_select(n, FALSE, TRUE);
1308     /* fixme: adjust ? */
1309     sp_nodepath_ensure_ctrls(nodepath);
1311     update_repr(nodepath);
1313     sp_nodepath_update_statusbar(nodepath);
1316 /*
1317  * Adjusts a segment so that t moves by a certain delta for dragging
1318  * converts lines to curves
1319  *
1320  * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
1321  * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
1322  */
1323 void
1324 sp_nodepath_curve_drag(Inkscape::NodePath::Node * e, double t, NR::Point delta, char * key)
1326     /* feel good is an arbitrary parameter that distributes the delta between handles
1327      * if t of the drag point is less than 1/6 distance form the endpoint only
1328      * the corresponding hadle is adjusted. This matches the behavior in GIMP
1329      */
1330     double feel_good;
1331     if (t <= 1.0 / 6.0)
1332         feel_good = 0;
1333     else if (t <= 0.5)
1334         feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
1335     else if (t <= 5.0 / 6.0)
1336         feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
1337     else
1338         feel_good = 1;
1340     //if we're dragging a line convert it to a curve
1341     if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
1342         sp_nodepath_set_line_type(e, NR_CURVETO);
1343     }
1345     NR::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
1346     NR::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
1347     e->p.other->n.pos += offsetcoord0;
1348     e->p.pos += offsetcoord1;
1350     // adjust controls of adjacent segments where necessary
1351     sp_node_adjust_knot(e,1);
1352     sp_node_adjust_knot(e->p.other,-1);
1354     sp_nodepath_ensure_ctrls(e->subpath->nodepath);
1356     update_repr_keyed(e->subpath->nodepath, key);
1358     sp_nodepath_update_statusbar(e->subpath->nodepath);
1362 /**
1363  * Call sp_nodepath_break() for all selected segments.
1364  */
1365 void sp_node_selected_break()
1367     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1368     if (!nodepath) return;
1370     GList *temp = NULL;
1371     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1372        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1373        Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
1374         if (nn == NULL) continue; // no break, no new node
1375         temp = g_list_prepend(temp, nn);
1376     }
1378     if (temp) {
1379         sp_nodepath_deselect(nodepath);
1380     }
1381     for (GList *l = temp; l != NULL; l = l->next) {
1382         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1383     }
1385     sp_nodepath_ensure_ctrls(nodepath);
1387     update_repr(nodepath);
1390 /**
1391  * Duplicate the selected node(s).
1392  */
1393 void sp_node_selected_duplicate()
1395     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1396     if (!nodepath) {
1397         return;
1398     }
1400     GList *temp = NULL;
1401     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1402        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1403        Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
1404         if (nn == NULL) continue; // could not duplicate
1405         temp = g_list_prepend(temp, nn);
1406     }
1408     if (temp) {
1409         sp_nodepath_deselect(nodepath);
1410     }
1411     for (GList *l = temp; l != NULL; l = l->next) {
1412         sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
1413     }
1415     sp_nodepath_ensure_ctrls(nodepath);
1417     update_repr(nodepath);
1420 /**
1421  *  Join two nodes by merging them into one.
1422  */
1423 void sp_node_selected_join()
1425     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1426     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1428     if (g_list_length(nodepath->selected) != 2) {
1429         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1430         return;
1431     }
1433    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1434    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1436     g_assert(a != b);
1437     g_assert(a->p.other || a->n.other);
1438     g_assert(b->p.other || b->n.other);
1440     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1441         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1442         return;
1443     }
1445     /* a and b are endpoints */
1447     NR::Point c = (a->pos + b->pos) / 2;
1449     if (a->subpath == b->subpath) {
1450        Inkscape::NodePath::SubPath *sp = a->subpath;
1451         sp_nodepath_subpath_close(sp);
1453         sp_nodepath_ensure_ctrls(sp->nodepath);
1455         update_repr(nodepath);
1457         return;
1458     }
1460     /* a and b are separate subpaths */
1461    Inkscape::NodePath::SubPath *sa = a->subpath;
1462    Inkscape::NodePath::SubPath *sb = b->subpath;
1463     NR::Point p;
1464    Inkscape::NodePath::Node *n;
1465     NRPathcode code;
1466     if (a == sa->first) {
1467         p = sa->first->n.pos;
1468         code = (NRPathcode)sa->first->n.other->code;
1469        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1470         n = sa->last;
1471         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1472         n = n->p.other;
1473         while (n) {
1474             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1475             n = n->p.other;
1476             if (n == sa->first) n = NULL;
1477         }
1478         sp_nodepath_subpath_destroy(sa);
1479         sa = t;
1480     } else if (a == sa->last) {
1481         p = sa->last->p.pos;
1482         code = (NRPathcode)sa->last->code;
1483         sp_nodepath_node_destroy(sa->last);
1484     } else {
1485         code = NR_END;
1486         g_assert_not_reached();
1487     }
1489     if (b == sb->first) {
1490         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
1491         for (n = sb->first->n.other; n != NULL; n = n->n.other) {
1492             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1493         }
1494     } else if (b == sb->last) {
1495         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
1496         for (n = sb->last->p.other; n != NULL; n = n->p.other) {
1497             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1498         }
1499     } else {
1500         g_assert_not_reached();
1501     }
1502     /* and now destroy sb */
1504     sp_nodepath_subpath_destroy(sb);
1506     sp_nodepath_ensure_ctrls(sa->nodepath);
1508     update_repr(nodepath);
1510     sp_nodepath_update_statusbar(nodepath);
1513 /**
1514  *  Join two nodes by adding a segment between them.
1515  */
1516 void sp_node_selected_join_segment()
1518     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1519     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1521     if (g_list_length(nodepath->selected) != 2) {
1522         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1523         return;
1524     }
1526    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1527    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1529     g_assert(a != b);
1530     g_assert(a->p.other || a->n.other);
1531     g_assert(b->p.other || b->n.other);
1533     if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
1534         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
1535         return;
1536     }
1538     if (a->subpath == b->subpath) {
1539        Inkscape::NodePath::SubPath *sp = a->subpath;
1541         /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
1542         sp->closed = TRUE;
1544         sp->first->p.other = sp->last;
1545         sp->last->n.other  = sp->first;
1547         sp_node_control_mirror_p_to_n(sp->last);
1548         sp_node_control_mirror_n_to_p(sp->first);
1550         sp->first->code = sp->last->code;
1551         sp->first       = sp->last;
1553         sp_nodepath_ensure_ctrls(sp->nodepath);
1555         update_repr(nodepath);
1557         return;
1558     }
1560     /* a and b are separate subpaths */
1561    Inkscape::NodePath::SubPath *sa = a->subpath;
1562    Inkscape::NodePath::SubPath *sb = b->subpath;
1564    Inkscape::NodePath::Node *n;
1565     NR::Point p;
1566     NRPathcode code;
1567     if (a == sa->first) {
1568         code = (NRPathcode) sa->first->n.other->code;
1569        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
1570         n = sa->last;
1571         sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
1572         for (n = n->p.other; n != NULL; n = n->p.other) {
1573             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1574         }
1575         sp_nodepath_subpath_destroy(sa);
1576         sa = t;
1577     } else if (a == sa->last) {
1578         code = (NRPathcode)sa->last->code;
1579     } else {
1580         code = NR_END;
1581         g_assert_not_reached();
1582     }
1584     if (b == sb->first) {
1585         n = sb->first;
1586         sp_node_control_mirror_p_to_n(sa->last);
1587         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
1588         sp_node_control_mirror_n_to_p(sa->last);
1589         for (n = n->n.other; n != NULL; n = n->n.other) {
1590             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
1591         }
1592     } else if (b == sb->last) {
1593         n = sb->last;
1594         sp_node_control_mirror_p_to_n(sa->last);
1595         sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
1596         sp_node_control_mirror_n_to_p(sa->last);
1597         for (n = n->p.other; n != NULL; n = n->p.other) {
1598             sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
1599         }
1600     } else {
1601         g_assert_not_reached();
1602     }
1603     /* and now destroy sb */
1605     sp_nodepath_subpath_destroy(sb);
1607     sp_nodepath_ensure_ctrls(sa->nodepath);
1609     update_repr(nodepath);
1612 /**
1613  * Delete one or more selected nodes.
1614  */
1615 void sp_node_selected_delete()
1617     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1618     if (!nodepath) return;
1619     if (!nodepath->selected) return;
1621     /** \todo fixme: do it the right way */
1622     while (nodepath->selected) {
1623        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
1624         sp_nodepath_node_destroy(node);
1625     }
1628     //clean up the nodepath (such as for trivial subpaths)
1629     sp_nodepath_cleanup(nodepath);
1631     sp_nodepath_ensure_ctrls(nodepath);
1633     // if the entire nodepath is removed, delete the selected object.
1634     if (nodepath->subpaths == NULL ||
1635         sp_nodepath_get_node_count(nodepath) < 2) {
1636         SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
1637         sp_nodepath_destroy(nodepath);
1638         sp_selection_delete();
1639         sp_document_done (document);
1640         return;
1641     }
1643     update_repr(nodepath);
1645     sp_nodepath_update_statusbar(nodepath);
1648 /**
1649  * Delete one or more segments between two selected nodes.
1650  * This is the code for 'split'.
1651  */
1652 void
1653 sp_node_selected_delete_segment(void)
1655    Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
1656    Inkscape::NodePath::Node *curr, *next;     //Iterators
1658     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1659     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1661     if (g_list_length(nodepath->selected) != 2) {
1662         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1663                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1664         return;
1665     }
1667     //Selected nodes, not inclusive
1668    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
1669    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
1671     if ( ( a==b)                       ||  //same node
1672          (a->subpath  != b->subpath )  ||  //not the same path
1673          (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
1674          (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
1675     {
1676         nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1677                                                  _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
1678         return;
1679     }
1681     //###########################################
1682     //# BEGIN EDITS
1683     //###########################################
1684     //##################################
1685     //# CLOSED PATH
1686     //##################################
1687     if (a->subpath->closed) {
1690         gboolean reversed = FALSE;
1692         //Since we can go in a circle, we need to find the shorter distance.
1693         //  a->b or b->a
1694         start = end = NULL;
1695         int distance    = 0;
1696         int minDistance = 0;
1697         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1698             if (curr==b) {
1699                 //printf("a to b:%d\n", distance);
1700                 start = a;//go from a to b
1701                 end   = b;
1702                 minDistance = distance;
1703                 //printf("A to B :\n");
1704                 break;
1705             }
1706             distance++;
1707         }
1709         //try again, the other direction
1710         distance = 0;
1711         for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1712             if (curr==a) {
1713                 //printf("b to a:%d\n", distance);
1714                 if (distance < minDistance) {
1715                     start    = b;  //we go from b to a
1716                     end      = a;
1717                     reversed = TRUE;
1718                     //printf("B to A\n");
1719                 }
1720                 break;
1721             }
1722             distance++;
1723         }
1726         //Copy everything from 'end' to 'start' to a new subpath
1727        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1728         for (curr=end ; curr ; curr=curr->n.other) {
1729             NRPathcode code = (NRPathcode) curr->code;
1730             if (curr == end)
1731                 code = NR_MOVETO;
1732             sp_nodepath_node_new(t, NULL,
1733                                  (Inkscape::NodePath::NodeType)curr->type, code,
1734                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1735             if (curr == start)
1736                 break;
1737         }
1738         sp_nodepath_subpath_destroy(a->subpath);
1741     }
1745     //##################################
1746     //# OPEN PATH
1747     //##################################
1748     else {
1750         //We need to get the direction of the list between A and B
1751         //Can we walk from a to b?
1752         start = end = NULL;
1753         for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
1754             if (curr==b) {
1755                 start = a;  //did it!  we go from a to b
1756                 end   = b;
1757                 //printf("A to B\n");
1758                 break;
1759             }
1760         }
1761         if (!start) {//didn't work?  let's try the other direction
1762             for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
1763                 if (curr==a) {
1764                     start = b;  //did it!  we go from b to a
1765                     end   = a;
1766                     //printf("B to A\n");
1767                     break;
1768                 }
1769             }
1770         }
1771         if (!start) {
1772             nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
1773                                                      _("Cannot find path between nodes."));
1774             return;
1775         }
1779         //Copy everything after 'end' to a new subpath
1780        Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
1781         for (curr=end ; curr ; curr=curr->n.other) {
1782             sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, (NRPathcode)curr->code,
1783                                  &curr->p.pos, &curr->pos, &curr->n.pos);
1784         }
1786         //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
1787         for (curr = start->n.other ; curr  ; curr=next) {
1788             next = curr->n.other;
1789             sp_nodepath_node_destroy(curr);
1790         }
1792     }
1793     //###########################################
1794     //# END EDITS
1795     //###########################################
1797     //clean up the nodepath (such as for trivial subpaths)
1798     sp_nodepath_cleanup(nodepath);
1800     sp_nodepath_ensure_ctrls(nodepath);
1802     update_repr(nodepath);
1804     // if the entire nodepath is removed, delete the selected object.
1805     if (nodepath->subpaths == NULL ||
1806         sp_nodepath_get_node_count(nodepath) < 2) {
1807         sp_nodepath_destroy(nodepath);
1808         sp_selection_delete();
1809         return;
1810     }
1812     sp_nodepath_update_statusbar(nodepath);
1815 /**
1816  * Call sp_nodepath_set_line() for all selected segments.
1817  */
1818 void
1819 sp_node_selected_set_line_type(NRPathcode code)
1821     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1822     if (nodepath == NULL) return;
1824     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1825        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1826         g_assert(n->selected);
1827         if (n->p.other && n->p.other->selected) {
1828             sp_nodepath_set_line_type(n, code);
1829         }
1830     }
1832     update_repr(nodepath);
1835 /**
1836  * Call sp_nodepath_convert_node_type() for all selected nodes.
1837  */
1838 void
1839 sp_node_selected_set_type(Inkscape::NodePath::NodeType type)
1841     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
1842     if (nodepath == NULL) return;
1844     for (GList *l = nodepath->selected; l != NULL; l = l->next) {
1845         sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
1846     }
1848     update_repr(nodepath);
1851 /**
1852  * Change select status of node, update its own and neighbour handles.
1853  */
1854 static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
1856     node->selected = selected;
1858     if (selected) {
1859         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 11 : 9);
1860         node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
1861         node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
1862         sp_knot_update_ctrl(node->knot);
1863     } else {
1864         node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP) ? 9 : 7);
1865         node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
1866         node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
1867         sp_knot_update_ctrl(node->knot);
1868     }
1870     sp_node_ensure_ctrls(node);
1871     if (node->n.other) sp_node_ensure_ctrls(node->n.other);
1872     if (node->p.other) sp_node_ensure_ctrls(node->p.other);
1875 /**
1876 \brief Select a node
1877 \param node     The node to select
1878 \param incremental   If true, add to selection, otherwise deselect others
1879 \param override   If true, always select this node, otherwise toggle selected status
1880 */
1881 static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
1883     Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
1885     if (incremental) {
1886         if (override) {
1887             if (!g_list_find(nodepath->selected, node)) {
1888                 nodepath->selected = g_list_prepend(nodepath->selected, node);
1889             }
1890             sp_node_set_selected(node, TRUE);
1891         } else { // toggle
1892             if (node->selected) {
1893                 g_assert(g_list_find(nodepath->selected, node));
1894                 nodepath->selected = g_list_remove(nodepath->selected, node);
1895             } else {
1896                 g_assert(!g_list_find(nodepath->selected, node));
1897                 nodepath->selected = g_list_prepend(nodepath->selected, node);
1898             }
1899             sp_node_set_selected(node, !node->selected);
1900         }
1901     } else {
1902         sp_nodepath_deselect(nodepath);
1903         nodepath->selected = g_list_prepend(nodepath->selected, node);
1904         sp_node_set_selected(node, TRUE);
1905     }
1907     sp_nodepath_update_statusbar(nodepath);
1911 /**
1912 \brief Deselect all nodes in the nodepath
1913 */
1914 void
1915 sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
1917     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1919     while (nodepath->selected) {
1920         sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
1921         nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
1922     }
1923     sp_nodepath_update_statusbar(nodepath);
1926 /**
1927 \brief Select or invert selection of all nodes in the nodepath
1928 */
1929 void
1930 sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
1932     if (!nodepath) return;
1934     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1935        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
1936         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1937            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1938            sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
1939         }
1940     }
1943 /**
1944  * If nothing selected, does the same as sp_nodepath_select_all();
1945  * otherwise selects/inverts all nodes in all subpaths that have selected nodes
1946  * (i.e., similar to "select all in layer", with the "selected" subpaths
1947  * being treated as "layers" in the path).
1948  */
1949 void
1950 sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
1952     if (!nodepath) return;
1954     if (g_list_length (nodepath->selected) == 0) {
1955         sp_nodepath_select_all (nodepath, invert);
1956         return;
1957     }
1959     GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
1960     GSList *subpaths = NULL;
1962     for (GList *l = copy; l != NULL; l = l->next) {
1963         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
1964         Inkscape::NodePath::SubPath *subpath = n->subpath;
1965         if (!g_slist_find (subpaths, subpath))
1966             subpaths = g_slist_prepend (subpaths, subpath);
1967     }
1969     for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
1970         Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
1971         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1972             Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1973             sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
1974         }
1975     }
1977     g_slist_free (subpaths);
1978     g_list_free (copy);
1981 /**
1982  * \brief Select the node after the last selected; if none is selected,
1983  * select the first within path.
1984  */
1985 void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
1987     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
1989    Inkscape::NodePath::Node *last = NULL;
1990     if (nodepath->selected) {
1991         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
1992            Inkscape::NodePath::SubPath *subpath, *subpath_next;
1993             subpath = (Inkscape::NodePath::SubPath *) spl->data;
1994             for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
1995                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
1996                 if (node->selected) {
1997                     if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
1998                         if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
1999                             if (spl->next) { // there's a next subpath
2000                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2001                                 last = subpath_next->first;
2002                             } else if (spl->prev) { // there's a previous subpath
2003                                 last = NULL; // to be set later to the first node of first subpath
2004                             } else {
2005                                 last = node->n.other;
2006                             }
2007                         } else {
2008                             last = node->n.other;
2009                         }
2010                     } else {
2011                         if (node->n.other) {
2012                             last = node->n.other;
2013                         } else {
2014                             if (spl->next) { // there's a next subpath
2015                                 subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
2016                                 last = subpath_next->first;
2017                             } else if (spl->prev) { // there's a previous subpath
2018                                 last = NULL; // to be set later to the first node of first subpath
2019                             } else {
2020                                 last = (Inkscape::NodePath::Node *) subpath->first;
2021                             }
2022                         }
2023                     }
2024                 }
2025             }
2026         }
2027         sp_nodepath_deselect(nodepath);
2028     }
2030     if (last) { // there's at least one more node after selected
2031         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2032     } else { // no more nodes, select the first one in first subpath
2033        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
2034         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
2035     }
2038 /**
2039  * \brief Select the node before the first selected; if none is selected,
2040  * select the last within path
2041  */
2042 void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
2044     if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
2046    Inkscape::NodePath::Node *last = NULL;
2047     if (nodepath->selected) {
2048         for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
2049            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2050             for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
2051                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2052                 if (node->selected) {
2053                     if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
2054                         if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
2055                             if (spl->prev) { // there's a prev subpath
2056                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2057                                 last = subpath_prev->last;
2058                             } else if (spl->next) { // there's a next subpath
2059                                 last = NULL; // to be set later to the last node of last subpath
2060                             } else {
2061                                 last = node->p.other;
2062                             }
2063                         } else {
2064                             last = node->p.other;
2065                         }
2066                     } else {
2067                         if (node->p.other) {
2068                             last = node->p.other;
2069                         } else {
2070                             if (spl->prev) { // there's a prev subpath
2071                                Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
2072                                 last = subpath_prev->last;
2073                             } else if (spl->next) { // there's a next subpath
2074                                 last = NULL; // to be set later to the last node of last subpath
2075                             } else {
2076                                 last = (Inkscape::NodePath::Node *) subpath->last;
2077                             }
2078                         }
2079                     }
2080                 }
2081             }
2082         }
2083         sp_nodepath_deselect(nodepath);
2084     }
2086     if (last) { // there's at least one more node before selected
2087         sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
2088     } else { // no more nodes, select the last one in last subpath
2089         GList *spl = g_list_last(nodepath->subpaths);
2090        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2091         sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
2092     }
2095 /**
2096  * \brief Select all nodes that are within the rectangle.
2097  */
2098 void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, NR::Rect const &b, gboolean incremental)
2100     if (!incremental) {
2101         sp_nodepath_deselect(nodepath);
2102     }
2104     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2105        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2106         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2107            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2109             if (b.contains(node->pos)) {
2110                 sp_nodepath_node_select(node, TRUE, TRUE);
2111             }
2112         }
2113     }
2116 /**
2117 \brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
2118 */
2119 GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
2121     if (!nodepath->selected) {
2122         return NULL;
2123     }
2125     GList *r = NULL;
2126     guint i = 0;
2127     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2128        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2129         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2130            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2131             i++;
2132             if (node->selected) {
2133                 r = g_list_append(r, GINT_TO_POINTER(i));
2134             }
2135         }
2136     }
2137     return r;
2140 /**
2141 \brief  Restores selection by selecting nodes whose positions are in the list
2142 */
2143 void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
2145     sp_nodepath_deselect(nodepath);
2147     guint i = 0;
2148     for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
2149        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
2150         for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
2151            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
2152             i++;
2153             if (g_list_find(r, GINT_TO_POINTER(i))) {
2154                 sp_nodepath_node_select(node, TRUE, TRUE);
2155             }
2156         }
2157     }
2161 /**
2162 \brief Adjusts control point according to node type and line code.
2163 */
2164 static void sp_node_adjust_knot(Inkscape::NodePath::Node *node, gint which_adjust)
2166     double len, otherlen, linelen;
2168     g_assert(node);
2170    Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
2171    Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
2173     /** \todo fixme: */
2174     if (me->other == NULL) return;
2175     if (other->other == NULL) return;
2177     /* I have line */
2179     NRPathcode mecode, ocode;
2180     if (which_adjust == 1) {
2181         mecode = (NRPathcode)me->other->code;
2182         ocode = (NRPathcode)node->code;
2183     } else {
2184         mecode = (NRPathcode)node->code;
2185         ocode = (NRPathcode)other->other->code;
2186     }
2188     if (mecode == NR_LINETO) return;
2190     /* I am curve */
2192     if (other->other == NULL) return;
2194     /* Other has line */
2196     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2198     NR::Point delta;
2199     if (ocode == NR_LINETO) {
2200         /* other is lineto, we are either smooth or symm */
2201        Inkscape::NodePath::Node *othernode = other->other;
2202         len = NR::L2(me->pos - node->pos);
2203         delta = node->pos - othernode->pos;
2204         linelen = NR::L2(delta);
2205         if (linelen < 1e-18) return;
2207         me->pos = node->pos + (len / linelen)*delta;
2209         sp_node_ensure_ctrls(node);
2210         return;
2211     }
2213     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2215         me->pos = 2 * node->pos - other->pos;
2217         sp_node_ensure_ctrls(node);
2218         return;
2219     }
2221     /* We are smooth */
2223     len = NR::L2(me->pos - node->pos);
2224     delta = other->pos - node->pos;
2225     otherlen = NR::L2(delta);
2226     if (otherlen < 1e-18) return;
2228     me->pos = node->pos - (len / otherlen) * delta;
2230     sp_node_ensure_ctrls(node);
2233 /**
2234  \brief Adjusts control point according to node type and line code
2235  */
2236 static void sp_node_adjust_knots(Inkscape::NodePath::Node *node)
2238     g_assert(node);
2240     if (node->type == Inkscape::NodePath::NODE_CUSP) return;
2242     /* we are either smooth or symm */
2244     if (node->p.other == NULL) return;
2246     if (node->n.other == NULL) return;
2248     if (node->code == NR_LINETO) {
2249         if (node->n.other->code == NR_LINETO) return;
2250         sp_node_adjust_knot(node, 1);
2251         sp_node_ensure_ctrls(node);
2252         return;
2253     }
2255     if (node->n.other->code == NR_LINETO) {
2256         if (node->code == NR_LINETO) return;
2257         sp_node_adjust_knot(node, -1);
2258         sp_node_ensure_ctrls(node);
2259         return;
2260     }
2262     /* both are curves */
2264     NR::Point const delta( node->n.pos - node->p.pos );
2266     if (node->type == Inkscape::NodePath::NODE_SYMM) {
2267         node->p.pos = node->pos - delta / 2;
2268         node->n.pos = node->pos + delta / 2;
2269         sp_node_ensure_ctrls(node);
2270         return;
2271     }
2273     /* We are smooth */
2275     double plen = NR::L2(node->p.pos - node->pos);
2276     if (plen < 1e-18) return;
2277     double nlen = NR::L2(node->n.pos - node->pos);
2278     if (nlen < 1e-18) return;
2279     node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
2280     node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
2281     sp_node_ensure_ctrls(node);
2284 /**
2285  * Knot events handler callback.
2286  */
2287 static gboolean node_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2289     gboolean ret = FALSE;
2290     switch (event->type) {
2291         case GDK_ENTER_NOTIFY:
2292             active_node = n;
2293             break;
2294         case GDK_LEAVE_NOTIFY:
2295             active_node = NULL;
2296             break;
2297         case GDK_KEY_PRESS:
2298             switch (get_group0_keyval (&event->key)) {
2299                 case GDK_space:
2300                     if (event->key.state & GDK_BUTTON1_MASK) {
2301                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2302                         stamp_repr(nodepath);
2303                         ret = TRUE;
2304                     }
2305                     break;
2306                 default:
2307                     break;
2308             }
2309             break;
2310         default:
2311             break;
2312     }
2314     return ret;
2317 /**
2318  * Handle keypress on node; directly called.
2319  */
2320 gboolean node_key(GdkEvent *event)
2322     Inkscape::NodePath::Path *np;
2324     // there is no way to verify nodes so set active_node to nil when deleting!!
2325     if (active_node == NULL) return FALSE;
2327     if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
2328         gint ret = FALSE;
2329         switch (get_group0_keyval (&event->key)) {
2330             /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
2331             case GDK_BackSpace:
2332                 np = active_node->subpath->nodepath;
2333                 sp_nodepath_node_destroy(active_node);
2334                 update_repr(np);
2335                 active_node = NULL;
2336                 ret = TRUE;
2337                 break;
2338             case GDK_c:
2339                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_CUSP);
2340                 ret = TRUE;
2341                 break;
2342             case GDK_s:
2343                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SMOOTH);
2344                 ret = TRUE;
2345                 break;
2346             case GDK_y:
2347                 sp_nodepath_set_node_type(active_node,Inkscape::NodePath::NODE_SYMM);
2348                 ret = TRUE;
2349                 break;
2350             case GDK_b:
2351                 sp_nodepath_node_break(active_node);
2352                 ret = TRUE;
2353                 break;
2354         }
2355         return ret;
2356     }
2357     return FALSE;
2360 /**
2361  * Mouseclick on node callback.
2362  */
2363 static void node_clicked(SPKnot *knot, guint state, gpointer data)
2365    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2367     if (state & GDK_CONTROL_MASK) {
2368         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2370         if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
2371             if (n->type == Inkscape::NodePath::NODE_CUSP) {
2372                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
2373             } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
2374                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
2375             } else {
2376                 sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
2377             }
2378             update_repr(nodepath);
2379             sp_nodepath_update_statusbar(nodepath);
2381         } else { //ctrl+alt+click: delete node
2382             sp_nodepath_node_destroy(n);
2383             //clean up the nodepath (such as for trivial subpaths)
2384             sp_nodepath_cleanup(nodepath);
2386             // if the entire nodepath is removed, delete the selected object.
2387             if (nodepath->subpaths == NULL ||
2388                 sp_nodepath_get_node_count(nodepath) < 2) {
2389                 SPDocument *document = SP_DT_DOCUMENT (nodepath->desktop);
2390                 sp_nodepath_destroy(nodepath);
2391                 sp_selection_delete();
2392                 sp_document_done (document);
2394             } else {
2395                 sp_nodepath_ensure_ctrls(nodepath);
2396                 update_repr(nodepath);
2397                 sp_nodepath_update_statusbar(nodepath);
2398             }
2399         }
2401     } else {
2402         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2403     }
2406 /**
2407  * Mouse grabbed node callback.
2408  */
2409 static void node_grabbed(SPKnot *knot, guint state, gpointer data)
2411    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2413     n->origin = knot->pos;
2415     if (!n->selected) {
2416         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2417     }
2420 /**
2421  * Mouse ungrabbed node callback.
2422  */
2423 static void node_ungrabbed(SPKnot *knot, guint state, gpointer data)
2425    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2427    n->dragging_out = NULL;
2429    update_repr(n->subpath->nodepath);
2432 /**
2433  * The point on a line, given by its angle, closest to the given point.
2434  * \param p  A point.
2435  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2436  * \param closest  Pointer to the point struct where the result is stored.
2437  * \todo FIXME: use dot product perhaps?
2438  */
2439 static void point_line_closest(NR::Point *p, double a, NR::Point *closest)
2441     if (a == HUGE_VAL) { // vertical
2442         *closest = NR::Point(0, (*p)[NR::Y]);
2443     } else {
2444         (*closest)[NR::X] = ( a * (*p)[NR::Y] + (*p)[NR::X]) / (a*a + 1);
2445         (*closest)[NR::Y] = a * (*closest)[NR::X];
2446     }
2449 /**
2450  * Distance from the point to a line given by its angle.
2451  * \param p  A point.
2452  * \param a  Angle of the line; it is assumed to go through coordinate origin.
2453  */
2454 static double point_line_distance(NR::Point *p, double a)
2456     NR::Point c;
2457     point_line_closest(p, a, &c);
2458     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]));
2461 /**
2462  * Callback for node "request" signal.
2463  * \todo fixme: This goes to "moved" event? (lauris)
2464  */
2465 static gboolean
2466 node_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2468     double yn, xn, yp, xp;
2469     double an, ap, na, pa;
2470     double d_an, d_ap, d_na, d_pa;
2471     gboolean collinear = FALSE;
2472     NR::Point c;
2473     NR::Point pr;
2475    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2477    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
2478    if (((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos))) || n->dragging_out) {
2480        NR::Point mouse = (*p);
2482        if (!n->dragging_out) {
2483            // This is the first drag-out event; find out which handle to drag out
2484            double appr_n = (n->n.other ? NR::L2(n->n.other->pos - n->pos) - NR::L2(n->n.other->pos - (*p)) : -HUGE_VAL);
2485            double appr_p = (n->p.other ? NR::L2(n->p.other->pos - n->pos) - NR::L2(n->p.other->pos - (*p)) : -HUGE_VAL);
2487            if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
2488                return FALSE;
2490            Inkscape::NodePath::NodeSide *opposite;
2491            if (appr_p > appr_n) { // closer to p
2492                n->dragging_out = &n->p;
2493                opposite = &n->n;
2494                n->code = NR_CURVETO;
2495            } else if (appr_p < appr_n) { // closer to n
2496                n->dragging_out = &n->n;
2497                opposite = &n->p;
2498                n->n.other->code = NR_CURVETO;
2499            } else { // p and n nodes are the same
2500                if (n->n.pos != n->pos) { // n handle already dragged, drag p
2501                    n->dragging_out = &n->p;
2502                    opposite = &n->n;
2503                    n->code = NR_CURVETO;
2504                } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
2505                    n->dragging_out = &n->n;
2506                    opposite = &n->p;
2507                    n->n.other->code = NR_CURVETO;
2508                } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
2509                    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);
2510                    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);
2511                    if (appr_other_p > appr_other_n) { // closer to other's p handle
2512                        n->dragging_out = &n->n;
2513                        opposite = &n->p;
2514                        n->n.other->code = NR_CURVETO;
2515                    } else { // closer to other's n handle
2516                        n->dragging_out = &n->p;
2517                        opposite = &n->n;
2518                        n->code = NR_CURVETO;
2519                    }
2520                }
2521            }
2523            // if there's another handle, make sure the one we drag out starts parallel to it
2524            if (opposite->pos != n->pos) {
2525                mouse = n->pos - NR::L2(mouse - n->pos) * NR::unit_vector(opposite->pos - n->pos);
2526            }
2528            // knots might not be created yet!
2529            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
2530            sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
2531        }
2533        // pass this on to the handle-moved callback
2534        node_ctrl_moved(n->dragging_out->knot, &mouse, state, (gpointer) n);
2535        sp_node_ensure_ctrls(n);
2536        return TRUE;
2537    }
2539     if (state & GDK_CONTROL_MASK) { // constrained motion
2541         // calculate relative distances of handles
2542         // n handle:
2543         yn = n->n.pos[NR::Y] - n->pos[NR::Y];
2544         xn = n->n.pos[NR::X] - n->pos[NR::X];
2545         // if there's no n handle (straight line), see if we can use the direction to the next point on path
2546         if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
2547             if (n->n.other) { // if there is the next point
2548                 if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
2549                     yn = n->n.other->pos[NR::Y] - n->origin[NR::Y]; // use origin because otherwise the direction will change as you drag
2550                     xn = n->n.other->pos[NR::X] - n->origin[NR::X];
2551             }
2552         }
2553         if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
2554         if (yn < 0) { xn = -xn; yn = -yn; }
2556         // p handle:
2557         yp = n->p.pos[NR::Y] - n->pos[NR::Y];
2558         xp = n->p.pos[NR::X] - n->pos[NR::X];
2559         // if there's no p handle (straight line), see if we can use the direction to the prev point on path
2560         if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
2561             if (n->p.other) {
2562                 if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
2563                     yp = n->p.other->pos[NR::Y] - n->origin[NR::Y];
2564                     xp = n->p.other->pos[NR::X] - n->origin[NR::X];
2565             }
2566         }
2567         if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
2568         if (yp < 0) { xp = -xp; yp = -yp; }
2570         if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
2571             // sliding on handles, only if at least one of the handles is non-vertical
2572             // (otherwise it's the same as ctrl+drag anyway)
2574             // calculate angles of the control handles
2575             if (xn == 0) {
2576                 if (yn == 0) { // no handle, consider it the continuation of the other one
2577                     an = 0;
2578                     collinear = TRUE;
2579                 }
2580                 else an = 0; // vertical; set the angle to horizontal
2581             } else an = yn/xn;
2583             if (xp == 0) {
2584                 if (yp == 0) { // no handle, consider it the continuation of the other one
2585                     ap = an;
2586                 }
2587                 else ap = 0; // vertical; set the angle to horizontal
2588             } else  ap = yp/xp;
2590             if (collinear) an = ap;
2592             // angles of the perpendiculars; HUGE_VAL means vertical
2593             if (an == 0) na = HUGE_VAL; else na = -1/an;
2594             if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
2596             //g_print("an %g    ap %g\n", an, ap);
2598             // mouse point relative to the node's original pos
2599             pr = (*p) - n->origin;
2601             // distances to the four lines (two handles and two perpendiculars)
2602             d_an = point_line_distance(&pr, an);
2603             d_na = point_line_distance(&pr, na);
2604             d_ap = point_line_distance(&pr, ap);
2605             d_pa = point_line_distance(&pr, pa);
2607             // find out which line is the closest, save its closest point in c
2608             if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
2609                 point_line_closest(&pr, an, &c);
2610             } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
2611                 point_line_closest(&pr, ap, &c);
2612             } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
2613                 point_line_closest(&pr, na, &c);
2614             } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
2615                 point_line_closest(&pr, pa, &c);
2616             }
2618             // move the node to the closest point
2619             sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2620                                             n->origin[NR::X] + c[NR::X] - n->pos[NR::X],
2621                                             n->origin[NR::Y] + c[NR::Y] - n->pos[NR::Y]);
2623         } else {  // constraining to hor/vert
2625             if (fabs((*p)[NR::X] - n->origin[NR::X]) > fabs((*p)[NR::Y] - n->origin[NR::Y])) { // snap to hor
2626                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, (*p)[NR::X] - n->pos[NR::X], n->origin[NR::Y] - n->pos[NR::Y]);
2627             } else { // snap to vert
2628                 sp_nodepath_selected_nodes_move(n->subpath->nodepath, n->origin[NR::X] - n->pos[NR::X], (*p)[NR::Y] - n->pos[NR::Y]);
2629             }
2630         }
2631     } else { // move freely
2632         sp_nodepath_selected_nodes_move(n->subpath->nodepath,
2633                                         (*p)[NR::X] - n->pos[NR::X],
2634                                         (*p)[NR::Y] - n->pos[NR::Y],
2635                                         (state & GDK_SHIFT_MASK) == 0);
2636     }
2638     n->subpath->nodepath->desktop->scroll_to_point(p);
2640     return TRUE;
2643 /**
2644  * Node handle clicked callback.
2645  */
2646 static void node_ctrl_clicked(SPKnot *knot, guint state, gpointer data)
2648    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2650     if (state & GDK_CONTROL_MASK) { // "delete" handle
2651         if (n->p.knot == knot) {
2652             n->p.pos = n->pos;
2653         } else if (n->n.knot == knot) {
2654             n->n.pos = n->pos;
2655         }
2656         sp_node_ensure_ctrls(n);
2657         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2658         update_repr(nodepath);
2659         sp_nodepath_update_statusbar(nodepath);
2661     } else { // just select or add to selection, depending in Shift
2662         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2663     }
2666 /**
2667  * Node handle grabbed callback.
2668  */
2669 static void node_ctrl_grabbed(SPKnot *knot, guint state, gpointer data)
2671    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2673     if (!n->selected) {
2674         sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
2675     }
2677     // remember the origin of the control
2678     if (n->p.knot == knot) {
2679         n->p.origin = n->p.pos - n->pos;
2680     } else if (n->n.knot == knot) {
2681         n->n.origin = n->n.pos - n->pos;
2682     } else {
2683         g_assert_not_reached();
2684     }
2688 /**
2689  * Node handle ungrabbed callback.
2690  */
2691 static void node_ctrl_ungrabbed(SPKnot *knot, guint state, gpointer data)
2693    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2695     // forget origin and set knot position once more (because it can be wrong now due to restrictions)
2696     if (n->p.knot == knot) {
2697         n->p.origin.a = 0;
2698         sp_knot_set_position(knot, &n->p.pos, state);
2699     } else if (n->n.knot == knot) {
2700         n->n.origin.a = 0;
2701         sp_knot_set_position(knot, &n->n.pos, state);
2702     } else {
2703         g_assert_not_reached();
2704     }
2706     update_repr(n->subpath->nodepath);
2709 /**
2710  * Node handle "request" signal callback.
2711  */
2712 static gboolean node_ctrl_request(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2714     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2716     Inkscape::NodePath::NodeSide *me, *opposite;
2717     gint which;
2718     if (n->p.knot == knot) {
2719         me = &n->p;
2720         opposite = &n->n;
2721         which = -1;
2722     } else if (n->n.knot == knot) {
2723         me = &n->n;
2724         opposite = &n->p;
2725         which = 1;
2726     } else {
2727         me = opposite = NULL;
2728         which = 0;
2729         g_assert_not_reached();
2730     }
2732     NRPathcode const othercode = sp_node_path_code_from_side(n, opposite);
2734     SnapManager const m(n->subpath->nodepath->desktop->namedview);
2736     if (opposite->other && (n->type != Inkscape::NodePath::NODE_CUSP) && (othercode == NR_LINETO)) {
2737         /* We are smooth node adjacent with line */
2738         NR::Point const delta = *p - n->pos;
2739         NR::Coord const len = NR::L2(delta);
2740         Inkscape::NodePath::Node *othernode = opposite->other;
2741         NR::Point const ndelta = n->pos - othernode->pos;
2742         NR::Coord const linelen = NR::L2(ndelta);
2743         if (len > NR_EPSILON && linelen > NR_EPSILON) {
2744             NR::Coord const scal = dot(delta, ndelta) / linelen;
2745             (*p) = n->pos + (scal / linelen) * ndelta;
2746         }
2747         *p = m.constrainedSnap(Inkscape::Snapper::SNAP_POINT, *p, ndelta, NULL).getPoint();
2748     } else {
2749         *p = m.freeSnap(Inkscape::Snapper::SNAP_POINT, *p, NULL).getPoint();
2750     }
2752     sp_node_adjust_knot(n, -which);
2754     return FALSE;
2757 /**
2758  * Node handle moved callback.
2759  */
2760 static void node_ctrl_moved(SPKnot *knot, NR::Point *p, guint state, gpointer data)
2762    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
2764    Inkscape::NodePath::NodeSide *me;
2765    Inkscape::NodePath::NodeSide *other;
2766     if (n->p.knot == knot) {
2767         me = &n->p;
2768         other = &n->n;
2769     } else if (n->n.knot == knot) {
2770         me = &n->n;
2771         other = &n->p;
2772     } else {
2773         me = NULL;
2774         other = NULL;
2775         g_assert_not_reached();
2776     }
2778     // calculate radial coordinates of the grabbed control, other control, and the mouse point
2779     Radial rme(me->pos - n->pos);
2780     Radial rother(other->pos - n->pos);
2781     Radial rnew(*p - n->pos);
2783     if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
2784         int const snaps = prefs_get_int_attribute("options.rotationsnapsperpi", "value", 12);
2785         /* 0 interpreted as "no snapping". */
2787         // The closest PI/snaps angle, starting from zero.
2788         double const a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
2789         if (me->origin.a == HUGE_VAL) {
2790             // ortho doesn't exist: original control was zero length.
2791             rnew.a = a_snapped;
2792         } else {
2793             /* The closest PI/2 angle, starting from original angle (i.e. snapping to original,
2794              * its opposite and perpendiculars). */
2795             double const a_ortho = me->origin.a + floor((rnew.a - me->origin.a)/(M_PI/2) + 0.5) * (M_PI/2);
2797             // Snap to the closest.
2798             rnew.a = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
2799                        ? a_snapped
2800                        : a_ortho );
2801         }
2802     }
2804     if (state & GDK_MOD1_MASK) {
2805         // lock handle length
2806         rnew.r = me->origin.r;
2807     }
2809     if (( n->type !=Inkscape::NodePath::NODE_CUSP || (state & GDK_SHIFT_MASK))
2810         && rme.a != HUGE_VAL && rnew.a != HUGE_VAL && fabs(rme.a - rnew.a) > 0.001) {
2811         // rotate the other handle correspondingly, if both old and new angles exist and are not the same
2812         rother.a += rnew.a - rme.a;
2813         other->pos = NR::Point(rother) + n->pos;
2814         sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
2815         sp_knot_set_position(other->knot, &other->pos, 0);
2816     }
2818     me->pos = NR::Point(rnew) + n->pos;
2819     sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
2821     // this is what sp_knot_set_position does, but without emitting the signal:
2822     // we cannot emit a "moved" signal because we're now processing it
2823     if (me->knot->item) SP_CTRL(me->knot->item)->moveto(me->pos);
2825     knot->desktop->set_coordinate_status(me->pos);
2827     update_object(n->subpath->nodepath);
2829     /* status text */
2830     SPDesktop *desktop = n->subpath->nodepath->desktop;
2831     if (!desktop) return;
2832     SPEventContext *ec = desktop->event_context;
2833     if (!ec) return;
2834     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
2835     if (!mc) return;
2837     double degrees = 180 / M_PI * rnew.a;
2838     if (degrees > 180) degrees -= 360;
2839     if (degrees < -180) degrees += 360;
2840     if (prefs_get_int_attribute("options.compassangledisplay", "value", 0) != 0)
2841         degrees = angle_to_compass (degrees);
2843     GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
2845     mc->setF(Inkscape::NORMAL_MESSAGE,
2846          _("<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);
2848     g_string_free(length, TRUE);
2851 /**
2852  * Node handle event callback.
2853  */
2854 static gboolean node_ctrl_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
2856     gboolean ret = FALSE;
2857     switch (event->type) {
2858         case GDK_KEY_PRESS:
2859             switch (get_group0_keyval (&event->key)) {
2860                 case GDK_space:
2861                     if (event->key.state & GDK_BUTTON1_MASK) {
2862                         Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
2863                         stamp_repr(nodepath);
2864                         ret = TRUE;
2865                     }
2866                     break;
2867                 default:
2868                     break;
2869             }
2870             break;
2871         default:
2872             break;
2873     }
2875     return ret;
2878 static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
2879                                  Radial &rme, Radial &rother, gboolean const both)
2881     rme.a += angle;
2882     if ( both
2883          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2884          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2885     {
2886         rother.a += angle;
2887     }
2890 static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
2891                                         Radial &rme, Radial &rother, gboolean const both)
2893     gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
2895     gdouble r;
2896     if ( both
2897          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2898          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2899     {
2900         r = MAX(rme.r, rother.r);
2901     } else {
2902         r = rme.r;
2903     }
2905     gdouble const weird_angle = atan2(norm_angle, r);
2906 /* Bulia says norm_angle is just the visible distance that the
2907  * object's end must travel on the screen.  Left as 'angle' for want of
2908  * a better name.*/
2910     rme.a += weird_angle;
2911     if ( both
2912          || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
2913          || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
2914     {
2915         rother.a += weird_angle;
2916     }
2919 /**
2920  * Rotate one node.
2921  */
2922 static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
2924     Inkscape::NodePath::NodeSide *me, *other;
2925     bool both = false;
2927     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
2928     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
2930     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
2931         me = &(n->p);
2932         other = &(n->n);
2933     } else if (!n->p.other) {
2934         me = &(n->n);
2935         other = &(n->p);
2936     } else {
2937         if (which > 0) { // right handle
2938             if (xn > xp) {
2939                 me = &(n->n);
2940                 other = &(n->p);
2941             } else {
2942                 me = &(n->p);
2943                 other = &(n->n);
2944             }
2945         } else if (which < 0){ // left handle
2946             if (xn <= xp) {
2947                 me = &(n->n);
2948                 other = &(n->p);
2949             } else {
2950                 me = &(n->p);
2951                 other = &(n->n);
2952             }
2953         } else { // both handles
2954             me = &(n->n);
2955             other = &(n->p);
2956             both = true;
2957         }
2958     }
2960     Radial rme(me->pos - n->pos);
2961     Radial rother(other->pos - n->pos);
2963     if (screen) {
2964         node_rotate_one_internal_screen (*n, angle, rme, rother, both);
2965     } else {
2966         node_rotate_one_internal (*n, angle, rme, rother, both);
2967     }
2969     me->pos = n->pos + NR::Point(rme);
2971     if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
2972         other->pos =  n->pos + NR::Point(rother);
2973     }
2975     sp_node_ensure_ctrls(n);
2978 /**
2979  * Rotate selected nodes.
2980  */
2981 void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
2983     if (!nodepath || !nodepath->selected) return;
2985     if (g_list_length(nodepath->selected) == 1) {
2986        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
2987         node_rotate_one (n, angle, which, screen);
2988     } else {
2989        // rotate as an object:
2991         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
2992         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
2993         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
2994             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
2995             box.expandTo (n->pos); // contain all selected nodes
2996         }
2998         gdouble rot;
2999         if (screen) {
3000             gdouble const zoom = nodepath->desktop->current_zoom();
3001             gdouble const zmove = angle / zoom;
3002             gdouble const r = NR::L2(box.max() - box.midpoint());
3003             rot = atan2(zmove, r);
3004         } else {
3005             rot = angle;
3006         }
3008         NR::Matrix t =
3009             NR::Matrix (NR::translate(-box.midpoint())) *
3010             NR::Matrix (NR::rotate(rot)) *
3011             NR::Matrix (NR::translate(box.midpoint()));
3013         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3014             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3015             n->pos *= t;
3016             n->n.pos *= t;
3017             n->p.pos *= t;
3018             sp_node_ensure_ctrls(n);
3019         }
3020     }
3022     update_object(nodepath);
3023     /// \todo fixme: use _keyed
3024     update_repr(nodepath);
3027 /**
3028  * Scale one node.
3029  */
3030 static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
3032     bool both = false;
3033     Inkscape::NodePath::NodeSide *me, *other;
3035     double xn = n->n.other? n->n.other->pos[NR::X] : n->pos[NR::X];
3036     double xp = n->p.other? n->p.other->pos[NR::X] : n->pos[NR::X];
3038     if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
3039         me = &(n->p);
3040         other = &(n->n);
3041         n->code = NR_CURVETO;
3042     } else if (!n->p.other) {
3043         me = &(n->n);
3044         other = &(n->p);
3045         if (n->n.other)
3046             n->n.other->code = NR_CURVETO;
3047     } else {
3048         if (which > 0) { // right handle
3049             if (xn > xp) {
3050                 me = &(n->n);
3051                 other = &(n->p);
3052                 if (n->n.other)
3053                     n->n.other->code = NR_CURVETO;
3054             } else {
3055                 me = &(n->p);
3056                 other = &(n->n);
3057                 n->code = NR_CURVETO;
3058             }
3059         } else if (which < 0){ // left handle
3060             if (xn <= xp) {
3061                 me = &(n->n);
3062                 other = &(n->p);
3063                 if (n->n.other)
3064                     n->n.other->code = NR_CURVETO;
3065             } else {
3066                 me = &(n->p);
3067                 other = &(n->n);
3068                 n->code = NR_CURVETO;
3069             }
3070         } else { // both handles
3071             me = &(n->n);
3072             other = &(n->p);
3073             both = true;
3074             n->code = NR_CURVETO;
3075             if (n->n.other)
3076                 n->n.other->code = NR_CURVETO;
3077         }
3078     }
3080     Radial rme(me->pos - n->pos);
3081     Radial rother(other->pos - n->pos);
3083     rme.r += grow;
3084     if (rme.r < 0) rme.r = 0;
3085     if (rme.a == HUGE_VAL) {
3086         if (me->other) { // if direction is unknown, initialize it towards the next node
3087             Radial rme_next(me->other->pos - n->pos);
3088             rme.a = rme_next.a;
3089         } else { // if there's no next, initialize to 0
3090             rme.a = 0;
3091         }
3092     }
3093     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3094         rother.r += grow;
3095         if (rother.r < 0) rother.r = 0;
3096         if (rother.a == HUGE_VAL) {
3097             rother.a = rme.a + M_PI;
3098         }
3099     }
3101     me->pos = n->pos + NR::Point(rme);
3103     if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
3104         other->pos = n->pos + NR::Point(rother);
3105     }
3107     sp_node_ensure_ctrls(n);
3110 /**
3111  * Scale selected nodes.
3112  */
3113 void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3115     if (!nodepath || !nodepath->selected) return;
3117     if (g_list_length(nodepath->selected) == 1) {
3118         // scale handles of the single selected node
3119         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3120         node_scale_one (n, grow, which);
3121     } else {
3122         // scale nodes as an "object":
3124         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3125         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3126         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3127             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3128             box.expandTo (n->pos); // contain all selected nodes
3129         }
3131         double scale = (box.maxExtent() + grow)/box.maxExtent();
3133         NR::Matrix t =
3134             NR::Matrix (NR::translate(-box.midpoint())) *
3135             NR::Matrix (NR::scale(scale, scale)) *
3136             NR::Matrix (NR::translate(box.midpoint()));
3138         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3139             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3140             n->pos *= t;
3141             n->n.pos *= t;
3142             n->p.pos *= t;
3143             sp_node_ensure_ctrls(n);
3144         }
3145     }
3147     update_object(nodepath);
3148     /// \todo fixme: use _keyed
3149     update_repr(nodepath);
3152 void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
3154     if (!nodepath) return;
3155     sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
3158 /**
3159  * Flip selected nodes horizontally/vertically.
3160  */
3161 void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, NR::Dim2 axis)
3163     if (!nodepath || !nodepath->selected) return;
3165     if (g_list_length(nodepath->selected) == 1) {
3166         // flip handles of the single selected node
3167         Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
3168         double temp = n->p.pos[axis];
3169         n->p.pos[axis] = n->n.pos[axis];
3170         n->n.pos[axis] = temp;
3171         sp_node_ensure_ctrls(n);
3172     } else {
3173         // scale nodes as an "object":
3175         Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
3176         NR::Rect box (n0->pos, n0->pos); // originally includes the first selected node
3177         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3178             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3179             box.expandTo (n->pos); // contain all selected nodes
3180         }
3182         NR::Matrix t =
3183             NR::Matrix (NR::translate(-box.midpoint())) *
3184             NR::Matrix ((axis == NR::X)? NR::scale(-1, 1) : NR::scale(1, -1)) *
3185             NR::Matrix (NR::translate(box.midpoint()));
3187         for (GList *l = nodepath->selected; l != NULL; l = l->next) {
3188             Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
3189             n->pos *= t;
3190             n->n.pos *= t;
3191             n->p.pos *= t;
3192             sp_node_ensure_ctrls(n);
3193         }
3194     }
3196     update_object(nodepath);
3197     /// \todo fixme: use _keyed
3198     update_repr(nodepath);
3201 //-----------------------------------------------
3202 /**
3203  * Return new subpath under given nodepath.
3204  */
3205 static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
3207     g_assert(nodepath);
3208     g_assert(nodepath->desktop);
3210    Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
3212     s->nodepath = nodepath;
3213     s->closed = FALSE;
3214     s->nodes = NULL;
3215     s->first = NULL;
3216     s->last = NULL;
3218     // do not use prepend here because:
3219     // if you have a path like "subpath_1 subpath_2 ... subpath_k" in the svg, you end up with
3220     // subpath_k -> ... ->subpath_1 in the nodepath structure. thus the i-th node of the svg is not
3221     // the i-th node in the nodepath (only if there are multiple subpaths)
3222     // note that the problem only arise when called from subpath_from_bpath(), since for all the other
3223     // cases, the repr is updated after the call to sp_nodepath_subpath_new()
3224     nodepath->subpaths = g_list_append /*g_list_prepend*/ (nodepath->subpaths, s);
3226     return s;
3229 /**
3230  * Destroy nodes in subpath, then subpath itself.
3231  */
3232 static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
3234     g_assert(subpath);
3235     g_assert(subpath->nodepath);
3236     g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
3238     while (subpath->nodes) {
3239         sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
3240     }
3242     subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
3244     g_free(subpath);
3247 /**
3248  * Link head to tail in subpath.
3249  */
3250 static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
3252     g_assert(!sp->closed);
3253     g_assert(sp->last != sp->first);
3254     g_assert(sp->first->code == NR_MOVETO);
3256     sp->closed = TRUE;
3258     //Link the head to the tail
3259     sp->first->p.other = sp->last;
3260     sp->last->n.other  = sp->first;
3261     sp->last->n.pos    = sp->first->n.pos;
3262     sp->first          = sp->last;
3264     //Remove the extra end node
3265     sp_nodepath_node_destroy(sp->last->n.other);
3268 /**
3269  * Open closed (loopy) subpath at node.
3270  */
3271 static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
3273     g_assert(sp->closed);
3274     g_assert(n->subpath == sp);
3275     g_assert(sp->first == sp->last);
3277     /* We create new startpoint, current node will become last one */
3279    Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
3280                                                 &n->pos, &n->pos, &n->n.pos);
3283     sp->closed        = FALSE;
3285     //Unlink to make a head and tail
3286     sp->first         = new_path;
3287     sp->last          = n;
3288     n->n.other        = NULL;
3289     new_path->p.other = NULL;
3292 /**
3293  * Returns area in triangle given by points; may be negative.
3294  */
3295 inline double
3296 triangle_area (NR::Point p1, NR::Point p2, NR::Point p3)
3298     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]);
3301 /**
3302  * Return new node in subpath with given properties.
3303  * \param pos Position of node.
3304  * \param ppos Handle position in previous direction
3305  * \param npos Handle position in previous direction
3306  */
3307 Inkscape::NodePath::Node *
3308 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)
3310     g_assert(sp);
3311     g_assert(sp->nodepath);
3312     g_assert(sp->nodepath->desktop);
3314     if (nodechunk == NULL)
3315         nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
3317     Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
3319     n->subpath  = sp;
3321     if (type != Inkscape::NodePath::NODE_NONE) {
3322         // use the type from sodipodi:nodetypes
3323         n->type = type;
3324     } else {
3325         if (fabs (triangle_area (*pos, *ppos, *npos)) < 1e-2) {
3326             // points are (almost) collinear
3327             if (NR::L2(*pos - *ppos) < 1e-6 || NR::L2(*pos - *npos) < 1e-6) {
3328                 // endnode, or a node with a retracted handle
3329                 n->type = Inkscape::NodePath::NODE_CUSP;
3330             } else {
3331                 n->type = Inkscape::NodePath::NODE_SMOOTH;
3332             }
3333         } else {
3334             n->type = Inkscape::NodePath::NODE_CUSP;
3335         }
3336     }
3338     n->code     = code;
3339     n->selected = FALSE;
3340     n->pos      = *pos;
3341     n->p.pos    = *ppos;
3342     n->n.pos    = *npos;
3344     n->dragging_out = NULL;
3346     Inkscape::NodePath::Node *prev;
3347     if (next) {
3348         //g_assert(g_list_find(sp->nodes, next));
3349         prev = next->p.other;
3350     } else {
3351         prev = sp->last;
3352     }
3354     if (prev)
3355         prev->n.other = n;
3356     else
3357         sp->first = n;
3359     if (next)
3360         next->p.other = n;
3361     else
3362         sp->last = n;
3364     n->p.other = prev;
3365     n->n.other = next;
3367     n->knot = sp_knot_new(sp->nodepath->desktop, _("<b>Node</b>: drag to edit the path; with <b>Ctrl</b> to snap to horizontal/vertical; with <b>Ctrl+Alt</b> to snap to handles' directions"));
3368     sp_knot_set_position(n->knot, pos, 0);
3370     n->knot->setShape ((n->type == Inkscape::NodePath::NODE_CUSP)? SP_KNOT_SHAPE_DIAMOND : SP_KNOT_SHAPE_SQUARE);
3371     n->knot->setSize ((n->type == Inkscape::NodePath::NODE_CUSP)? 9 : 7);
3372     n->knot->setAnchor (GTK_ANCHOR_CENTER);
3373     n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
3374     n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
3375     sp_knot_update_ctrl(n->knot);
3377     g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
3378     g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
3379     g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
3380     g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
3381     g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
3382     sp_knot_show(n->knot);
3384     // We only create side knots and lines on demand
3385     n->p.knot = NULL;
3386     n->p.line = NULL;
3387     n->n.knot = NULL;
3388     n->n.line = NULL;
3390     sp->nodes = g_list_prepend(sp->nodes, n);
3392     return n;
3395 /**
3396  * Destroy node and its knots, link neighbors in subpath.
3397  */
3398 static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
3400     g_assert(node);
3401     g_assert(node->subpath);
3402     g_assert(SP_IS_KNOT(node->knot));
3403 //    g_assert(g_list_find(node->subpath->nodes, node));
3405    Inkscape::NodePath::SubPath *sp = node->subpath;
3407     if (node->selected) { // first, deselect
3408         g_assert(g_list_find(node->subpath->nodepath->selected, node));
3409         node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
3410     }
3412     node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
3414     g_object_unref(G_OBJECT(node->knot));
3415     if (node->p.knot)
3416         g_object_unref(G_OBJECT(node->p.knot));
3417     if (node->n.knot)
3418         g_object_unref(G_OBJECT(node->n.knot));
3420     if (node->p.line)
3421         gtk_object_destroy(GTK_OBJECT(node->p.line));
3422     if (node->n.line)
3423         gtk_object_destroy(GTK_OBJECT(node->n.line));
3425     if (sp->nodes) { // there are others nodes on the subpath
3426         if (sp->closed) {
3427             if (sp->first == node) {
3428                 g_assert(sp->last == node);
3429                 sp->first = node->n.other;
3430                 sp->last = sp->first;
3431             }
3432             node->p.other->n.other = node->n.other;
3433             node->n.other->p.other = node->p.other;
3434         } else {
3435             if (sp->first == node) {
3436                 sp->first = node->n.other;
3437                 sp->first->code = NR_MOVETO;
3438             }
3439             if (sp->last == node) sp->last = node->p.other;
3440             if (node->p.other) node->p.other->n.other = node->n.other;
3441             if (node->n.other) node->n.other->p.other = node->p.other;
3442         }
3443     } else { // this was the last node on subpath
3444         sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
3445     }
3447     g_mem_chunk_free(nodechunk, node);
3450 /**
3451  * Returns one of the node's two knots (node sides).
3452  * \param which Indicates which side.
3453  * \return Pointer to previous node side if which==-1, next if which==1.
3454  */
3455 static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
3457     g_assert(node);
3459     switch (which) {
3460         case -1:
3461             return &node->p;
3462         case 1:
3463             return &node->n;
3464         default:
3465             break;
3466     }
3468     g_assert_not_reached();
3470     return NULL;
3473 /**
3474  * Return knot on other side of node.
3475  */
3476 static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3478     g_assert(node);
3480     if (me == &node->p) return &node->n;
3481     if (me == &node->n) return &node->p;
3483     g_assert_not_reached();
3485     return NULL;
3488 /**
3489  * Return NRPathcode on this knot's side of the node.
3490  */
3491 static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
3493     g_assert(node);
3495     if (me == &node->p) {
3496         if (node->p.other) return (NRPathcode)node->code;
3497         return NR_MOVETO;
3498     }
3500     if (me == &node->n) {
3501         if (node->n.other) return (NRPathcode)node->n.other->code;
3502         return NR_MOVETO;
3503     }
3505     g_assert_not_reached();
3507     return NR_END;
3510 /**
3511  * Call sp_nodepath_line_add_node() at t on the segment denoted by piece
3512  */
3513 Inkscape::NodePath::Node *
3514 sp_nodepath_get_node_by_index(int index)
3516     Inkscape::NodePath::Node *e = NULL;
3518     Inkscape::NodePath::Path *nodepath = sp_nodepath_current();
3519     if (!nodepath) {
3520         return e;
3521     }
3523     //find segment
3524     for (GList *l = nodepath->subpaths; l ; l=l->next) {
3526         Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
3527         int n = g_list_length(sp->nodes);
3528         if (sp->closed) {
3529             n++;
3530         }
3532         //if the piece belongs to this subpath grab it
3533         //otherwise move onto the next subpath
3534         if (index < n) {
3535             e = sp->first;
3536             for (int i = 0; i < index; ++i) {
3537                 e = e->n.other;
3538             }
3539             break;
3540         } else {
3541             if (sp->closed) {
3542                 index -= (n+1);
3543             } else {
3544                 index -= n;
3545             }
3546         }
3547     }
3549     return e;
3552 /**
3553  * Returns plain text meaning of node type.
3554  */
3555 static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
3557     unsigned retracted = 0;
3558     bool endnode = false;
3560     for (int which = -1; which <= 1; which += 2) {
3561         Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
3562         if (side->other && NR::L2(side->pos - node->pos) < 1e-6)
3563             retracted ++;
3564         if (!side->other)
3565             endnode = true;
3566     }
3568     if (retracted == 0) {
3569         if (endnode) {
3570                 // TRANSLATORS: "end" is an adjective here (NOT a verb)
3571                 return _("end node");
3572         } else {
3573             switch (node->type) {
3574                 case Inkscape::NodePath::NODE_CUSP:
3575                     // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
3576                     return _("cusp");
3577                 case Inkscape::NodePath::NODE_SMOOTH:
3578                     // TRANSLATORS: "smooth" is an adjective here
3579                     return _("smooth");
3580                 case Inkscape::NodePath::NODE_SYMM:
3581                     return _("symmetric");
3582             }
3583         }
3584     } else if (retracted == 1) {
3585         if (endnode) {
3586             // TRANSLATORS: "end" is an adjective here (NOT a verb)
3587             return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
3588         } else {
3589             return _("one handle retracted (drag with <b>Shift</b> to extend)");
3590         }
3591     } else {
3592         return _("both handles retracted (drag with <b>Shift</b> to extend)");
3593     }
3595     return NULL;
3598 /**
3599  * Handles content of statusbar as long as node tool is active.
3600  */
3601 void
3602 sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)
3604     gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>arrow</b> keys to move nodes");
3605     gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
3607     gint total = 0;
3608     gint selected = 0;
3609     SPDesktop *desktop = NULL;
3611     if (nodepath) {
3612         for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
3613             Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
3614             total += g_list_length(subpath->nodes);
3615         }
3616         selected = g_list_length(nodepath->selected);
3617         desktop = nodepath->desktop;
3618     } else {
3619         desktop = SP_ACTIVE_DESKTOP;
3620     }
3622     SPEventContext *ec = desktop->event_context;
3623     if (!ec) return;
3624     Inkscape::MessageContext *mc = SP_NODE_CONTEXT (ec)->_node_message_context;
3625     if (!mc) return;
3627     if (selected == 0) {
3628         Inkscape::Selection *sel = desktop->selection;
3629         if (!sel || sel->isEmpty()) {
3630             mc->setF(Inkscape::NORMAL_MESSAGE,
3631                      _("Select a single object to edit its nodes or handles."));
3632         } else {
3633             if (nodepath) {
3634             mc->setF(Inkscape::NORMAL_MESSAGE,
3635                      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.",
3636                               "<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.",
3637                               total),
3638                      total);
3639             } else {
3640                 if (g_slist_length((GSList *)sel->itemList()) == 1) {
3641                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
3642                 } else {
3643                     mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
3644                 }
3645             }
3646         }
3647     } else if (nodepath && selected == 1) {
3648         mc->setF(Inkscape::NORMAL_MESSAGE,
3649                  ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
3650                           "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
3651                           total),
3652                  selected, total, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
3653     } else {
3654         mc->setF(Inkscape::NORMAL_MESSAGE,
3655                  ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
3656                           "<b>%i</b> of <b>%i</b> nodes selected. %s.",
3657                           total),
3658                  selected, total, when_selected);
3659     }
3663 /*
3664   Local Variables:
3665   mode:c++
3666   c-file-style:"stroustrup"
3667   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3668   indent-tabs-mode:nil
3669   fill-column:99
3670   End:
3671 */
3672 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :