~valavanisalex/ubuntu/precise/inkscape/fix-943984

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/nodepath.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __SP_NODEPATH_C__
 
2
 
 
3
/** \file
 
4
 * Path handler in node edit mode
 
5
 *
 
6
 * Authors:
 
7
 *   Lauris Kaplinski <lauris@kaplinski.com>
 
8
 *   bulia byak <buliabyak@users.sf.net>
 
9
 *
 
10
 * Portions of this code are in public domain; node sculpting functions written by bulia byak are under GNU GPL
 
11
 */
 
12
 
 
13
#ifdef HAVE_CONFIG_H
 
14
# include "config.h"
 
15
#endif
 
16
 
 
17
#include <gdk/gdkkeysyms.h>
 
18
#include "display/canvas-bpath.h"
 
19
#include "display/curve.h"
 
20
#include "display/sp-ctrlline.h"
 
21
#include "display/sodipodi-ctrl.h"
 
22
#include "display/sp-canvas-util.h"
 
23
#include <glibmm/i18n.h>
 
24
#include "2geom/pathvector.h"
 
25
#include "2geom/sbasis-to-bezier.h"
 
26
#include "2geom/bezier-curve.h"
 
27
#include "2geom/hvlinesegment.h"
 
28
#include "helper/units.h"
 
29
#include "helper/geom.h"
 
30
#include "knot.h"
 
31
#include "inkscape.h"
 
32
#include "document.h"
 
33
#include "sp-namedview.h"
 
34
#include "desktop.h"
 
35
#include "desktop-handles.h"
 
36
#include "snap.h"
 
37
#include "message-stack.h"
 
38
#include "message-context.h"
 
39
#include "node-context.h"
 
40
#include "lpe-tool-context.h"
 
41
#include "shape-editor.h"
 
42
#include "selection-chemistry.h"
 
43
#include "selection.h"
 
44
#include "xml/repr.h"
 
45
#include "preferences.h"
 
46
#include "sp-metrics.h"
 
47
#include "sp-path.h"
 
48
#include "sp-text.h"
 
49
#include "sp-shape.h"
 
50
#include "libnr/nr-matrix-ops.h"
 
51
#include "svg/svg.h"
 
52
#include "verbs.h"
 
53
#include <2geom/bezier-utils.h>
 
54
#include <vector>
 
55
#include <algorithm>
 
56
#include <cstring>
 
57
#include <cmath>
 
58
#include "live_effects/lpeobject.h"
 
59
#include "live_effects/lpeobject-reference.h"
 
60
#include "live_effects/effect.h"
 
61
#include "live_effects/parameter/parameter.h"
 
62
#include "live_effects/parameter/path.h"
 
63
#include "util/mathfns.h"
 
64
#include "display/snap-indicator.h"
 
65
#include "snapped-point.h"
 
66
 
 
67
namespace Geom { class Matrix; }
 
68
 
 
69
/// \todo
 
70
/// evil evil evil. FIXME: conflict of two different Path classes!
 
71
/// There is a conflict in the namespace between two classes named Path.
 
72
/// #include "sp-flowtext.h"
 
73
/// #include "sp-flowregion.h"
 
74
 
 
75
#define SP_TYPE_FLOWREGION            (sp_flowregion_get_type ())
 
76
#define SP_IS_FLOWREGION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWREGION))
 
77
GType sp_flowregion_get_type (void);
 
78
#define SP_TYPE_FLOWTEXT            (sp_flowtext_get_type ())
 
79
#define SP_IS_FLOWTEXT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_FLOWTEXT))
 
80
GType sp_flowtext_get_type (void);
 
81
// end evil workaround
 
82
 
 
83
#include "helper/stlport.h"
 
84
 
 
85
 
 
86
/// \todo fixme: Implement these via preferences */
 
87
 
 
88
#define NODE_FILL          0xbfbfbf00
 
89
#define NODE_STROKE        0x000000ff
 
90
#define NODE_FILL_HI       0xff000000
 
91
#define NODE_STROKE_HI     0x000000ff
 
92
#define NODE_FILL_SEL      0x0000ffff
 
93
#define NODE_STROKE_SEL    0x000000ff
 
94
#define NODE_FILL_SEL_HI   0xff000000
 
95
#define NODE_STROKE_SEL_HI 0x000000ff
 
96
#define KNOT_FILL          0xffffffff
 
97
#define KNOT_STROKE        0x000000ff
 
98
#define KNOT_FILL_HI       0xff000000
 
99
#define KNOT_STROKE_HI     0x000000ff
 
100
 
 
101
static GMemChunk *nodechunk = NULL;
 
102
 
 
103
/* Creation from object */
 
104
 
 
105
static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t);
 
106
static Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length);
 
107
 
 
108
/* Object updating */
 
109
 
 
110
static void stamp_repr(Inkscape::NodePath::Path *np);
 
111
static SPCurve *create_curve(Inkscape::NodePath::Path *np);
 
112
static gchar *create_typestr(Inkscape::NodePath::Path *np);
 
113
 
 
114
static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals = true);
 
115
 
 
116
static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override);
 
117
 
 
118
static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected);
 
119
 
 
120
static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type);
 
121
 
 
122
/* Adjust handle placement, if the node or the other handle is moved */
 
123
static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust);
 
124
static void sp_node_adjust_handles(Inkscape::NodePath::Node *node);
 
125
static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node);
 
126
 
 
127
/* Node event callbacks */
 
128
static void node_clicked(SPKnot *knot, guint state, gpointer data);
 
129
static void node_grabbed(SPKnot *knot, guint state, gpointer data);
 
130
static void node_ungrabbed(SPKnot *knot, guint state, gpointer data);
 
131
static gboolean node_request(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
 
132
 
 
133
/* Handle event callbacks */
 
134
static void node_handle_clicked(SPKnot *knot, guint state, gpointer data);
 
135
static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data);
 
136
static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data);
 
137
static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data);
 
138
static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data);
 
139
static gboolean node_handle_event(SPKnot *knot, GdkEvent *event, Inkscape::NodePath::Node *n);
 
140
 
 
141
/* Constructors and destructors */
 
142
 
 
143
static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath);
 
144
static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath);
 
145
static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp);
 
146
static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n);
 
147
static Inkscape::NodePath::Node * sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *next,Inkscape::NodePath::NodeType type, NRPathcode code,
 
148
                                         Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos);
 
149
static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node);
 
150
 
 
151
/* Helpers */
 
152
 
 
153
static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which);
 
154
static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
 
155
static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me);
 
156
 
 
157
static SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key);
 
158
static void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve);
 
159
 
 
160
// active_node indicates mouseover node
 
161
Inkscape::NodePath::Node * Inkscape::NodePath::Path::active_node = NULL;
 
162
 
 
163
static SPCanvasItem *
 
164
sp_nodepath_make_helper_item(Inkscape::NodePath::Path *np, /*SPDesktop *desktop, */const SPCurve *curve, bool show = false, guint32 color = 0xff0000ff) {
 
165
    SPCurve *helper_curve = curve->copy();
 
166
    helper_curve->transform(np->i2d);
 
167
    SPCanvasItem *helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
 
168
    sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(helper_path), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
 
169
    sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(helper_path), 0, SP_WIND_RULE_NONZERO);
 
170
    sp_canvas_item_move_to_z(helper_path, 0);
 
171
    if (show) {
 
172
        sp_canvas_item_show(helper_path);
 
173
    }
 
174
    helper_curve->unref();
 
175
    return helper_path;
 
176
}
 
177
 
 
178
static void
 
179
sp_nodepath_create_helperpaths(Inkscape::NodePath::Path *np) {
 
180
    //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
 
181
    if (!SP_IS_LPE_ITEM(np->item)) {
 
182
        g_print ("Only LPEItems can have helperpaths!\n");
 
183
        return;
 
184
    }
 
185
 
 
186
    SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
 
187
    PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
 
188
    for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
 
189
        Inkscape::LivePathEffect::LPEObjectReference *lperef = (*i);
 
190
        Inkscape::LivePathEffect::Effect *lpe = lperef->lpeobject->get_lpe();
 
191
        if (lpe) {
 
192
            // create new canvas items from the effect's helper paths
 
193
            std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
 
194
            for (std::vector<Geom::PathVector>::iterator j = hpaths.begin(); j != hpaths.end(); ++j) {
 
195
                SPCurve *helper_curve = new SPCurve(*j);
 
196
                SPCanvasItem * canvasitem = sp_nodepath_make_helper_item(np, helper_curve, true, 0x509050dd);
 
197
                np->helper_path_vec[lpe].push_back(canvasitem);
 
198
                helper_curve->unref();
 
199
            }
 
200
        }
 
201
    }
 
202
}
 
203
 
 
204
void
 
205
sp_nodepath_update_helperpaths(Inkscape::NodePath::Path *np) {
 
206
    //std::map<Inkscape::LivePathEffect::Effect *, std::vector<SPCanvasItem *> > helper_path_vec;
 
207
    if (!SP_IS_LPE_ITEM(np->item)) {
 
208
        g_print ("Only LPEItems can have helperpaths!\n");
 
209
        return;
 
210
    }
 
211
 
 
212
    SPLPEItem *lpeitem = SP_LPE_ITEM(np->item);
 
213
    PathEffectList lpelist = sp_lpe_item_get_effect_list(lpeitem);
 
214
    for (PathEffectList::iterator i = lpelist.begin(); i != lpelist.end(); ++i) {
 
215
        Inkscape::LivePathEffect::Effect *lpe = (*i)->lpeobject->get_lpe();
 
216
        if (lpe) {
 
217
            /* update canvas items from the effect's helper paths; note that this code relies on the
 
218
             * fact that getHelperPaths() will always return the same number of helperpaths in the same
 
219
             * order as during their creation in sp_nodepath_create_helperpaths
 
220
             */
 
221
            std::vector<Geom::PathVector> hpaths = lpe->getHelperPaths(lpeitem);
 
222
            for (unsigned int j = 0; j < hpaths.size(); ++j) {
 
223
                SPCurve *curve = new SPCurve(hpaths[j]);
 
224
                curve->transform(np->i2d);
 
225
                sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH((np->helper_path_vec[lpe])[j]), curve);
 
226
                curve = curve->unref();
 
227
            }
 
228
        }
 
229
    }
 
230
}
 
231
 
 
232
static void
 
233
sp_nodepath_destroy_helperpaths(Inkscape::NodePath::Path *np) {
 
234
    for (HelperPathList::iterator i = np->helper_path_vec.begin(); i != np->helper_path_vec.end(); ++i) {
 
235
        for (std::vector<SPCanvasItem *>::iterator j = (*i).second.begin(); j != (*i).second.end(); ++j) {
 
236
            GtkObject *temp = *j;
 
237
            *j = NULL;
 
238
            gtk_object_destroy(temp);
 
239
        }
 
240
    }
 
241
    np->helper_path_vec.clear();
 
242
}
 
243
 
 
244
 
 
245
/**
 
246
 * \brief Creates new nodepath from item
 
247
 *
 
248
 * If repr_key_in is not NULL, object *has* to be a LivePathEffectObject !
 
249
 *
 
250
 * \todo create proper constructor for nodepath::path, this method returns null a constructor cannot so this cannot be simply converted to constructor.
 
251
 */
 
252
Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, bool show_handles, const char * repr_key_in, SPItem *item)
 
253
{
 
254
    if (repr_key_in) {
 
255
        g_assert(IS_LIVEPATHEFFECT(object));
 
256
    }
 
257
 
 
258
    Inkscape::XML::Node *repr = object->repr;
 
259
 
 
260
    /** \todo
 
261
     * FIXME: remove this. We don't want to edit paths inside flowtext.
 
262
     * Instead we will build our flowtext with cloned paths, so that the
 
263
     * real paths are outside the flowtext and thus editable as usual.
 
264
     */
 
265
    if (SP_IS_FLOWTEXT(object)) {
 
266
        for (SPObject *child = sp_object_first_child(object) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
 
267
            if SP_IS_FLOWREGION(child) {
 
268
                SPObject *grandchild = sp_object_first_child(SP_OBJECT(child));
 
269
                if (grandchild && SP_IS_PATH(grandchild)) {
 
270
                    object = SP_ITEM(grandchild);
 
271
                    break;
 
272
                }
 
273
            }
 
274
        }
 
275
    }
 
276
 
 
277
    SPCurve *curve = sp_nodepath_object_get_curve(object, repr_key_in);
 
278
 
 
279
    if (curve == NULL) {
 
280
        return NULL;
 
281
    }
 
282
 
 
283
    if (curve->get_segment_count() < 1) {
 
284
        curve->unref();
 
285
        return NULL; // prevent crash for one-node paths
 
286
    }
 
287
 
 
288
    //Create new nodepath
 
289
    Inkscape::NodePath::Path *np = new Inkscape::NodePath::Path();
 
290
    if (!np) {
 
291
        curve->unref();
 
292
        return NULL;
 
293
    }
 
294
 
 
295
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
296
 
 
297
    // Set defaults
 
298
    np->desktop     = desktop;
 
299
    np->object      = object;
 
300
    np->subpaths    = NULL;
 
301
    np->selected    = NULL;
 
302
    np->shape_editor = NULL; //Let the shapeeditor that makes this set it
 
303
    np->local_change = 0;
 
304
    np->show_handles = show_handles;
 
305
    np->helper_path = NULL;
 
306
    np->helperpath_rgba = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
 
307
    np->helperpath_width = 1.0;
 
308
    np->curve = curve->copy();
 
309
    np->show_helperpath = prefs->getBool("/tools/nodes/show_helperpath");
 
310
    if (SP_IS_LPE_ITEM(object)) {
 
311
        Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(object));
 
312
        if (lpe && lpe->isVisible() && lpe->showOrigPath()) {
 
313
            np->show_helperpath = true;
 
314
        }
 
315
    }
 
316
    np->straight_path = false;
 
317
    if (IS_LIVEPATHEFFECT(object) && item) {
 
318
        np->item = item;
 
319
    } else {
 
320
        np->item = SP_ITEM(object);
 
321
    }
 
322
 
 
323
    np->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
 
324
 
 
325
    // we need to update item's transform from the repr here,
 
326
    // because they may be out of sync when we respond
 
327
    // to a change in repr by regenerating nodepath     --bb
 
328
    sp_object_read_attr(SP_OBJECT(np->item), "transform");
 
329
 
 
330
    np->i2d  = sp_item_i2d_affine(np->item);
 
331
    np->d2i  = np->i2d.inverse();
 
332
 
 
333
    np->repr = repr;
 
334
    if (repr_key_in) { // apparently the object is an LPEObject
 
335
        np->repr_key = g_strdup(repr_key_in);
 
336
        np->repr_nodetypes_key = g_strconcat(np->repr_key, "-nodetypes", NULL);
 
337
        Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(object)->get_lpe();
 
338
        if (!lpe) {
 
339
            g_error("sp_nodepath_new: lpeobject without real lpe passed as argument!");
 
340
            delete np;
 
341
        }
 
342
        Inkscape::LivePathEffect::Parameter *lpeparam = lpe->getParameter(repr_key_in);
 
343
        if (lpeparam) {
 
344
            lpeparam->param_setup_nodepath(np);
 
345
        }
 
346
    } else {
 
347
        np->repr_nodetypes_key = g_strdup("sodipodi:nodetypes");
 
348
        if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) {
 
349
            np->repr_key = g_strdup("inkscape:original-d");
 
350
 
 
351
            Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object));
 
352
            if (lpe) {
 
353
                lpe->setup_nodepath(np);
 
354
            }
 
355
        } else {
 
356
            np->repr_key = g_strdup("d");
 
357
        }
 
358
    }
 
359
 
 
360
    /* Calculate length of the nodetype string. The closing/starting point for closed paths is counted twice.
 
361
     * So for example a closed rectangle has a nodetypestring of length 5.
 
362
     * To get the correct count, one can count all segments in the paths, and then add the total number of (non-empty) paths. */
 
363
    Geom::PathVector pathv_sanitized = pathv_to_linear_and_cubic_beziers(np->curve->get_pathvector());
 
364
    np->curve->set_pathvector(pathv_sanitized);
 
365
    guint length = np->curve->get_segment_count();
 
366
    for (Geom::PathVector::const_iterator pit = pathv_sanitized.begin(); pit != pathv_sanitized.end(); ++pit) {
 
367
        length += pit->empty() ? 0 : 1;
 
368
    }
 
369
 
 
370
    gchar const *nodetypes = np->repr->attribute(np->repr_nodetypes_key);
 
371
    Inkscape::NodePath::NodeType *typestr = parse_nodetypes(nodetypes, length);
 
372
 
 
373
    // create the subpath(s) from the bpath
 
374
    subpaths_from_pathvector(np, pathv_sanitized, typestr);
 
375
 
 
376
    // reverse the list, because sp_nodepath_subpath_new() used g_list_prepend instead of append (for speed)
 
377
    np->subpaths = g_list_reverse(np->subpaths);
 
378
 
 
379
    delete[] typestr;
 
380
    curve->unref();
 
381
 
 
382
    // Draw helper curve
 
383
    if (np->show_helperpath) {
 
384
        np->helper_path = sp_nodepath_make_helper_item(np, /*desktop, */np->curve, true, np->helperpath_rgba);
 
385
    }
 
386
 
 
387
    sp_nodepath_create_helperpaths(np);
 
388
 
 
389
    return np;
 
390
}
 
391
 
 
392
/**
 
393
 * Destroys nodepath's subpaths, then itself, also tell parent ShapeEditor about it.
 
394
 */
 
395
Inkscape::NodePath::Path::~Path() {
 
396
    while (this->subpaths) {
 
397
        sp_nodepath_subpath_destroy((Inkscape::NodePath::SubPath *) this->subpaths->data);
 
398
    }
 
399
 
 
400
    //Inform the ShapeEditor that made me, if any, that I am gone.
 
401
    if (this->shape_editor)
 
402
        this->shape_editor->nodepath_destroyed();
 
403
 
 
404
    g_assert(!this->selected);
 
405
 
 
406
    if (this->helper_path) {
 
407
        GtkObject *temp = this->helper_path;
 
408
        this->helper_path = NULL;
 
409
        gtk_object_destroy(temp);
 
410
    }
 
411
    if (this->curve) {
 
412
        this->curve->unref();
 
413
        this->curve = NULL;
 
414
    }
 
415
 
 
416
    if (this->repr_key) {
 
417
        g_free(this->repr_key);
 
418
        this->repr_key = NULL;
 
419
    }
 
420
    if (this->repr_nodetypes_key) {
 
421
        g_free(this->repr_nodetypes_key);
 
422
        this->repr_nodetypes_key = NULL;
 
423
    }
 
424
 
 
425
    sp_nodepath_destroy_helperpaths(this);
 
426
 
 
427
    this->desktop = NULL;
 
428
}
 
429
 
 
430
/**
 
431
 *  Return the node count of a given NodeSubPath.
 
432
 */
 
433
static gint sp_nodepath_subpath_get_node_count(Inkscape::NodePath::SubPath *subpath)
 
434
{
 
435
    int nodeCount = 0;
 
436
 
 
437
    if (subpath) {
 
438
        nodeCount = g_list_length(subpath->nodes);
 
439
    }
 
440
 
 
441
    return nodeCount;
 
442
}
 
443
 
 
444
/**
 
445
 *  Return the node count of a given NodePath.
 
446
 */
 
447
static gint sp_nodepath_get_node_count(Inkscape::NodePath::Path *np)
 
448
{
 
449
    gint nodeCount = 0;
 
450
    if (np) {
 
451
        for (GList *item = np->subpaths ; item ; item=item->next) {
 
452
            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *)item->data;
 
453
            nodeCount += g_list_length(subpath->nodes);
 
454
        }
 
455
    }
 
456
    return nodeCount;
 
457
}
 
458
 
 
459
/**
 
460
 *  Return the subpath count of a given NodePath.
 
461
 */
 
462
static gint sp_nodepath_get_subpath_count(Inkscape::NodePath::Path *np)
 
463
{
 
464
    gint nodeCount = 0;
 
465
    if (np) {
 
466
        nodeCount = g_list_length(np->subpaths);
 
467
    }
 
468
    return nodeCount;
 
469
}
 
470
 
 
471
/**
 
472
 *  Return the selected node count of a given NodePath.
 
473
 */
 
474
static gint sp_nodepath_selection_get_node_count(Inkscape::NodePath::Path *np)
 
475
{
 
476
    gint nodeCount = 0;
 
477
    if (np) {
 
478
        nodeCount = g_list_length(np->selected);
 
479
    }
 
480
    return nodeCount;
 
481
}
 
482
 
 
483
/**
 
484
 *  Return the number of subpaths where nodes are selected in a given NodePath.
 
485
 */
 
486
static gint sp_nodepath_selection_get_subpath_count(Inkscape::NodePath::Path *np)
 
487
{
 
488
    gint nodeCount = 0;
 
489
    if (np && np->selected) {
 
490
        if (!np->selected->next) {
 
491
            nodeCount = 1;
 
492
        } else {
 
493
            for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
 
494
                Inkscape::NodePath::SubPath *subpath = static_cast<Inkscape::NodePath::SubPath *>(spl->data);
 
495
                for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
 
496
                    Inkscape::NodePath::Node *node = static_cast<Inkscape::NodePath::Node *>(nl->data);
 
497
                    if (node->selected) {
 
498
                        nodeCount++;
 
499
                        break;
 
500
                    }
 
501
                }
 
502
            }
 
503
        }
 
504
    }
 
505
    return nodeCount;
 
506
}
 
507
 
 
508
/**
 
509
 * Clean up a nodepath after editing.
 
510
 *
 
511
 * Currently we are deleting trivial subpaths.
 
512
 */
 
513
static void sp_nodepath_cleanup(Inkscape::NodePath::Path *nodepath)
 
514
{
 
515
    GList *badSubPaths = NULL;
 
516
 
 
517
    //Check all closed subpaths to be >=1 nodes, all open subpaths to be >= 2 nodes
 
518
    for (GList *l = nodepath->subpaths; l ; l=l->next) {
 
519
       Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
 
520
       if ((sp_nodepath_subpath_get_node_count(sp)<2 && !sp->closed) || (sp_nodepath_subpath_get_node_count(sp)<1 && sp->closed))
 
521
            badSubPaths = g_list_append(badSubPaths, sp);
 
522
    }
 
523
 
 
524
    //Delete them.  This second step is because sp_nodepath_subpath_destroy()
 
525
    //also removes the subpath from nodepath->subpaths
 
526
    for (GList *l = badSubPaths; l ; l=l->next) {
 
527
       Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
 
528
        sp_nodepath_subpath_destroy(sp);
 
529
    }
 
530
 
 
531
    g_list_free(badSubPaths);
 
532
}
 
533
 
 
534
/**
 
535
 * Create new nodepaths from pathvector, make it subpaths of np.
 
536
 * \param t The node type array.
 
537
 */
 
538
static void subpaths_from_pathvector(Inkscape::NodePath::Path *np, Geom::PathVector const & pathv, Inkscape::NodePath::NodeType const *t)
 
539
{
 
540
    guint i = 0;  // index into node type array
 
541
    for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
 
542
        if (pit->empty())
 
543
            continue;  // don't add single knot paths
 
544
 
 
545
        Inkscape::NodePath::SubPath *sp = sp_nodepath_subpath_new(np);
 
546
 
 
547
        Geom::Point ppos = pit->initialPoint() * np->i2d;
 
548
        NRPathcode pcode = NR_MOVETO;
 
549
 
 
550
        /* Johan: Note that this is pretty arcane code. I am pretty sure it is working correctly, be very certain to change it! (better to just rewrite this whole method)*/
 
551
        for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
 
552
            if( dynamic_cast<Geom::LineSegment const*>(&*cit) ||
 
553
                dynamic_cast<Geom::HLineSegment const*>(&*cit) ||
 
554
                dynamic_cast<Geom::VLineSegment const*>(&*cit) )
 
555
            {
 
556
                Geom::Point pos = cit->initialPoint() * (Geom::Matrix)np->i2d;
 
557
                sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &pos);
 
558
 
 
559
                ppos = cit->finalPoint() * (Geom::Matrix)np->i2d;
 
560
                pcode = NR_LINETO;
 
561
            }
 
562
            else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
 
563
                std::vector<Geom::Point> points = cubic_bezier->points();
 
564
                Geom::Point pos = points[0] * (Geom::Matrix)np->i2d;
 
565
                Geom::Point npos = points[1] * (Geom::Matrix)np->i2d;
 
566
                sp_nodepath_node_new(sp, NULL, t[i++], pcode, &ppos, &pos, &npos);
 
567
 
 
568
                ppos = points[2] * (Geom::Matrix)np->i2d;
 
569
                pcode = NR_CURVETO;
 
570
            }
 
571
        }
 
572
 
 
573
        if (pit->closed()) {
 
574
            // Add last knot (because sp_nodepath_subpath_close kills the last knot)
 
575
            /* Remember that last closing segment is always a lineto, but its length can be zero if the path is visually closed already
 
576
             * If the length is zero, don't add it to the nodepath. */
 
577
            Geom::Curve const &closing_seg = pit->back_closed();
 
578
            // Don't use !closing_seg.isDegenerate() as it is too precise, and does not account for floating point rounding probs (LP bug #257289)
 
579
            if ( ! are_near(closing_seg.initialPoint(), closing_seg.finalPoint()) ) {
 
580
                Geom::Point pos = closing_seg.finalPoint() * (Geom::Matrix)np->i2d;
 
581
                sp_nodepath_node_new(sp, NULL, t[i++], NR_LINETO, &pos, &pos, &pos);
 
582
            }
 
583
 
 
584
            sp_nodepath_subpath_close(sp);
 
585
        }
 
586
    }
 
587
}
 
588
 
 
589
/**
 
590
 * Convert from sodipodi:nodetypes to new style type array.
 
591
 */
 
592
static
 
593
Inkscape::NodePath::NodeType * parse_nodetypes(gchar const *types, guint length)
 
594
{
 
595
    Inkscape::NodePath::NodeType *typestr = new Inkscape::NodePath::NodeType[length + 1];
 
596
 
 
597
    guint pos = 0;
 
598
 
 
599
    if (types) {
 
600
        for (guint i = 0; types[i] && ( i < length ); i++) {
 
601
            while ((types[i] > '\0') && (types[i] <= ' ')) i++;
 
602
            if (types[i] != '\0') {
 
603
                switch (types[i]) {
 
604
                    case 's':
 
605
                        typestr[pos++] =Inkscape::NodePath::NODE_SMOOTH;
 
606
                        break;
 
607
                    case 'a':
 
608
                        typestr[pos++] =Inkscape::NodePath::NODE_AUTO;
 
609
                        break;
 
610
                    case 'z':
 
611
                        typestr[pos++] =Inkscape::NodePath::NODE_SYMM;
 
612
                        break;
 
613
                    case 'c':
 
614
                        typestr[pos++] =Inkscape::NodePath::NODE_CUSP;
 
615
                        break;
 
616
                    default:
 
617
                        typestr[pos++] =Inkscape::NodePath::NODE_NONE;
 
618
                        break;
 
619
                }
 
620
            }
 
621
        }
 
622
    }
 
623
 
 
624
    while (pos < length) {
 
625
        typestr[pos++] = Inkscape::NodePath::NODE_NONE;
 
626
    }
 
627
 
 
628
    return typestr;
 
629
}
 
630
 
 
631
/**
 
632
 * Make curve out of nodepath, write it into that nodepath's SPShape item so that display is
 
633
 * updated but repr is not (for speed). Used during curve and node drag.
 
634
 */
 
635
static void update_object(Inkscape::NodePath::Path *np)
 
636
{
 
637
    g_assert(np);
 
638
 
 
639
    np->curve->unref();
 
640
    np->curve = create_curve(np);
 
641
 
 
642
    sp_nodepath_set_curve(np, np->curve);
 
643
 
 
644
    if (np->show_helperpath) {
 
645
        SPCurve * helper_curve = np->curve->copy();
 
646
        helper_curve->transform(np->i2d);
 
647
        sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
 
648
        helper_curve->unref();
 
649
    }
 
650
 
 
651
    // updating helperpaths of LPEItems is now done in sp_lpe_item_update();
 
652
    //sp_nodepath_update_helperpaths(np);
 
653
 
 
654
    // now that nodepath and knotholder can be enabled simultaneously, we must update the knotholder, too
 
655
    // TODO: this should be done from ShapeEditor!! nodepath should be oblivious of knotholder!
 
656
    np->shape_editor->update_knotholder();
 
657
}
 
658
 
 
659
/**
 
660
 * Update XML path node with data from path object.
 
661
 */
 
662
static void update_repr_internal(Inkscape::NodePath::Path *np)
 
663
{
 
664
    g_assert(np);
 
665
 
 
666
    Inkscape::XML::Node *repr = np->object->repr;
 
667
 
 
668
    np->curve->unref();
 
669
    np->curve = create_curve(np);
 
670
 
 
671
    gchar *typestr = create_typestr(np);
 
672
    gchar *svgpath = sp_svg_write_path(np->curve->get_pathvector());
 
673
 
 
674
    // determine if path has an effect applied and write to correct "d" attribute.
 
675
    if (repr->attribute(np->repr_key) == NULL || strcmp(svgpath, repr->attribute(np->repr_key))) { // d changed
 
676
        np->local_change++;
 
677
        repr->setAttribute(np->repr_key, svgpath);
 
678
    }
 
679
 
 
680
    if (repr->attribute(np->repr_nodetypes_key) == NULL || strcmp(typestr, repr->attribute(np->repr_nodetypes_key))) { // nodetypes changed
 
681
        np->local_change++;
 
682
        repr->setAttribute(np->repr_nodetypes_key, typestr);
 
683
    }
 
684
 
 
685
    g_free(svgpath);
 
686
    g_free(typestr);
 
687
 
 
688
    if (np->show_helperpath) {
 
689
        SPCurve * helper_curve = np->curve->copy();
 
690
        helper_curve->transform(np->i2d);
 
691
        sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
 
692
        helper_curve->unref();
 
693
    }
 
694
 
 
695
    // TODO: do we need this call here? after all, update_object() should have been called just before
 
696
    //sp_nodepath_update_helperpaths(np);
 
697
}
 
698
 
 
699
/**
 
700
 * Update XML path node with data from path object, commit changes forever.
 
701
 */
 
702
void sp_nodepath_update_repr(Inkscape::NodePath::Path *np, const gchar *annotation)
 
703
{
 
704
    //fixme: np can be NULL, so check before proceeding
 
705
    g_return_if_fail(np != NULL);
 
706
 
 
707
    update_repr_internal(np);
 
708
    sp_canvas_end_forced_full_redraws(np->desktop->canvas);
 
709
 
 
710
    sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
 
711
                     annotation);
 
712
}
 
713
 
 
714
/**
 
715
 * Update XML path node with data from path object, commit changes with undo.
 
716
 */
 
717
static void sp_nodepath_update_repr_keyed(Inkscape::NodePath::Path *np, gchar const *key, const gchar *annotation)
 
718
{
 
719
    update_repr_internal(np);
 
720
    sp_document_maybe_done(sp_desktop_document(np->desktop), key, SP_VERB_CONTEXT_NODE,
 
721
                           annotation);
 
722
}
 
723
 
 
724
/**
 
725
 * Make duplicate of path, replace corresponding XML node in tree, commit.
 
726
 */
 
727
static void stamp_repr(Inkscape::NodePath::Path *np)
 
728
{
 
729
    g_assert(np);
 
730
 
 
731
    Inkscape::XML::Node *old_repr = np->object->repr;
 
732
    Inkscape::XML::Node *new_repr = old_repr->duplicate(old_repr->document());
 
733
 
 
734
    // remember the position of the item
 
735
    gint pos = old_repr->position();
 
736
    // remember parent
 
737
    Inkscape::XML::Node *parent = sp_repr_parent(old_repr);
 
738
 
 
739
    SPCurve *curve = create_curve(np);
 
740
    gchar *typestr = create_typestr(np);
 
741
 
 
742
    gchar *svgpath = sp_svg_write_path(curve->get_pathvector());
 
743
 
 
744
    new_repr->setAttribute(np->repr_key, svgpath);
 
745
    new_repr->setAttribute(np->repr_nodetypes_key, typestr);
 
746
 
 
747
    // add the new repr to the parent
 
748
    parent->appendChild(new_repr);
 
749
    // move to the saved position
 
750
    new_repr->setPosition(pos > 0 ? pos : 0);
 
751
 
 
752
    sp_document_done(sp_desktop_document(np->desktop), SP_VERB_CONTEXT_NODE,
 
753
                     _("Stamp"));
 
754
 
 
755
    Inkscape::GC::release(new_repr);
 
756
    g_free(svgpath);
 
757
    g_free(typestr);
 
758
    curve->unref();
 
759
}
 
760
 
 
761
/**
 
762
 * Create curve from path.
 
763
 */
 
764
static SPCurve *create_curve(Inkscape::NodePath::Path *np)
 
765
{
 
766
    SPCurve *curve = new SPCurve();
 
767
 
 
768
    for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
 
769
       Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
 
770
       curve->moveto(sp->first->pos * np->d2i);
 
771
       Inkscape::NodePath::Node *n = sp->first->n.other;
 
772
        while (n) {
 
773
            Geom::Point const end_pt = n->pos * np->d2i;
 
774
            if (!IS_FINITE(n->pos[0]) || !IS_FINITE(n->pos[1])){
 
775
                g_message("niet finite");
 
776
            }
 
777
            switch (n->code) {
 
778
                case NR_LINETO:
 
779
                    curve->lineto(end_pt);
 
780
                    break;
 
781
                case NR_CURVETO:
 
782
                    curve->curveto(n->p.other->n.pos * np->d2i,
 
783
                                     n->p.pos * np->d2i,
 
784
                                     end_pt);
 
785
                    break;
 
786
                default:
 
787
                    g_assert_not_reached();
 
788
                    break;
 
789
            }
 
790
            if (n != sp->last) {
 
791
                n = n->n.other;
 
792
            } else {
 
793
                n = NULL;
 
794
            }
 
795
        }
 
796
        if (sp->closed) {
 
797
            curve->closepath();
 
798
        }
 
799
    }
 
800
 
 
801
    return curve;
 
802
}
 
803
 
 
804
/**
 
805
 * Convert path type string to sodipodi:nodetypes style.
 
806
 */
 
807
static gchar *create_typestr(Inkscape::NodePath::Path *np)
 
808
{
 
809
    gchar *typestr = g_new(gchar, 32);
 
810
    gint len = 32;
 
811
    gint pos = 0;
 
812
 
 
813
    for (GList *spl = np->subpaths; spl != NULL; spl = spl->next) {
 
814
       Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *) spl->data;
 
815
 
 
816
        if (pos >= len) {
 
817
            typestr = g_renew(gchar, typestr, len + 32);
 
818
            len += 32;
 
819
        }
 
820
 
 
821
        typestr[pos++] = 'c';
 
822
 
 
823
       Inkscape::NodePath::Node *n;
 
824
        n = sp->first->n.other;
 
825
        while (n) {
 
826
            gchar code;
 
827
 
 
828
            switch (n->type) {
 
829
                case Inkscape::NodePath::NODE_CUSP:
 
830
                    code = 'c';
 
831
                    break;
 
832
                case Inkscape::NodePath::NODE_SMOOTH:
 
833
                    code = 's';
 
834
                    break;
 
835
                case Inkscape::NodePath::NODE_AUTO:
 
836
                    code = 'a';
 
837
                    break;
 
838
                case Inkscape::NodePath::NODE_SYMM:
 
839
                    code = 'z';
 
840
                    break;
 
841
                default:
 
842
                    g_assert_not_reached();
 
843
                    code = '\0';
 
844
                    break;
 
845
            }
 
846
 
 
847
            if (pos >= len) {
 
848
                typestr = g_renew(gchar, typestr, len + 32);
 
849
                len += 32;
 
850
            }
 
851
 
 
852
            typestr[pos++] = code;
 
853
 
 
854
            if (n != sp->last) {
 
855
                n = n->n.other;
 
856
            } else {
 
857
                n = NULL;
 
858
            }
 
859
        }
 
860
    }
 
861
 
 
862
    if (pos >= len) {
 
863
        typestr = g_renew(gchar, typestr, len + 1);
 
864
        len += 1;
 
865
    }
 
866
 
 
867
    typestr[pos++] = '\0';
 
868
 
 
869
    return typestr;
 
870
}
 
871
 
 
872
// Returns different message contexts depending on the current context. This function should only
 
873
// be called when ec is either a SPNodeContext or SPLPEToolContext, thus we return NULL in all
 
874
// other cases.
 
875
static Inkscape::MessageContext *
 
876
get_message_context(SPEventContext *ec)
 
877
{
 
878
    Inkscape::MessageContext *mc = 0;
 
879
 
 
880
    if (SP_IS_NODE_CONTEXT(ec)) {
 
881
        mc = SP_NODE_CONTEXT(ec)->_node_message_context;
 
882
    } else if (SP_IS_LPETOOL_CONTEXT(ec)) {
 
883
        mc = SP_LPETOOL_CONTEXT(ec)->_lpetool_message_context;
 
884
    } else {
 
885
        g_warning ("Nodepath should only be present in Node tool or Geometric tool.");
 
886
    }
 
887
 
 
888
    return mc;
 
889
}
 
890
 
 
891
/**
 
892
 \brief Fills node and handle positions for three nodes, splitting line
 
893
  marked by end at distance t.
 
894
 */
 
895
static void sp_nodepath_line_midpoint(Inkscape::NodePath::Node *new_path,Inkscape::NodePath::Node *end, gdouble t)
 
896
{
 
897
    g_assert(new_path != NULL);
 
898
    g_assert(end      != NULL);
 
899
 
 
900
    g_assert(end->p.other == new_path);
 
901
   Inkscape::NodePath::Node *start = new_path->p.other;
 
902
    g_assert(start);
 
903
 
 
904
    if (end->code == NR_LINETO) {
 
905
        new_path->type =Inkscape::NodePath::NODE_CUSP;
 
906
        new_path->code = NR_LINETO;
 
907
        new_path->pos = new_path->n.pos = new_path->p.pos = (t * start->pos + (1 - t) * end->pos);
 
908
    } else {
 
909
        new_path->type =Inkscape::NodePath::NODE_SMOOTH;
 
910
        new_path->code = NR_CURVETO;
 
911
        gdouble s      = 1 - t;
 
912
        for (int dim = 0; dim < 2; dim++) {
 
913
            Geom::Coord const f000 = start->pos[dim];
 
914
            Geom::Coord const f001 = start->n.pos[dim];
 
915
            Geom::Coord const f011 = end->p.pos[dim];
 
916
            Geom::Coord const f111 = end->pos[dim];
 
917
            Geom::Coord const f00t = s * f000 + t * f001;
 
918
            Geom::Coord const f01t = s * f001 + t * f011;
 
919
            Geom::Coord const f11t = s * f011 + t * f111;
 
920
            Geom::Coord const f0tt = s * f00t + t * f01t;
 
921
            Geom::Coord const f1tt = s * f01t + t * f11t;
 
922
            Geom::Coord const fttt = s * f0tt + t * f1tt;
 
923
            start->n.pos[dim]    = f00t;
 
924
            new_path->p.pos[dim] = f0tt;
 
925
            new_path->pos[dim]   = fttt;
 
926
            new_path->n.pos[dim] = f1tt;
 
927
            end->p.pos[dim]      = f11t;
 
928
        }
 
929
    }
 
930
}
 
931
 
 
932
/**
 
933
 * Adds new node on direct line between two nodes, activates handles of all
 
934
 * three nodes.
 
935
 */
 
936
static Inkscape::NodePath::Node *sp_nodepath_line_add_node(Inkscape::NodePath::Node *end, gdouble t)
 
937
{
 
938
    g_assert(end);
 
939
    g_assert(end->subpath);
 
940
    g_assert(g_list_find(end->subpath->nodes, end));
 
941
 
 
942
   Inkscape::NodePath::Node *start = end->p.other;
 
943
    g_assert( start->n.other == end );
 
944
   Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(end->subpath,
 
945
                                               end,
 
946
                                               (NRPathcode)end->code == NR_LINETO?
 
947
                                                  Inkscape::NodePath::NODE_CUSP : Inkscape::NodePath::NODE_SMOOTH,
 
948
                                               (NRPathcode)end->code,
 
949
                                               &start->pos, &start->pos, &start->n.pos);
 
950
    sp_nodepath_line_midpoint(newnode, end, t);
 
951
 
 
952
    sp_node_adjust_handles(start);
 
953
    sp_node_update_handles(start);
 
954
    sp_node_update_handles(newnode);
 
955
    sp_node_adjust_handles(end);
 
956
    sp_node_update_handles(end);
 
957
 
 
958
    return newnode;
 
959
}
 
960
 
 
961
/**
 
962
\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
 
963
*/
 
964
static Inkscape::NodePath::Node *sp_nodepath_node_break(Inkscape::NodePath::Node *node)
 
965
{
 
966
    g_assert(node);
 
967
    g_assert(node->subpath);
 
968
    g_assert(g_list_find(node->subpath->nodes, node));
 
969
 
 
970
    Inkscape::NodePath::Node* result = 0;
 
971
    Inkscape::NodePath::SubPath *sp = node->subpath;
 
972
    Inkscape::NodePath::Path *np    = sp->nodepath;
 
973
 
 
974
    if (sp->closed) {
 
975
        sp_nodepath_subpath_open(sp, node);
 
976
        result = sp->first;
 
977
    } else if ( (node == sp->first) || (node == sp->last ) ){
 
978
        // no break for end nodes
 
979
        result = 0;
 
980
    } else {
 
981
        // create a new subpath
 
982
        Inkscape::NodePath::SubPath *newsubpath = sp_nodepath_subpath_new(np);
 
983
 
 
984
        // duplicate the break node as start of the new subpath
 
985
        Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(newsubpath, NULL,
 
986
                                                                 static_cast<Inkscape::NodePath::NodeType>(node->type),
 
987
                                                                 NR_MOVETO, &node->pos, &node->pos, &node->n.pos);
 
988
 
 
989
        // attach rest of curve to new node
 
990
        g_assert(node->n.other);
 
991
        newnode->n.other = node->n.other; node->n.other = NULL;
 
992
        newnode->n.other->p.other = newnode;
 
993
        newsubpath->last = sp->last;
 
994
        sp->last = node;
 
995
        node = newnode;
 
996
        while (node->n.other) {
 
997
            node = node->n.other;
 
998
            node->subpath = newsubpath;
 
999
            sp->nodes = g_list_remove(sp->nodes, node);
 
1000
            newsubpath->nodes = g_list_prepend(newsubpath->nodes, node);
 
1001
        }
 
1002
 
 
1003
 
 
1004
        result = newnode;
 
1005
    }
 
1006
    return result;
 
1007
}
 
1008
 
 
1009
/**
 
1010
 * Duplicate node and connect to neighbours.
 
1011
 */
 
1012
static Inkscape::NodePath::Node *sp_nodepath_node_duplicate(Inkscape::NodePath::Node *node)
 
1013
{
 
1014
    g_assert(node);
 
1015
    g_assert(node->subpath);
 
1016
    g_assert(g_list_find(node->subpath->nodes, node));
 
1017
 
 
1018
   Inkscape::NodePath::SubPath *sp = node->subpath;
 
1019
 
 
1020
    NRPathcode code = (NRPathcode) node->code;
 
1021
    if (code == NR_MOVETO) { // if node is the endnode,
 
1022
        node->code = NR_LINETO; // new one is inserted before it, so change that to line
 
1023
    }
 
1024
 
 
1025
    Inkscape::NodePath::Node *newnode = sp_nodepath_node_new(sp, node, (Inkscape::NodePath::NodeType)node->type, code, &node->p.pos, &node->pos, &node->n.pos);
 
1026
 
 
1027
    if (!node->n.other || !node->p.other) { // if node is an endnode, select it
 
1028
        return node;
 
1029
    } else {
 
1030
        return newnode; // otherwise select the newly created node
 
1031
    }
 
1032
}
 
1033
 
 
1034
static void sp_node_handle_mirror_n_to_p(Inkscape::NodePath::Node *node)
 
1035
{
 
1036
    node->p.pos = (node->pos + (node->pos - node->n.pos));
 
1037
}
 
1038
 
 
1039
static void sp_node_handle_mirror_p_to_n(Inkscape::NodePath::Node *node)
 
1040
{
 
1041
    node->n.pos = (node->pos + (node->pos - node->p.pos));
 
1042
}
 
1043
 
 
1044
/**
 
1045
 * Change line type at node, with side effects on neighbours.
 
1046
 */
 
1047
static void sp_nodepath_set_line_type(Inkscape::NodePath::Node *end, NRPathcode code)
 
1048
{
 
1049
    g_assert(end);
 
1050
    g_assert(end->subpath);
 
1051
    g_assert(end->p.other);
 
1052
 
 
1053
    if (end->code != static_cast<guint>(code) ) {
 
1054
        Inkscape::NodePath::Node *start = end->p.other;
 
1055
 
 
1056
        end->code = code;
 
1057
 
 
1058
        if (code == NR_LINETO) {
 
1059
            if (start->code == NR_LINETO) {
 
1060
                sp_nodepath_set_node_type(start, Inkscape::NodePath::NODE_CUSP);
 
1061
            }
 
1062
            if (end->n.other) {
 
1063
                if (end->n.other->code == NR_LINETO) {
 
1064
                    sp_nodepath_set_node_type(end, Inkscape::NodePath::NODE_CUSP);
 
1065
                }
 
1066
            }
 
1067
 
 
1068
            if (start->type == Inkscape::NodePath::NODE_AUTO)
 
1069
                start->type = Inkscape::NodePath::NODE_SMOOTH;
 
1070
            if (end->type == Inkscape::NodePath::NODE_AUTO)
 
1071
                end->type = Inkscape::NodePath::NODE_SMOOTH;
 
1072
 
 
1073
            start->n.pos = start->pos;
 
1074
            end->p.pos = end->pos;
 
1075
 
 
1076
            sp_node_adjust_handle(start, -1);
 
1077
            sp_node_adjust_handle(end, 1);
 
1078
 
 
1079
        } else {
 
1080
            Geom::Point delta = end->pos - start->pos;
 
1081
            start->n.pos = start->pos + delta / 3;
 
1082
            end->p.pos = end->pos - delta / 3;
 
1083
            sp_node_adjust_handle(start, 1);
 
1084
            sp_node_adjust_handle(end, -1);
 
1085
        }
 
1086
 
 
1087
        sp_node_update_handles(start);
 
1088
        sp_node_update_handles(end);
 
1089
    }
 
1090
}
 
1091
 
 
1092
static void
 
1093
sp_nodepath_update_node_knot(Inkscape::NodePath::Node *node)
 
1094
{
 
1095
    if (node->type == Inkscape::NodePath::NODE_CUSP) {
 
1096
        node->knot->setShape (SP_KNOT_SHAPE_DIAMOND);
 
1097
        node->knot->setSize (node->selected? 11 : 9);
 
1098
        sp_knot_update_ctrl(node->knot);
 
1099
    } else if (node->type == Inkscape::NodePath::NODE_AUTO) {
 
1100
        node->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
 
1101
        node->knot->setSize (node->selected? 11 : 9);
 
1102
        sp_knot_update_ctrl(node->knot);
 
1103
    } else {
 
1104
        node->knot->setShape (SP_KNOT_SHAPE_SQUARE);
 
1105
        node->knot->setSize (node->selected? 9 : 7);
 
1106
        sp_knot_update_ctrl(node->knot);
 
1107
    }
 
1108
}
 
1109
 
 
1110
 
 
1111
/**
 
1112
 * Change node type, and its handles accordingly.
 
1113
 */
 
1114
static Inkscape::NodePath::Node *sp_nodepath_set_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
 
1115
{
 
1116
    g_assert(node);
 
1117
    g_assert(node->subpath);
 
1118
 
 
1119
    if ((node->p.other != NULL) && (node->n.other != NULL)) {
 
1120
        if ((node->code == NR_LINETO) && (node->n.other->code == NR_LINETO)) {
 
1121
            type =Inkscape::NodePath::NODE_CUSP;
 
1122
        }
 
1123
    }
 
1124
 
 
1125
    node->type = type;
 
1126
 
 
1127
    sp_nodepath_update_node_knot(node);
 
1128
 
 
1129
    // if one of handles is mouseovered, preserve its position
 
1130
    if (node->p.knot && SP_KNOT_IS_MOUSEOVER(node->p.knot)) {
 
1131
        sp_node_adjust_handle(node, 1);
 
1132
    } else if (node->n.knot && SP_KNOT_IS_MOUSEOVER(node->n.knot)) {
 
1133
        sp_node_adjust_handle(node, -1);
 
1134
    } else {
 
1135
        sp_node_adjust_handles(node);
 
1136
    }
 
1137
 
 
1138
    sp_node_update_handles(node);
 
1139
 
 
1140
    sp_nodepath_update_statusbar(node->subpath->nodepath);
 
1141
 
 
1142
    return node;
 
1143
}
 
1144
 
 
1145
bool
 
1146
sp_node_side_is_line (Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
 
1147
{
 
1148
// TODO clean up multiple returns
 
1149
        Inkscape::NodePath::Node *othernode = side->other;
 
1150
        if (!othernode)
 
1151
            return false;
 
1152
        NRPathcode const code = sp_node_path_code_from_side(node, side);
 
1153
        if (code == NR_LINETO)
 
1154
            return true;
 
1155
        Inkscape::NodePath::NodeSide *other_to_me = NULL;
 
1156
        if (&node->p == side) {
 
1157
            other_to_me = &othernode->n;
 
1158
        } else if (&node->n == side) {
 
1159
            other_to_me = &othernode->p;
 
1160
        }
 
1161
        if (!other_to_me)
 
1162
            return false;
 
1163
        bool is_line =
 
1164
             (Geom::L2(othernode->pos - other_to_me->pos) < 1e-6 &&
 
1165
              Geom::L2(node->pos - side->pos) < 1e-6);
 
1166
        return is_line;
 
1167
}
 
1168
 
 
1169
/**
 
1170
 * Same as sp_nodepath_set_node_type(), but also converts, if necessary, adjacent segments from
 
1171
 * lines to curves.  If adjacent to one line segment, pulls out or rotates opposite handle to align
 
1172
 * with that segment, procucing half-smooth node. If already half-smooth, pull out the second handle too.
 
1173
 * If already cusp and set to cusp, retracts handles.
 
1174
*/
 
1175
void sp_nodepath_convert_node_type(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeType type)
 
1176
{
 
1177
    if (type == Inkscape::NodePath::NODE_AUTO) {
 
1178
        if (node->p.other != NULL)
 
1179
            node->code = NR_CURVETO;
 
1180
        if (node->n.other != NULL)
 
1181
            node->n.other->code = NR_CURVETO;
 
1182
    }
 
1183
 
 
1184
    if (type == Inkscape::NodePath::NODE_SYMM || type == Inkscape::NodePath::NODE_SMOOTH) {
 
1185
 
 
1186
/*
 
1187
  Here's the algorithm of converting node to smooth (Shift+S or toolbar button), in pseudocode:
 
1188
 
 
1189
        if (two_handles) {
 
1190
            // do nothing, adjust_handles called via set_node_type will line them up
 
1191
        } else if (one_handle) {
 
1192
            if (opposite_to_handle_is_line) {
 
1193
                if (lined_up) {
 
1194
                    // already half-smooth; pull opposite handle too making it fully smooth
 
1195
                } else {
 
1196
                    // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
 
1197
                }
 
1198
            } else {
 
1199
                // pull opposite handle in line with the existing one
 
1200
            }
 
1201
        } else if (no_handles) {
 
1202
            if (both_segments_are_lines OR both_segments_are_curves) {
 
1203
                //pull both handles
 
1204
            } else {
 
1205
                // pull the handle opposite to line segment, making node half-smooth
 
1206
            }
 
1207
        }
 
1208
*/
 
1209
        bool p_has_handle = (Geom::L2(node->pos  - node->p.pos) > 1e-6);
 
1210
        bool n_has_handle = (Geom::L2(node->pos  - node->n.pos) > 1e-6);
 
1211
        bool p_is_line = sp_node_side_is_line(node, &node->p);
 
1212
        bool n_is_line = sp_node_side_is_line(node, &node->n);
 
1213
 
 
1214
        if (p_has_handle && n_has_handle) {
 
1215
            // do nothing, adjust_handles will line them up
 
1216
        } else if (p_has_handle || n_has_handle) {
 
1217
            if (p_has_handle && n_is_line) {
 
1218
                Radial line (node->n.other->pos - node->pos);
 
1219
                Radial handle (node->pos - node->p.pos);
 
1220
                if (fabs(line.a - handle.a) < 1e-3) { // lined up
 
1221
                    // already half-smooth; pull opposite handle too making it fully smooth
 
1222
                    node->n.pos = node->pos + (node->n.other->pos - node->pos) / 3;
 
1223
                } else {
 
1224
                    // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
 
1225
                }
 
1226
            } else if (n_has_handle && p_is_line) {
 
1227
                Radial line (node->p.other->pos - node->pos);
 
1228
                Radial handle (node->pos - node->n.pos);
 
1229
                if (fabs(line.a - handle.a) < 1e-3) { // lined up
 
1230
                    // already half-smooth; pull opposite handle too making it fully smooth
 
1231
                    node->p.pos = node->pos + (node->p.other->pos - node->pos) / 3;
 
1232
                } else {
 
1233
                    // do nothing, adjust_handles will line the handle  up, producing a half-smooth node
 
1234
                }
 
1235
            } else if (p_has_handle && node->n.other) {
 
1236
                // pull n handle
 
1237
                node->n.other->code = NR_CURVETO;
 
1238
                double len =  (type == Inkscape::NodePath::NODE_SYMM)?
 
1239
                    Geom::L2(node->p.pos - node->pos) :
 
1240
                    Geom::L2(node->n.other->pos - node->pos) / 3;
 
1241
                node->n.pos = node->pos - (len / Geom::L2(node->p.pos - node->pos)) * (node->p.pos - node->pos);
 
1242
            } else if (n_has_handle && node->p.other) {
 
1243
                // pull p handle
 
1244
                node->code = NR_CURVETO;
 
1245
                double len =  (type == Inkscape::NodePath::NODE_SYMM)?
 
1246
                    Geom::L2(node->n.pos - node->pos) :
 
1247
                    Geom::L2(node->p.other->pos - node->pos) / 3;
 
1248
                node->p.pos = node->pos - (len / Geom::L2(node->n.pos - node->pos)) * (node->n.pos - node->pos);
 
1249
            }
 
1250
        } else if (!p_has_handle && !n_has_handle) {
 
1251
            if ((p_is_line && n_is_line) || (!p_is_line && node->p.other && !n_is_line && node->n.other)) {
 
1252
                // no handles, but both segments are either lnes or curves:
 
1253
                //pull both handles
 
1254
 
 
1255
                // convert both to curves:
 
1256
                node->code = NR_CURVETO;
 
1257
                node->n.other->code = NR_CURVETO;
 
1258
 
 
1259
                sp_node_adjust_handles_auto(node);
 
1260
            } else {
 
1261
                // pull the handle opposite to line segment, making it half-smooth
 
1262
                if (p_is_line && node->n.other) {
 
1263
                    if (type != Inkscape::NodePath::NODE_SYMM) {
 
1264
                        // pull n handle
 
1265
                        node->n.other->code = NR_CURVETO;
 
1266
                        double len =  Geom::L2(node->n.other->pos - node->pos) / 3;
 
1267
                        node->n.pos = node->pos + (len / Geom::L2(node->p.other->pos - node->pos)) * (node->p.other->pos - node->pos);
 
1268
                    }
 
1269
                } else if (n_is_line && node->p.other) {
 
1270
                    if (type != Inkscape::NodePath::NODE_SYMM) {
 
1271
                        // pull p handle
 
1272
                        node->code = NR_CURVETO;
 
1273
                        double len =  Geom::L2(node->p.other->pos - node->pos) / 3;
 
1274
                        node->p.pos = node->pos + (len / Geom::L2(node->n.other->pos - node->pos)) * (node->n.other->pos - node->pos);
 
1275
                    }
 
1276
                }
 
1277
            }
 
1278
        }
 
1279
    } else if (type == Inkscape::NodePath::NODE_CUSP && node->type == Inkscape::NodePath::NODE_CUSP) {
 
1280
        // cusping a cusp: retract nodes
 
1281
        node->p.pos = node->pos;
 
1282
        node->n.pos = node->pos;
 
1283
    }
 
1284
 
 
1285
    sp_nodepath_set_node_type (node, type);
 
1286
}
 
1287
 
 
1288
/**
 
1289
 * Move node to point, and adjust its and neighbouring handles.
 
1290
 */
 
1291
void sp_node_moveto(Inkscape::NodePath::Node *node, Geom::Point p)
 
1292
{
 
1293
    if (node->type == Inkscape::NodePath::NODE_AUTO) {
 
1294
        node->pos = p;
 
1295
        sp_node_adjust_handles_auto(node);
 
1296
    } else {
 
1297
        Geom::Point delta = p - node->pos;
 
1298
        node->pos = p;
 
1299
 
 
1300
        node->p.pos += delta;
 
1301
        node->n.pos += delta;
 
1302
    }
 
1303
 
 
1304
    Inkscape::NodePath::Node *node_p = NULL;
 
1305
    Inkscape::NodePath::Node *node_n = NULL;
 
1306
 
 
1307
    if (node->p.other) {
 
1308
        if (node->code == NR_LINETO) {
 
1309
            sp_node_adjust_handle(node, 1);
 
1310
            sp_node_adjust_handle(node->p.other, -1);
 
1311
            node_p = node->p.other;
 
1312
        }
 
1313
        if (!node->p.other->selected && node->p.other->type == Inkscape::NodePath::NODE_AUTO) {
 
1314
            sp_node_adjust_handles_auto(node->p.other);
 
1315
            node_p = node->p.other;
 
1316
        }
 
1317
    }
 
1318
    if (node->n.other) {
 
1319
        if (node->n.other->code == NR_LINETO) {
 
1320
            sp_node_adjust_handle(node, -1);
 
1321
            sp_node_adjust_handle(node->n.other, 1);
 
1322
            node_n = node->n.other;
 
1323
        }
 
1324
        if (!node->n.other->selected && node->n.other->type == Inkscape::NodePath::NODE_AUTO) {
 
1325
            sp_node_adjust_handles_auto(node->n.other);
 
1326
            node_n = node->n.other;
 
1327
        }
 
1328
    }
 
1329
 
 
1330
    // this function is only called from batch movers that will update display at the end
 
1331
    // themselves, so here we just move all the knots without emitting move signals, for speed
 
1332
    sp_node_update_handles(node, false);
 
1333
    if (node_n) {
 
1334
        sp_node_update_handles(node_n, false);
 
1335
    }
 
1336
    if (node_p) {
 
1337
        sp_node_update_handles(node_p, false);
 
1338
    }
 
1339
}
 
1340
 
 
1341
/**
 
1342
 * Call sp_node_moveto() for node selection and handle possible snapping.
 
1343
 */
 
1344
static void sp_nodepath_selected_nodes_move(Inkscape::NodePath::Path *nodepath, Geom::Coord dx, Geom::Coord dy,
 
1345
                                            bool const snap, bool constrained = false,
 
1346
                                            Inkscape::Snapper::ConstraintLine const &constraint = Geom::Point())
 
1347
{
 
1348
    Geom::Point delta(dx, dy);
 
1349
    Geom::Point best_pt = delta;
 
1350
    Inkscape::SnappedPoint best;
 
1351
 
 
1352
    if (snap) {
 
1353
        /* When dragging a (selected) node, it should only snap to other nodes (i.e. unselected nodes), and
 
1354
         * not to itself. The snapper however can not tell which nodes are selected and which are not, so we
 
1355
         * must provide that information. */
 
1356
 
 
1357
        // Build a list of the unselected nodes to which the snapper should snap
 
1358
        std::vector<std::pair<Geom::Point, int> > unselected_nodes;
 
1359
        for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
 
1360
            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
 
1361
            for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
 
1362
                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
 
1363
                if (!node->selected) {
 
1364
                    unselected_nodes.push_back(std::make_pair(to_2geom(node->pos), node->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPTARGET_NODE_SMOOTH : Inkscape::SNAPTARGET_NODE_CUSP));
 
1365
                }
 
1366
            }
 
1367
        }
 
1368
 
 
1369
        SnapManager &m = nodepath->desktop->namedview->snap_manager;
 
1370
 
 
1371
        // When only the node closest to the mouse pointer is to be snapped
 
1372
        // then we will not even try to snap to other points and discard those immediately
 
1373
        Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
1374
        bool closest_only = prefs->getBool("/options/snapclosestonly/value", false);
 
1375
 
 
1376
        Inkscape::NodePath::Node *closest_node = NULL;
 
1377
        Geom::Coord closest_dist = NR_HUGE;
 
1378
 
 
1379
        if (closest_only) {
 
1380
                for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
1381
                        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
1382
                        Geom::Coord dist = Geom::L2(nodepath->drag_origin_mouse - n->origin);
 
1383
                        if (dist < closest_dist) {
 
1384
                                closest_node = n;
 
1385
                                closest_dist = dist;
 
1386
                        }
 
1387
                }
 
1388
        }
 
1389
 
 
1390
        // Iterate through all selected nodes
 
1391
        m.setup(nodepath->desktop, false, nodepath->item, &unselected_nodes);
 
1392
        for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
1393
            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
1394
            if (!closest_only || n == closest_node) { //try to snap either all selected nodes or only the closest one
 
1395
                    Inkscape::SnappedPoint s;
 
1396
                    Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
 
1397
                    if (constrained) {
 
1398
                        Inkscape::Snapper::ConstraintLine dedicated_constraint = constraint;
 
1399
                        dedicated_constraint.setPoint(n->pos);
 
1400
                        s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type, dedicated_constraint, false);
 
1401
                    } else {
 
1402
                        s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(n->pos + delta), source_type);
 
1403
                    }
 
1404
 
 
1405
                    if (s.getSnapped()) {
 
1406
                        s.setPointerDistance(Geom::L2(nodepath->drag_origin_mouse - n->origin));
 
1407
                        if (!s.isOtherSnapBetter(best, true)) {
 
1408
                                best = s;
 
1409
                                best_pt = from_2geom(s.getPoint()) - n->pos;
 
1410
                        }
 
1411
                    }
 
1412
            }
 
1413
        }
 
1414
 
 
1415
        if (best.getSnapped()) {
 
1416
            nodepath->desktop->snapindicator->set_new_snaptarget(best);
 
1417
        } else {
 
1418
            nodepath->desktop->snapindicator->remove_snaptarget();
 
1419
        }
 
1420
    }
 
1421
 
 
1422
    for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
1423
        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
1424
        sp_node_moveto(n, n->pos + best_pt);
 
1425
    }
 
1426
 
 
1427
    // do not update repr here so that node dragging is acceptably fast
 
1428
    update_object(nodepath);
 
1429
}
 
1430
 
 
1431
/**
 
1432
Function mapping x (in the range 0..1) to y (in the range 1..0) using a smooth half-bell-like
 
1433
curve; the parameter alpha determines how blunt (alpha > 1) or sharp (alpha < 1) will be the curve
 
1434
near x = 0.
 
1435
 */
 
1436
double
 
1437
sculpt_profile (double x, double alpha, guint profile)
 
1438
{
 
1439
    double result = 1;
 
1440
 
 
1441
    if (x >= 1) {
 
1442
        result = 0;
 
1443
    } else if (x <= 0) {
 
1444
        result = 1;
 
1445
    } else {
 
1446
        switch (profile) {
 
1447
            case SCULPT_PROFILE_LINEAR:
 
1448
                result = 1 - x;
 
1449
                break;
 
1450
            case SCULPT_PROFILE_BELL:
 
1451
                result = (0.5 * cos (M_PI * (pow(x, alpha))) + 0.5);
 
1452
                break;
 
1453
            case SCULPT_PROFILE_ELLIPTIC:
 
1454
                result = sqrt(1 - x*x);
 
1455
                break;
 
1456
            default:
 
1457
                g_assert_not_reached();
 
1458
        }
 
1459
    }
 
1460
 
 
1461
    return result;
 
1462
}
 
1463
 
 
1464
double
 
1465
bezier_length (Geom::Point a, Geom::Point ah, Geom::Point bh, Geom::Point b)
 
1466
{
 
1467
    // extremely primitive for now, don't have time to look for the real one
 
1468
    double lower = Geom::L2(b - a);
 
1469
    double upper = Geom::L2(ah - a) + Geom::L2(bh - ah) + Geom::L2(bh - b);
 
1470
    return (lower + upper)/2;
 
1471
}
 
1472
 
 
1473
void
 
1474
sp_nodepath_move_node_and_handles (Inkscape::NodePath::Node *n, Geom::Point delta, Geom::Point delta_n, Geom::Point delta_p)
 
1475
{
 
1476
    n->pos = n->origin + delta;
 
1477
    n->n.pos = n->n.origin + delta_n;
 
1478
    n->p.pos = n->p.origin + delta_p;
 
1479
    sp_node_adjust_handles(n);
 
1480
    sp_node_update_handles(n, false);
 
1481
}
 
1482
 
 
1483
/**
 
1484
 * Displace selected nodes and their handles by fractions of delta (from their origins), depending
 
1485
 * on how far they are from the dragged node n.
 
1486
 */
 
1487
static void
 
1488
sp_nodepath_selected_nodes_sculpt(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, Geom::Point delta)
 
1489
{
 
1490
    g_assert (n);
 
1491
    g_assert (nodepath);
 
1492
    g_assert (n->subpath->nodepath == nodepath);
 
1493
 
 
1494
    double pressure = n->knot->pressure;
 
1495
    if (pressure == 0)
 
1496
        pressure = 0.5; // default
 
1497
    pressure = CLAMP (pressure, 0.2, 0.8);
 
1498
 
 
1499
    // map pressure to alpha = 1/5 ... 5
 
1500
    double alpha = 1 - 2 * fabs(pressure - 0.5);
 
1501
    if (pressure > 0.5)
 
1502
        alpha = 1/alpha;
 
1503
 
 
1504
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
1505
    guint profile = prefs->getInt("/tools/nodes/sculpting_profile", SCULPT_PROFILE_BELL);
 
1506
 
 
1507
    if (sp_nodepath_selection_get_subpath_count(nodepath) <= 1) {
 
1508
        // Only one subpath has selected nodes:
 
1509
        // use linear mode, where the distance from n to node being dragged is calculated along the path
 
1510
 
 
1511
        double n_sel_range = 0, p_sel_range = 0;
 
1512
        guint n_nodes = 0, p_nodes = 0;
 
1513
        guint n_sel_nodes = 0, p_sel_nodes = 0;
 
1514
 
 
1515
        // First pass: calculate ranges (TODO: we could cache them, as they don't change while dragging)
 
1516
        {
 
1517
            double n_range = 0, p_range = 0;
 
1518
            bool n_going = true, p_going = true;
 
1519
            Inkscape::NodePath::Node *n_node = n;
 
1520
            Inkscape::NodePath::Node *p_node = n;
 
1521
            do {
 
1522
                // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
 
1523
                if (n_node && n_going)
 
1524
                    n_node = n_node->n.other;
 
1525
                if (n_node == NULL) {
 
1526
                    n_going = false;
 
1527
                } else {
 
1528
                    n_nodes ++;
 
1529
                    n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
 
1530
                    if (n_node->selected) {
 
1531
                        n_sel_nodes ++;
 
1532
                        n_sel_range = n_range;
 
1533
                    }
 
1534
                    if (n_node == p_node) {
 
1535
                        n_going = false;
 
1536
                        p_going = false;
 
1537
                    }
 
1538
                }
 
1539
                if (p_node && p_going)
 
1540
                    p_node = p_node->p.other;
 
1541
                if (p_node == NULL) {
 
1542
                    p_going = false;
 
1543
                } else {
 
1544
                    p_nodes ++;
 
1545
                    p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
 
1546
                    if (p_node->selected) {
 
1547
                        p_sel_nodes ++;
 
1548
                        p_sel_range = p_range;
 
1549
                    }
 
1550
                    if (p_node == n_node) {
 
1551
                        n_going = false;
 
1552
                        p_going = false;
 
1553
                    }
 
1554
                }
 
1555
            } while (n_going || p_going);
 
1556
        }
 
1557
 
 
1558
        // Second pass: actually move nodes in this subpath
 
1559
        sp_nodepath_move_node_and_handles (n, delta, delta, delta);
 
1560
        {
 
1561
            double n_range = 0, p_range = 0;
 
1562
            bool n_going = true, p_going = true;
 
1563
            Inkscape::NodePath::Node *n_node = n;
 
1564
            Inkscape::NodePath::Node *p_node = n;
 
1565
            do {
 
1566
                // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
 
1567
                if (n_node && n_going)
 
1568
                    n_node = n_node->n.other;
 
1569
                if (n_node == NULL) {
 
1570
                    n_going = false;
 
1571
                } else {
 
1572
                    n_range += bezier_length (n_node->p.other->origin, n_node->p.other->n.origin, n_node->p.origin, n_node->origin);
 
1573
                    if (n_node->selected) {
 
1574
                        sp_nodepath_move_node_and_handles (n_node,
 
1575
                                                           sculpt_profile (n_range / n_sel_range, alpha, profile) * delta,
 
1576
                                                           sculpt_profile ((n_range + Geom::L2(n_node->n.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta,
 
1577
                                                           sculpt_profile ((n_range - Geom::L2(n_node->p.origin - n_node->origin)) / n_sel_range, alpha, profile) * delta);
 
1578
                    }
 
1579
                    if (n_node == p_node) {
 
1580
                        n_going = false;
 
1581
                        p_going = false;
 
1582
                    }
 
1583
                }
 
1584
                if (p_node && p_going)
 
1585
                    p_node = p_node->p.other;
 
1586
                if (p_node == NULL) {
 
1587
                    p_going = false;
 
1588
                } else {
 
1589
                    p_range += bezier_length (p_node->n.other->origin, p_node->n.other->p.origin, p_node->n.origin, p_node->origin);
 
1590
                    if (p_node->selected) {
 
1591
                        sp_nodepath_move_node_and_handles (p_node,
 
1592
                                                           sculpt_profile (p_range / p_sel_range, alpha, profile) * delta,
 
1593
                                                           sculpt_profile ((p_range - Geom::L2(p_node->n.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta,
 
1594
                                                           sculpt_profile ((p_range + Geom::L2(p_node->p.origin - p_node->origin)) / p_sel_range, alpha, profile) * delta);
 
1595
                    }
 
1596
                    if (p_node == n_node) {
 
1597
                        n_going = false;
 
1598
                        p_going = false;
 
1599
                    }
 
1600
                }
 
1601
            } while (n_going || p_going);
 
1602
        }
 
1603
 
 
1604
    } else {
 
1605
        // Multiple subpaths have selected nodes:
 
1606
        // use spatial mode, where the distance from n to node being dragged is measured directly as Geom::L2.
 
1607
        // TODO: correct these distances taking into account their angle relative to the bisector, so as to
 
1608
        // fix the pear-like shape when sculpting e.g. a ring
 
1609
 
 
1610
        // First pass: calculate range
 
1611
        gdouble direct_range = 0;
 
1612
        for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
 
1613
            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
 
1614
            for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
 
1615
                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
 
1616
                if (node->selected) {
 
1617
                    direct_range = MAX(direct_range, Geom::L2(node->origin - n->origin));
 
1618
                }
 
1619
            }
 
1620
        }
 
1621
 
 
1622
        // Second pass: actually move nodes
 
1623
        for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
 
1624
            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
 
1625
            for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
 
1626
                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
 
1627
                if (node->selected) {
 
1628
                    if (direct_range > 1e-6) {
 
1629
                        sp_nodepath_move_node_and_handles (node,
 
1630
                                                       sculpt_profile (Geom::L2(node->origin - n->origin) / direct_range, alpha, profile) * delta,
 
1631
                                                       sculpt_profile (Geom::L2(node->n.origin - n->origin) / direct_range, alpha, profile) * delta,
 
1632
                                                       sculpt_profile (Geom::L2(node->p.origin - n->origin) / direct_range, alpha, profile) * delta);
 
1633
                    } else {
 
1634
                        sp_nodepath_move_node_and_handles (node, delta, delta, delta);
 
1635
                    }
 
1636
 
 
1637
                }
 
1638
            }
 
1639
        }
 
1640
    }
 
1641
 
 
1642
    // do not update repr here so that node dragging is acceptably fast
 
1643
    update_object(nodepath);
 
1644
}
 
1645
 
 
1646
 
 
1647
/**
 
1648
 * Move node selection to point, adjust its and neighbouring handles,
 
1649
 * handle possible snapping, and commit the change with possible undo.
 
1650
 */
 
1651
void
 
1652
sp_node_selected_move(Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
 
1653
{
 
1654
    if (!nodepath) return;
 
1655
 
 
1656
    sp_nodepath_selected_nodes_move(nodepath, dx, dy, false);
 
1657
 
 
1658
    if (dx == 0) {
 
1659
        sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
 
1660
    } else if (dy == 0) {
 
1661
        sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
 
1662
    } else {
 
1663
        sp_nodepath_update_repr(nodepath, _("Move nodes"));
 
1664
    }
 
1665
}
 
1666
 
 
1667
/**
 
1668
 * Move node selection off screen and commit the change.
 
1669
 */
 
1670
void
 
1671
sp_node_selected_move_screen(SPDesktop *desktop, Inkscape::NodePath::Path *nodepath, gdouble dx, gdouble dy)
 
1672
{
 
1673
    // borrowed from sp_selection_move_screen in selection-chemistry.c
 
1674
    // we find out the current zoom factor and divide deltas by it
 
1675
 
 
1676
    gdouble zoom = desktop->current_zoom();
 
1677
    gdouble zdx = dx / zoom;
 
1678
    gdouble zdy = dy / zoom;
 
1679
 
 
1680
    if (!nodepath) return;
 
1681
 
 
1682
    sp_nodepath_selected_nodes_move(nodepath, zdx, zdy, false);
 
1683
 
 
1684
    if (dx == 0) {
 
1685
        sp_nodepath_update_repr_keyed(nodepath, "node:move:vertical", _("Move nodes vertically"));
 
1686
    } else if (dy == 0) {
 
1687
        sp_nodepath_update_repr_keyed(nodepath, "node:move:horizontal", _("Move nodes horizontally"));
 
1688
    } else {
 
1689
        sp_nodepath_update_repr(nodepath, _("Move nodes"));
 
1690
    }
 
1691
}
 
1692
 
 
1693
/**
 
1694
 * Move selected nodes to the absolute position given
 
1695
 */
 
1696
void sp_node_selected_move_absolute(Inkscape::NodePath::Path *nodepath, Geom::Coord val, Geom::Dim2 axis)
 
1697
{
 
1698
    for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
1699
        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
1700
        Geom::Point npos(axis == Geom::X ? val : n->pos[Geom::X], axis == Geom::Y ? val : n->pos[Geom::Y]);
 
1701
        sp_node_moveto(n, npos);
 
1702
    }
 
1703
 
 
1704
    sp_nodepath_update_repr(nodepath, _("Move nodes"));
 
1705
}
 
1706
 
 
1707
/**
 
1708
 * If the coordinates of all selected nodes coincide, return the common coordinate; otherwise return Geom::Nothing
 
1709
 */
 
1710
boost::optional<Geom::Coord> sp_node_selected_common_coord (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
 
1711
{
 
1712
    boost::optional<Geom::Coord> no_coord;
 
1713
    g_return_val_if_fail(nodepath->selected, no_coord);
 
1714
 
 
1715
    // determine coordinate of first selected node
 
1716
    GList *nsel = nodepath->selected;
 
1717
    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nsel->data;
 
1718
    Geom::Coord coord = n->pos[axis];
 
1719
    bool coincide = true;
 
1720
 
 
1721
    // compare it to the coordinates of all the other selected nodes
 
1722
    for (GList *l = nsel->next; l != NULL; l = l->next) {
 
1723
        n = (Inkscape::NodePath::Node *) l->data;
 
1724
        if (n->pos[axis] != coord) {
 
1725
            coincide = false;
 
1726
        }
 
1727
    }
 
1728
    if (coincide) {
 
1729
        return coord;
 
1730
    } else {
 
1731
        Geom::Rect bbox = sp_node_selected_bbox(nodepath);
 
1732
        // currently we return the coordinate of the bounding box midpoint because I don't know how
 
1733
        // to erase the spin button entry field :), but maybe this can be useful behaviour anyway
 
1734
        return bbox.midpoint()[axis];
 
1735
    }
 
1736
}
 
1737
 
 
1738
/** If they don't yet exist, creates knot and line for the given side of the node */
 
1739
static void sp_node_ensure_knot_exists (SPDesktop *desktop, Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *side)
 
1740
{
 
1741
    if (!side->knot) {
 
1742
        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"));
 
1743
 
 
1744
        side->knot->setShape (SP_KNOT_SHAPE_CIRCLE);
 
1745
        side->knot->setSize (7);
 
1746
        side->knot->setAnchor (GTK_ANCHOR_CENTER);
 
1747
        side->knot->setFill(KNOT_FILL, KNOT_FILL_HI, KNOT_FILL_HI);
 
1748
        side->knot->setStroke(KNOT_STROKE, KNOT_STROKE_HI, KNOT_STROKE_HI);
 
1749
        sp_knot_update_ctrl(side->knot);
 
1750
 
 
1751
        g_signal_connect(G_OBJECT(side->knot), "clicked", G_CALLBACK(node_handle_clicked), node);
 
1752
        g_signal_connect(G_OBJECT(side->knot), "grabbed", G_CALLBACK(node_handle_grabbed), node);
 
1753
        g_signal_connect(G_OBJECT(side->knot), "ungrabbed", G_CALLBACK(node_handle_ungrabbed), node);
 
1754
        g_signal_connect(G_OBJECT(side->knot), "request", G_CALLBACK(node_handle_request), node);
 
1755
        g_signal_connect(G_OBJECT(side->knot), "moved", G_CALLBACK(node_handle_moved), node);
 
1756
        g_signal_connect(G_OBJECT(side->knot), "event", G_CALLBACK(node_handle_event), node);
 
1757
    }
 
1758
 
 
1759
    if (!side->line) {
 
1760
        side->line = sp_canvas_item_new(sp_desktop_controls(desktop),
 
1761
                                        SP_TYPE_CTRLLINE, NULL);
 
1762
    }
 
1763
}
 
1764
 
 
1765
/**
 
1766
 * Ensure the given handle of the node is visible/invisible, update its screen position
 
1767
 */
 
1768
static void sp_node_update_handle(Inkscape::NodePath::Node *node, gint which, gboolean show_handle, bool fire_move_signals)
 
1769
{
 
1770
    g_assert(node != NULL);
 
1771
 
 
1772
   Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
 
1773
    NRPathcode code = sp_node_path_code_from_side(node, side);
 
1774
 
 
1775
    show_handle = show_handle && (code == NR_CURVETO) && (Geom::L2(side->pos - node->pos) > 1e-6);
 
1776
 
 
1777
    if (show_handle) {
 
1778
        if (!side->knot) { // No handle knot at all
 
1779
            sp_node_ensure_knot_exists(node->subpath->nodepath->desktop, node, side);
 
1780
            // Just created, so we shouldn't fire the node_moved callback - instead set the knot position directly
 
1781
            side->knot->pos = side->pos;
 
1782
            if (side->knot->item)
 
1783
                SP_CTRL(side->knot->item)->moveto(side->pos);
 
1784
            sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
 
1785
            sp_knot_show(side->knot);
 
1786
        } else {
 
1787
            if (side->knot->pos != to_2geom(side->pos)) { // only if it's really moved
 
1788
                if (fire_move_signals) {
 
1789
                    sp_knot_set_position(side->knot, side->pos, 0); // this will set coords of the line as well
 
1790
                } else {
 
1791
                    sp_knot_moveto(side->knot, side->pos);
 
1792
                    sp_ctrlline_set_coords(SP_CTRLLINE(side->line), node->pos, side->pos);
 
1793
                }
 
1794
            }
 
1795
            if (!SP_KNOT_IS_VISIBLE(side->knot)) {
 
1796
                sp_knot_show(side->knot);
 
1797
            }
 
1798
        }
 
1799
        sp_canvas_item_show(side->line);
 
1800
    } else {
 
1801
        if (side->knot) {
 
1802
            if (SP_KNOT_IS_VISIBLE(side->knot)) {
 
1803
                sp_knot_hide(side->knot);
 
1804
            }
 
1805
        }
 
1806
        if (side->line) {
 
1807
            sp_canvas_item_hide(side->line);
 
1808
        }
 
1809
    }
 
1810
}
 
1811
 
 
1812
/**
 
1813
 * Ensure the node itself is visible, its handles and those of the neighbours of the node are
 
1814
 * visible if selected, update their screen positions. If fire_move_signals, move the node and its
 
1815
 * handles so that the corresponding signals are fired, callbacks are activated, and curve is
 
1816
 * updated; otherwise, just move the knots silently (used in batch moves).
 
1817
 */
 
1818
static void sp_node_update_handles(Inkscape::NodePath::Node *node, bool fire_move_signals)
 
1819
{
 
1820
    g_assert(node != NULL);
 
1821
 
 
1822
    if (!SP_KNOT_IS_VISIBLE(node->knot)) {
 
1823
        sp_knot_show(node->knot);
 
1824
    }
 
1825
 
 
1826
    if (node->knot->pos != to_2geom(node->pos)) { // visible knot is in a different position, need to update
 
1827
        if (fire_move_signals)
 
1828
            sp_knot_set_position(node->knot, node->pos, 0);
 
1829
        else
 
1830
            sp_knot_moveto(node->knot, node->pos);
 
1831
    }
 
1832
 
 
1833
    gboolean show_handles = node->selected;
 
1834
    if (node->p.other != NULL) {
 
1835
        if (node->p.other->selected) show_handles = TRUE;
 
1836
    }
 
1837
    if (node->n.other != NULL) {
 
1838
        if (node->n.other->selected) show_handles = TRUE;
 
1839
    }
 
1840
 
 
1841
    if (node->subpath->nodepath->show_handles == false)
 
1842
        show_handles = FALSE;
 
1843
 
 
1844
    sp_node_update_handle(node, -1, show_handles, fire_move_signals);
 
1845
    sp_node_update_handle(node, 1, show_handles, fire_move_signals);
 
1846
}
 
1847
 
 
1848
/**
 
1849
 * Call sp_node_update_handles() for all nodes on subpath.
 
1850
 */
 
1851
static void sp_nodepath_subpath_update_handles(Inkscape::NodePath::SubPath *subpath)
 
1852
{
 
1853
    g_assert(subpath != NULL);
 
1854
 
 
1855
    for (GList *l = subpath->nodes; l != NULL; l = l->next) {
 
1856
        sp_node_update_handles((Inkscape::NodePath::Node *) l->data);
 
1857
    }
 
1858
}
 
1859
 
 
1860
/**
 
1861
 * Call sp_nodepath_subpath_update_handles() for all subpaths of nodepath.
 
1862
 */
 
1863
static void sp_nodepath_update_handles(Inkscape::NodePath::Path *nodepath)
 
1864
{
 
1865
    g_assert(nodepath != NULL);
 
1866
 
 
1867
    for (GList *l = nodepath->subpaths; l != NULL; l = l->next) {
 
1868
        sp_nodepath_subpath_update_handles((Inkscape::NodePath::SubPath *) l->data);
 
1869
    }
 
1870
}
 
1871
 
 
1872
void
 
1873
sp_nodepath_show_handles(Inkscape::NodePath::Path *nodepath, bool show)
 
1874
{
 
1875
    if (nodepath) {
 
1876
        nodepath->show_handles = show;
 
1877
        sp_nodepath_update_handles(nodepath);
 
1878
    }
 
1879
}
 
1880
 
 
1881
/**
 
1882
 * Adds all selected nodes in nodepath to list.
 
1883
 */
 
1884
void Inkscape::NodePath::Path::selection(std::list<Node *> &l)
 
1885
{
 
1886
    StlConv<Node *>::list(l, selected);
 
1887
/// \todo this adds a copying, rework when the selection becomes a stl list
 
1888
}
 
1889
 
 
1890
/**
 
1891
 * Align selected nodes on the specified axis.
 
1892
 */
 
1893
void sp_nodepath_selected_align(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
 
1894
{
 
1895
    if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
 
1896
        return;
 
1897
    }
 
1898
 
 
1899
    if ( !nodepath->selected->next ) { // only one node selected
 
1900
        return;
 
1901
    }
 
1902
   Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
 
1903
    Geom::Point dest(pNode->pos);
 
1904
    for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
1905
        pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
 
1906
        if (pNode) {
 
1907
            dest[axis] = pNode->pos[axis];
 
1908
            sp_node_moveto(pNode, dest);
 
1909
        }
 
1910
    }
 
1911
 
 
1912
    sp_nodepath_update_repr(nodepath, _("Align nodes"));
 
1913
}
 
1914
 
 
1915
/// Helper struct.
 
1916
struct NodeSort
 
1917
{
 
1918
   Inkscape::NodePath::Node *_node;
 
1919
    Geom::Coord _coord;
 
1920
    /// \todo use vectorof pointers instead of calling copy ctor
 
1921
    NodeSort(Inkscape::NodePath::Node *node, Geom::Dim2 axis) :
 
1922
        _node(node), _coord(node->pos[axis])
 
1923
    {}
 
1924
 
 
1925
};
 
1926
 
 
1927
static bool operator<(NodeSort const &a, NodeSort const &b)
 
1928
{
 
1929
    return (a._coord < b._coord);
 
1930
}
 
1931
 
 
1932
/**
 
1933
 * Distribute selected nodes on the specified axis.
 
1934
 */
 
1935
void sp_nodepath_selected_distribute(Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis)
 
1936
{
 
1937
    if ( !nodepath || !nodepath->selected ) { // no nodepath, or no nodes selected
 
1938
        return;
 
1939
    }
 
1940
 
 
1941
    if ( ! (nodepath->selected->next && nodepath->selected->next->next) ) { // less than 3 nodes selected
 
1942
        return;
 
1943
    }
 
1944
 
 
1945
   Inkscape::NodePath::Node *pNode = reinterpret_cast<Inkscape::NodePath::Node *>(nodepath->selected->data);
 
1946
    std::vector<NodeSort> sorted;
 
1947
    for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
1948
        pNode = reinterpret_cast<Inkscape::NodePath::Node *>(l->data);
 
1949
        if (pNode) {
 
1950
            NodeSort n(pNode, axis);
 
1951
            sorted.push_back(n);
 
1952
            //dest[axis] = pNode->pos[axis];
 
1953
            //sp_node_moveto(pNode, dest);
 
1954
        }
 
1955
    }
 
1956
    std::sort(sorted.begin(), sorted.end());
 
1957
    unsigned int len = sorted.size();
 
1958
    //overall bboxes span
 
1959
    float dist = (sorted.back()._coord -
 
1960
                  sorted.front()._coord);
 
1961
    //new distance between each bbox
 
1962
    float step = (dist) / (len - 1);
 
1963
    float pos = sorted.front()._coord;
 
1964
    for ( std::vector<NodeSort> ::iterator it(sorted.begin());
 
1965
          it < sorted.end();
 
1966
          it ++ )
 
1967
    {
 
1968
        Geom::Point dest((*it)._node->pos);
 
1969
        dest[axis] = pos;
 
1970
        sp_node_moveto((*it)._node, dest);
 
1971
        pos += step;
 
1972
    }
 
1973
 
 
1974
    sp_nodepath_update_repr(nodepath, _("Distribute nodes"));
 
1975
}
 
1976
 
 
1977
 
 
1978
/**
 
1979
 * Call sp_nodepath_line_add_node() for all selected segments.
 
1980
 */
 
1981
void
 
1982
sp_node_selected_add_node(Inkscape::NodePath::Path *nodepath)
 
1983
{
 
1984
    if (!nodepath) {
 
1985
        return;
 
1986
    }
 
1987
 
 
1988
    GList *nl = NULL;
 
1989
 
 
1990
    int n_added = 0;
 
1991
 
 
1992
    for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
1993
       Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) l->data;
 
1994
        g_assert(t->selected);
 
1995
        if (t->p.other && t->p.other->selected) {
 
1996
            nl = g_list_prepend(nl, t);
 
1997
        }
 
1998
    }
 
1999
 
 
2000
    while (nl) {
 
2001
       Inkscape::NodePath::Node *t = (Inkscape::NodePath::Node *) nl->data;
 
2002
       Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(t, 0.5);
 
2003
       sp_nodepath_node_select(n, TRUE, FALSE);
 
2004
       n_added ++;
 
2005
       nl = g_list_remove(nl, t);
 
2006
    }
 
2007
 
 
2008
    /** \todo fixme: adjust ? */
 
2009
    sp_nodepath_update_handles(nodepath);
 
2010
 
 
2011
    if (n_added > 1) {
 
2012
        sp_nodepath_update_repr(nodepath, _("Add nodes"));
 
2013
    } else if (n_added > 0) {
 
2014
        sp_nodepath_update_repr(nodepath, _("Add node"));
 
2015
    }
 
2016
 
 
2017
    sp_nodepath_update_statusbar(nodepath);
 
2018
}
 
2019
 
 
2020
/**
 
2021
 * Select segment nearest to point
 
2022
 */
 
2023
void
 
2024
sp_nodepath_select_segment_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p, bool toggle)
 
2025
{
 
2026
    if (!nodepath) {
 
2027
        return;
 
2028
    }
 
2029
 
 
2030
    SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
 
2031
    Geom::PathVector const &pathv = curve->get_pathvector();
 
2032
    boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
 
2033
    if (!pvpos) {
 
2034
        g_print ("Possible error?\n");
 
2035
        return;
 
2036
    }
 
2037
 
 
2038
    // calculate index for nodepath's representation.
 
2039
    unsigned int segment_index = floor(pvpos->t) + 1;
 
2040
    for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
 
2041
        segment_index += pathv[i].size() + 1;
 
2042
        if (pathv[i].closed()) {
 
2043
            segment_index += 1;
 
2044
        }
 
2045
    }
 
2046
 
 
2047
    curve->unref();
 
2048
 
 
2049
    //find segment to segment
 
2050
    Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
 
2051
 
 
2052
    //fixme: this can return NULL, so check before proceeding.
 
2053
    g_return_if_fail(e != NULL);
 
2054
 
 
2055
    gboolean force = FALSE;
 
2056
    if (!(e->selected && (!e->p.other || e->p.other->selected))) {
 
2057
        force = TRUE;
 
2058
    }
 
2059
    sp_nodepath_node_select(e, (gboolean) toggle, force);
 
2060
    if (e->p.other)
 
2061
        sp_nodepath_node_select(e->p.other, TRUE, force);
 
2062
 
 
2063
    sp_nodepath_update_handles(nodepath);
 
2064
 
 
2065
    sp_nodepath_update_statusbar(nodepath);
 
2066
}
 
2067
 
 
2068
/**
 
2069
 * Add a node nearest to point
 
2070
 */
 
2071
void
 
2072
sp_nodepath_add_node_near_point(Inkscape::NodePath::Path *nodepath, Geom::Point p)
 
2073
{
 
2074
    if (!nodepath) {
 
2075
        return;
 
2076
    }
 
2077
 
 
2078
    SPCurve *curve = create_curve(nodepath);   // perhaps we can use nodepath->curve here instead?
 
2079
    Geom::PathVector const &pathv = curve->get_pathvector();
 
2080
    boost::optional<Geom::PathVectorPosition> pvpos = Geom::nearestPoint(pathv, p);
 
2081
    if (!pvpos) {
 
2082
        g_print ("Possible error?\n");
 
2083
        return;
 
2084
    }
 
2085
 
 
2086
    // calculate index for nodepath's representation.
 
2087
    double int_part;
 
2088
    double t = std::modf(pvpos->t, &int_part);
 
2089
    unsigned int segment_index = (unsigned int)int_part + 1;
 
2090
    for (unsigned int i = 0; i < pvpos->path_nr; ++i) {
 
2091
        segment_index += pathv[i].size() + 1;
 
2092
        if (pathv[i].closed()) {
 
2093
            segment_index += 1;
 
2094
        }
 
2095
    }
 
2096
 
 
2097
    curve->unref();
 
2098
 
 
2099
    //find segment to split
 
2100
    Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, segment_index);
 
2101
    if (!e) {
 
2102
        return;
 
2103
    }
 
2104
 
 
2105
    //don't know why but t seems to flip for lines
 
2106
    if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1)) == NR_LINETO) {
 
2107
        t = 1.0 - t;
 
2108
    }
 
2109
 
 
2110
    Inkscape::NodePath::Node *n = sp_nodepath_line_add_node(e, t);
 
2111
    sp_nodepath_node_select(n, FALSE, TRUE);
 
2112
 
 
2113
    /* fixme: adjust ? */
 
2114
    sp_nodepath_update_handles(nodepath);
 
2115
 
 
2116
    sp_nodepath_update_repr(nodepath, _("Add node"));
 
2117
 
 
2118
    sp_nodepath_update_statusbar(nodepath);
 
2119
}
 
2120
 
 
2121
/*
 
2122
 * Adjusts a segment so that t moves by a certain delta for dragging
 
2123
 * converts lines to curves
 
2124
 *
 
2125
 * method and idea borrowed from Simon Budig  <simon@gimp.org> and the GIMP
 
2126
 * cf. app/vectors/gimpbezierstroke.c, gimp_bezier_stroke_point_move_relative()
 
2127
 */
 
2128
void
 
2129
sp_nodepath_curve_drag(Inkscape::NodePath::Path *nodepath, int node, double t, Geom::Point delta)
 
2130
{
 
2131
    Inkscape::NodePath::Node *e = sp_nodepath_get_node_by_index(nodepath, node);
 
2132
 
 
2133
    //fixme: e and e->p can be NULL, so check for those before proceeding
 
2134
    g_return_if_fail(e != NULL);
 
2135
    g_return_if_fail(&e->p != NULL);
 
2136
 
 
2137
    if (e->type == Inkscape::NodePath::NODE_AUTO) {
 
2138
        e->type = Inkscape::NodePath::NODE_SMOOTH;
 
2139
        sp_nodepath_update_node_knot (e);
 
2140
    }
 
2141
    if (e->p.other->type == Inkscape::NodePath::NODE_AUTO) {
 
2142
        e->p.other->type = Inkscape::NodePath::NODE_SMOOTH;
 
2143
        sp_nodepath_update_node_knot (e->p.other);
 
2144
    }
 
2145
 
 
2146
    /* feel good is an arbitrary parameter that distributes the delta between handles
 
2147
     * if t of the drag point is less than 1/6 distance form the endpoint only
 
2148
     * the corresponding hadle is adjusted. This matches the behavior in GIMP
 
2149
     */
 
2150
    double feel_good;
 
2151
    if (t <= 1.0 / 6.0)
 
2152
        feel_good = 0;
 
2153
    else if (t <= 0.5)
 
2154
        feel_good = (pow((6 * t - 1) / 2.0, 3)) / 2;
 
2155
    else if (t <= 5.0 / 6.0)
 
2156
        feel_good = (1 - pow((6 * (1-t) - 1) / 2.0, 3)) / 2 + 0.5;
 
2157
    else
 
2158
        feel_good = 1;
 
2159
 
 
2160
    //if we're dragging a line convert it to a curve
 
2161
    if (sp_node_path_code_from_side(e, sp_node_get_side(e, -1))==NR_LINETO) {
 
2162
        sp_nodepath_set_line_type(e, NR_CURVETO);
 
2163
    }
 
2164
 
 
2165
    Geom::Point offsetcoord0 = ((1-feel_good)/(3*t*(1-t)*(1-t))) * delta;
 
2166
    Geom::Point offsetcoord1 = (feel_good/(3*t*t*(1-t))) * delta;
 
2167
    e->p.other->n.pos += offsetcoord0;
 
2168
    e->p.pos += offsetcoord1;
 
2169
 
 
2170
    // adjust handles of adjacent nodes where necessary
 
2171
    sp_node_adjust_handle(e,1);
 
2172
    sp_node_adjust_handle(e->p.other,-1);
 
2173
 
 
2174
    sp_nodepath_update_handles(e->subpath->nodepath);
 
2175
 
 
2176
    update_object(e->subpath->nodepath);
 
2177
 
 
2178
    sp_nodepath_update_statusbar(e->subpath->nodepath);
 
2179
}
 
2180
 
 
2181
 
 
2182
/**
 
2183
 * Call sp_nodepath_break() for all selected segments.
 
2184
 */
 
2185
void sp_node_selected_break(Inkscape::NodePath::Path *nodepath)
 
2186
{
 
2187
    if (!nodepath) return;
 
2188
 
 
2189
    GList *tempin = g_list_copy(nodepath->selected);
 
2190
    GList *temp = NULL;
 
2191
    for (GList *l = tempin; l != NULL; l = l->next) {
 
2192
       Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
2193
       Inkscape::NodePath::Node *nn = sp_nodepath_node_break(n);
 
2194
        if (nn == NULL) continue; // no break, no new node
 
2195
        temp = g_list_prepend(temp, nn);
 
2196
    }
 
2197
    g_list_free(tempin);
 
2198
 
 
2199
    if (temp) {
 
2200
        sp_nodepath_deselect(nodepath);
 
2201
    }
 
2202
    for (GList *l = temp; l != NULL; l = l->next) {
 
2203
        sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
 
2204
    }
 
2205
 
 
2206
    sp_nodepath_update_handles(nodepath);
 
2207
 
 
2208
    sp_nodepath_update_repr(nodepath, _("Break path"));
 
2209
}
 
2210
 
 
2211
/**
 
2212
 * Duplicate the selected node(s).
 
2213
 */
 
2214
void sp_node_selected_duplicate(Inkscape::NodePath::Path *nodepath)
 
2215
{
 
2216
    if (!nodepath) {
 
2217
        return;
 
2218
    }
 
2219
 
 
2220
    GList *temp = NULL;
 
2221
    for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
2222
       Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
2223
       Inkscape::NodePath::Node *nn = sp_nodepath_node_duplicate(n);
 
2224
        if (nn == NULL) continue; // could not duplicate
 
2225
        temp = g_list_prepend(temp, nn);
 
2226
    }
 
2227
 
 
2228
    if (temp) {
 
2229
        sp_nodepath_deselect(nodepath);
 
2230
    }
 
2231
    for (GList *l = temp; l != NULL; l = l->next) {
 
2232
        sp_nodepath_node_select((Inkscape::NodePath::Node *) l->data, TRUE, TRUE);
 
2233
    }
 
2234
 
 
2235
    sp_nodepath_update_handles(nodepath);
 
2236
 
 
2237
    sp_nodepath_update_repr(nodepath, _("Duplicate node"));
 
2238
}
 
2239
 
 
2240
/**
 
2241
 *  Internal function to join two nodes by merging them into one.
 
2242
 */
 
2243
static void do_node_selected_join(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
 
2244
{
 
2245
    /* a and b are endpoints */
 
2246
 
 
2247
    // if one of the two nodes is mouseovered, fix its position
 
2248
    Geom::Point c;
 
2249
    if (a->knot && SP_KNOT_IS_MOUSEOVER(a->knot)) {
 
2250
        c = a->pos;
 
2251
    } else if (b->knot && SP_KNOT_IS_MOUSEOVER(b->knot)) {
 
2252
        c = b->pos;
 
2253
    } else {
 
2254
        // otherwise, move joined node to the midpoint
 
2255
        c = (a->pos + b->pos) / 2;
 
2256
    }
 
2257
 
 
2258
    if (a->subpath == b->subpath) {
 
2259
       Inkscape::NodePath::SubPath *sp = a->subpath;
 
2260
        sp_nodepath_subpath_close(sp);
 
2261
        sp_node_moveto (sp->first, c);
 
2262
 
 
2263
        sp_nodepath_update_handles(sp->nodepath);
 
2264
        sp_nodepath_update_repr(nodepath, _("Close subpath"));
 
2265
        return;
 
2266
    }
 
2267
 
 
2268
    /* a and b are separate subpaths */
 
2269
    Inkscape::NodePath::SubPath *sa = a->subpath;
 
2270
    Inkscape::NodePath::SubPath *sb = b->subpath;
 
2271
    Geom::Point p;
 
2272
    Inkscape::NodePath::Node *n;
 
2273
    NRPathcode code;
 
2274
    if (a == sa->first) {
 
2275
        // we will now reverse sa, so that a is its last node, not first, and drop that node
 
2276
        p = sa->first->n.pos;
 
2277
        code = (NRPathcode)sa->first->n.other->code;
 
2278
        // create new subpath
 
2279
       Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
 
2280
       // create a first moveto node on it
 
2281
        n = sa->last;
 
2282
        sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
 
2283
        n = n->p.other;
 
2284
        if (n == sa->first) n = NULL;
 
2285
        while (n) {
 
2286
            // copy the rest of the nodes from sa to t, going backwards
 
2287
            sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
 
2288
            n = n->p.other;
 
2289
            if (n == sa->first) n = NULL;
 
2290
        }
 
2291
        // replace sa with t
 
2292
        sp_nodepath_subpath_destroy(sa);
 
2293
        sa = t;
 
2294
    } else if (a == sa->last) {
 
2295
        // a is already last, just drop it
 
2296
        p = sa->last->p.pos;
 
2297
        code = (NRPathcode)sa->last->code;
 
2298
        sp_nodepath_node_destroy(sa->last);
 
2299
    } else {
 
2300
        code = NR_END;
 
2301
        g_assert_not_reached();
 
2302
    }
 
2303
 
 
2304
    if (b == sb->first) {
 
2305
        // copy all nodes from b to a, forward
 
2306
        sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->first->n.pos);
 
2307
        for (n = sb->first->n.other; n != NULL; n = n->n.other) {
 
2308
            sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
 
2309
        }
 
2310
    } else if (b == sb->last) {
 
2311
        // copy all nodes from b to a, backward
 
2312
        sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &c, &sb->last->p.pos);
 
2313
        for (n = sb->last->p.other; n != NULL; n = n->p.other) {
 
2314
            sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
 
2315
        }
 
2316
    } else {
 
2317
        g_assert_not_reached();
 
2318
    }
 
2319
    /* and now destroy sb */
 
2320
 
 
2321
    sp_nodepath_subpath_destroy(sb);
 
2322
 
 
2323
    sp_nodepath_update_handles(sa->nodepath);
 
2324
 
 
2325
    sp_nodepath_update_repr(nodepath, _("Join nodes"));
 
2326
 
 
2327
    sp_nodepath_update_statusbar(nodepath);
 
2328
}
 
2329
 
 
2330
/**
 
2331
 *  Internal function to join two nodes by adding a segment between them.
 
2332
 */
 
2333
static void do_node_selected_join_segment(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *a, Inkscape::NodePath::Node *b)
 
2334
{
 
2335
    if (a->subpath == b->subpath) {
 
2336
       Inkscape::NodePath::SubPath *sp = a->subpath;
 
2337
 
 
2338
        /*similar to sp_nodepath_subpath_close(sp), without the node destruction*/
 
2339
        sp->closed = TRUE;
 
2340
 
 
2341
        sp->first->p.other = sp->last;
 
2342
        sp->last->n.other  = sp->first;
 
2343
 
 
2344
        sp_node_handle_mirror_p_to_n(sp->last);
 
2345
        sp_node_handle_mirror_n_to_p(sp->first);
 
2346
 
 
2347
        sp->first->code = sp->last->code;
 
2348
        sp->first       = sp->last;
 
2349
 
 
2350
        sp_nodepath_update_handles(sp->nodepath);
 
2351
 
 
2352
        sp_nodepath_update_repr(nodepath, _("Close subpath by segment"));
 
2353
 
 
2354
        return;
 
2355
    }
 
2356
 
 
2357
    /* a and b are separate subpaths */
 
2358
    Inkscape::NodePath::SubPath *sa = a->subpath;
 
2359
    Inkscape::NodePath::SubPath *sb = b->subpath;
 
2360
 
 
2361
    Inkscape::NodePath::Node *n;
 
2362
    Geom::Point p;
 
2363
    NRPathcode code;
 
2364
    if (a == sa->first) {
 
2365
        code = (NRPathcode) sa->first->n.other->code;
 
2366
       Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(sa->nodepath);
 
2367
        n = sa->last;
 
2368
        sp_nodepath_node_new(t, NULL,Inkscape::NodePath::NODE_CUSP, NR_MOVETO, &n->n.pos, &n->pos, &n->p.pos);
 
2369
        for (n = n->p.other; n != NULL; n = n->p.other) {
 
2370
            sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
 
2371
        }
 
2372
        sp_nodepath_subpath_destroy(sa);
 
2373
        sa = t;
 
2374
    } else if (a == sa->last) {
 
2375
        code = (NRPathcode)sa->last->code;
 
2376
    } else {
 
2377
        code = NR_END;
 
2378
        g_assert_not_reached();
 
2379
    }
 
2380
 
 
2381
    if (b == sb->first) {
 
2382
        n = sb->first;
 
2383
        sp_node_handle_mirror_p_to_n(sa->last);
 
2384
        sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &n->p.pos, &n->pos, &n->n.pos);
 
2385
        sp_node_handle_mirror_n_to_p(sa->last);
 
2386
        for (n = n->n.other; n != NULL; n = n->n.other) {
 
2387
            sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->code, &n->p.pos, &n->pos, &n->n.pos);
 
2388
        }
 
2389
    } else if (b == sb->last) {
 
2390
        n = sb->last;
 
2391
        sp_node_handle_mirror_p_to_n(sa->last);
 
2392
        sp_nodepath_node_new(sa, NULL,Inkscape::NodePath::NODE_CUSP, code, &p, &n->pos, &n->p.pos);
 
2393
        sp_node_handle_mirror_n_to_p(sa->last);
 
2394
        for (n = n->p.other; n != NULL; n = n->p.other) {
 
2395
            sp_nodepath_node_new(sa, NULL, (Inkscape::NodePath::NodeType)n->type, (NRPathcode)n->n.other->code, &n->n.pos, &n->pos, &n->p.pos);
 
2396
        }
 
2397
    } else {
 
2398
        g_assert_not_reached();
 
2399
    }
 
2400
    /* and now destroy sb */
 
2401
 
 
2402
    sp_nodepath_subpath_destroy(sb);
 
2403
 
 
2404
    sp_nodepath_update_handles(sa->nodepath);
 
2405
 
 
2406
    sp_nodepath_update_repr(nodepath, _("Join nodes by segment"));
 
2407
}
 
2408
 
 
2409
enum NodeJoinType { NODE_JOIN_ENDPOINTS, NODE_JOIN_SEGMENT };
 
2410
 
 
2411
/**
 
2412
 * Internal function to handle joining two nodes.
 
2413
 */
 
2414
static void node_do_selected_join(Inkscape::NodePath::Path *nodepath, NodeJoinType mode)
 
2415
{
 
2416
    if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
 
2417
 
 
2418
    if (g_list_length(nodepath->selected) != 2) {
 
2419
        nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
 
2420
        return;
 
2421
    }
 
2422
 
 
2423
    Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
 
2424
    Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
 
2425
 
 
2426
    g_assert(a != b);
 
2427
    if (!(a->p.other || a->n.other) || !(b->p.other || b->n.other)) {
 
2428
        // someone tried to join an orphan node (i.e. a single-node subpath).
 
2429
        // this is not worth an error message, just fail silently.
 
2430
        return;
 
2431
    }
 
2432
 
 
2433
    if (((a->subpath->closed) || (b->subpath->closed)) || (a->p.other && a->n.other) || (b->p.other && b->n.other)) {
 
2434
        nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("To join, you must have <b>two endnodes</b> selected."));
 
2435
        return;
 
2436
    }
 
2437
 
 
2438
    switch(mode) {
 
2439
        case NODE_JOIN_ENDPOINTS:
 
2440
            do_node_selected_join(nodepath, a, b);
 
2441
            break;
 
2442
        case NODE_JOIN_SEGMENT:
 
2443
            do_node_selected_join_segment(nodepath, a, b);
 
2444
            break;
 
2445
    }
 
2446
}
 
2447
 
 
2448
/**
 
2449
 *  Join two nodes by merging them into one.
 
2450
 */
 
2451
void sp_node_selected_join(Inkscape::NodePath::Path *nodepath)
 
2452
{
 
2453
    node_do_selected_join(nodepath, NODE_JOIN_ENDPOINTS);
 
2454
}
 
2455
 
 
2456
/**
 
2457
 *  Join two nodes by adding a segment between them.
 
2458
 */
 
2459
void sp_node_selected_join_segment(Inkscape::NodePath::Path *nodepath)
 
2460
{
 
2461
    node_do_selected_join(nodepath, NODE_JOIN_SEGMENT);
 
2462
}
 
2463
 
 
2464
/**
 
2465
 * Delete one or more selected nodes and preserve the shape of the path as much as possible.
 
2466
 */
 
2467
void sp_node_delete_preserve(GList *nodes_to_delete)
 
2468
{
 
2469
    GSList *nodepaths = NULL;
 
2470
 
 
2471
    while (nodes_to_delete) {
 
2472
        Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node*) g_list_first(nodes_to_delete)->data;
 
2473
        Inkscape::NodePath::SubPath *sp = node->subpath;
 
2474
        Inkscape::NodePath::Path *nodepath = sp->nodepath;
 
2475
        Inkscape::NodePath::Node *sample_cursor = NULL;
 
2476
        Inkscape::NodePath::Node *sample_end = NULL;
 
2477
        Inkscape::NodePath::Node *delete_cursor = node;
 
2478
        bool just_delete = false;
 
2479
 
 
2480
        //find the start of this contiguous selection
 
2481
        //move left to the first node that is not selected
 
2482
        //or the start of the non-closed path
 
2483
        for (Inkscape::NodePath::Node *curr=node->p.other; curr && curr!=node && g_list_find(nodes_to_delete, curr); curr=curr->p.other) {
 
2484
            delete_cursor = curr;
 
2485
        }
 
2486
 
 
2487
        //just delete at the beginning of an open path
 
2488
        if (!delete_cursor->p.other) {
 
2489
            sample_cursor = delete_cursor;
 
2490
            just_delete = true;
 
2491
        } else {
 
2492
            sample_cursor = delete_cursor->p.other;
 
2493
        }
 
2494
 
 
2495
        //calculate points for each segment
 
2496
        int rate = 5;
 
2497
        float period = 1.0 / rate;
 
2498
        std::vector<Geom::Point> data;
 
2499
        if (!just_delete) {
 
2500
            data.push_back(sample_cursor->pos);
 
2501
            for (Inkscape::NodePath::Node *curr=sample_cursor; curr; curr=curr->n.other) {
 
2502
                //just delete at the end of an open path
 
2503
                if (!sp->closed && curr == sp->last) {
 
2504
                    just_delete = true;
 
2505
                    break;
 
2506
                }
 
2507
 
 
2508
                //sample points on the contiguous selected segment
 
2509
                Geom::Point *bez;
 
2510
                bez = new Geom::Point [4];
 
2511
                bez[0] = curr->pos;
 
2512
                bez[1] = curr->n.pos;
 
2513
                bez[2] = curr->n.other->p.pos;
 
2514
                bez[3] = curr->n.other->pos;
 
2515
                for (int i=1; i<rate; i++) {
 
2516
                    gdouble t = i * period;
 
2517
                    Geom::Point p = bezier_pt(3, bez, t);
 
2518
                    data.push_back(p);
 
2519
                }
 
2520
                data.push_back(curr->n.other->pos);
 
2521
 
 
2522
                sample_end = curr->n.other;
 
2523
                //break if we've come full circle or hit the end of the selection
 
2524
                if (!g_list_find(nodes_to_delete, curr->n.other) || curr->n.other==sample_cursor) {
 
2525
                    break;
 
2526
                }
 
2527
            }
 
2528
        }
 
2529
 
 
2530
        if (!just_delete) {
 
2531
            //calculate the best fitting single segment and adjust the endpoints
 
2532
            Geom::Point *adata;
 
2533
            adata = new Geom::Point [data.size()];
 
2534
            copy(data.begin(), data.end(), adata);
 
2535
 
 
2536
            Geom::Point *bez;
 
2537
            bez = new Geom::Point [4];
 
2538
            //would decreasing error create a better fitting approximation?
 
2539
            gdouble error = 1.0;
 
2540
            gint ret;
 
2541
            ret = Geom::bezier_fit_cubic (bez, adata, data.size(), error);
 
2542
 
 
2543
            //if these nodes are smooth or symmetrical, the endpoints will be thrown out of sync.
 
2544
            //make sure these nodes are changed to cusp nodes so that, once the endpoints are moved,
 
2545
            //the resulting nodes behave as expected.
 
2546
            if (sample_cursor->type != Inkscape::NodePath::NODE_CUSP)
 
2547
                sp_nodepath_convert_node_type(sample_cursor, Inkscape::NodePath::NODE_CUSP);
 
2548
            if (sample_end->type != Inkscape::NodePath::NODE_CUSP)
 
2549
                sp_nodepath_convert_node_type(sample_end, Inkscape::NodePath::NODE_CUSP);
 
2550
 
 
2551
            //adjust endpoints
 
2552
            sample_cursor->n.pos = bez[1];
 
2553
            sample_end->p.pos = bez[2];
 
2554
        }
 
2555
 
 
2556
        //destroy this contiguous selection
 
2557
        while (delete_cursor && g_list_find(nodes_to_delete, delete_cursor)) {
 
2558
            Inkscape::NodePath::Node *temp = delete_cursor;
 
2559
            if (delete_cursor->n.other == delete_cursor) {
 
2560
                // delete_cursor->n points to itself, which means this is the last node on a closed subpath
 
2561
                delete_cursor = NULL;
 
2562
            } else {
 
2563
                delete_cursor = delete_cursor->n.other;
 
2564
            }
 
2565
            nodes_to_delete = g_list_remove(nodes_to_delete, temp);
 
2566
            sp_nodepath_node_destroy(temp);
 
2567
        }
 
2568
 
 
2569
        sp_nodepath_update_handles(nodepath);
 
2570
 
 
2571
        if (!g_slist_find(nodepaths, nodepath))
 
2572
            nodepaths = g_slist_prepend (nodepaths, nodepath);
 
2573
    }
 
2574
 
 
2575
    for (GSList *i = nodepaths; i; i = i->next) {
 
2576
        // FIXME: when/if we teach node tool to have more than one nodepath, deleting nodes from
 
2577
        // different nodepaths will give us one undo event per nodepath
 
2578
        Inkscape::NodePath::Path *nodepath = (Inkscape::NodePath::Path *) i->data;
 
2579
 
 
2580
        // if the entire nodepath is removed, delete the selected object.
 
2581
        if (nodepath->subpaths == NULL ||
 
2582
            //FIXME: a closed path CAN legally have one node, it's only an open one which must be
 
2583
            //at least 2
 
2584
            sp_nodepath_get_node_count(nodepath) < 2) {
 
2585
            SPDocument *document = sp_desktop_document (nodepath->desktop);
 
2586
            //FIXME: The following line will be wrong when we have mltiple nodepaths: we only want to
 
2587
            //delete this nodepath's object, not the entire selection! (though at this time, this
 
2588
            //does not matter)
 
2589
            sp_selection_delete(nodepath->desktop);
 
2590
            sp_document_done (document, SP_VERB_CONTEXT_NODE,
 
2591
                              _("Delete nodes"));
 
2592
        } else {
 
2593
            sp_nodepath_update_repr(nodepath, _("Delete nodes preserving shape"));
 
2594
            sp_nodepath_update_statusbar(nodepath);
 
2595
        }
 
2596
    }
 
2597
 
 
2598
    g_slist_free (nodepaths);
 
2599
}
 
2600
 
 
2601
/**
 
2602
 * Delete one or more selected nodes.
 
2603
 */
 
2604
void sp_node_selected_delete(Inkscape::NodePath::Path *nodepath)
 
2605
{
 
2606
    if (!nodepath) return;
 
2607
    if (!nodepath->selected) return;
 
2608
 
 
2609
    /** \todo fixme: do it the right way */
 
2610
    while (nodepath->selected) {
 
2611
       Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nodepath->selected->data;
 
2612
        sp_nodepath_node_destroy(node);
 
2613
    }
 
2614
 
 
2615
 
 
2616
    //clean up the nodepath (such as for trivial subpaths)
 
2617
    sp_nodepath_cleanup(nodepath);
 
2618
 
 
2619
    sp_nodepath_update_handles(nodepath);
 
2620
 
 
2621
    // if the entire nodepath is removed, delete the selected object.
 
2622
    if (nodepath->subpaths == NULL ||
 
2623
        sp_nodepath_get_node_count(nodepath) < 2) {
 
2624
        SPDocument *document = sp_desktop_document (nodepath->desktop);
 
2625
        sp_selection_delete(nodepath->desktop);
 
2626
        sp_document_done (document, SP_VERB_CONTEXT_NODE,
 
2627
                          _("Delete nodes"));
 
2628
        return;
 
2629
    }
 
2630
 
 
2631
    sp_nodepath_update_repr(nodepath, _("Delete nodes"));
 
2632
 
 
2633
    sp_nodepath_update_statusbar(nodepath);
 
2634
}
 
2635
 
 
2636
/**
 
2637
 * Delete one or more segments between two selected nodes.
 
2638
 * This is the code for 'split'.
 
2639
 */
 
2640
void
 
2641
sp_node_selected_delete_segment(Inkscape::NodePath::Path *nodepath)
 
2642
{
 
2643
   Inkscape::NodePath::Node *start, *end;     //Start , end nodes.  not inclusive
 
2644
   Inkscape::NodePath::Node *curr, *next;     //Iterators
 
2645
 
 
2646
    if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
 
2647
 
 
2648
    if (g_list_length(nodepath->selected) != 2) {
 
2649
        nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
 
2650
                                                 _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
 
2651
        return;
 
2652
    }
 
2653
 
 
2654
    //Selected nodes, not inclusive
 
2655
   Inkscape::NodePath::Node *a = (Inkscape::NodePath::Node *) nodepath->selected->data;
 
2656
   Inkscape::NodePath::Node *b = (Inkscape::NodePath::Node *) nodepath->selected->next->data;
 
2657
 
 
2658
    if ( ( a==b)                       ||  //same node
 
2659
         (a->subpath  != b->subpath )  ||  //not the same path
 
2660
         (!a->p.other || !a->n.other)  ||  //one of a's sides does not have a segment
 
2661
         (!b->p.other || !b->n.other) )    //one of b's sides does not have a segment
 
2662
    {
 
2663
        nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
 
2664
                                                 _("Select <b>two non-endpoint nodes</b> on a path between which to delete segments."));
 
2665
        return;
 
2666
    }
 
2667
 
 
2668
    //###########################################
 
2669
    //# BEGIN EDITS
 
2670
    //###########################################
 
2671
    //##################################
 
2672
    //# CLOSED PATH
 
2673
    //##################################
 
2674
    if (a->subpath->closed) {
 
2675
 
 
2676
 
 
2677
        gboolean reversed = FALSE;
 
2678
 
 
2679
        //Since we can go in a circle, we need to find the shorter distance.
 
2680
        //  a->b or b->a
 
2681
        start = end = NULL;
 
2682
        int distance    = 0;
 
2683
        int minDistance = 0;
 
2684
        for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
 
2685
            if (curr==b) {
 
2686
                //printf("a to b:%d\n", distance);
 
2687
                start = a;//go from a to b
 
2688
                end   = b;
 
2689
                minDistance = distance;
 
2690
                //printf("A to B :\n");
 
2691
                break;
 
2692
            }
 
2693
            distance++;
 
2694
        }
 
2695
 
 
2696
        //try again, the other direction
 
2697
        distance = 0;
 
2698
        for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
 
2699
            if (curr==a) {
 
2700
                //printf("b to a:%d\n", distance);
 
2701
                if (distance < minDistance) {
 
2702
                    start    = b;  //we go from b to a
 
2703
                    end      = a;
 
2704
                    reversed = TRUE;
 
2705
                    //printf("B to A\n");
 
2706
                }
 
2707
                break;
 
2708
            }
 
2709
            distance++;
 
2710
        }
 
2711
 
 
2712
 
 
2713
        //Copy everything from 'end' to 'start' to a new subpath
 
2714
       Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
 
2715
        for (curr=end ; curr ; curr=curr->n.other) {
 
2716
            NRPathcode code = (NRPathcode) curr->code;
 
2717
            if (curr == end)
 
2718
                code = NR_MOVETO;
 
2719
            sp_nodepath_node_new(t, NULL,
 
2720
                                 (Inkscape::NodePath::NodeType)curr->type, code,
 
2721
                                 &curr->p.pos, &curr->pos, &curr->n.pos);
 
2722
            if (curr == start)
 
2723
                break;
 
2724
        }
 
2725
        sp_nodepath_subpath_destroy(a->subpath);
 
2726
 
 
2727
 
 
2728
    }
 
2729
 
 
2730
 
 
2731
 
 
2732
    //##################################
 
2733
    //# OPEN PATH
 
2734
    //##################################
 
2735
    else {
 
2736
 
 
2737
        //We need to get the direction of the list between A and B
 
2738
        //Can we walk from a to b?
 
2739
        start = end = NULL;
 
2740
        for (curr = a->n.other ; curr && curr!=a ; curr=curr->n.other) {
 
2741
            if (curr==b) {
 
2742
                start = a;  //did it!  we go from a to b
 
2743
                end   = b;
 
2744
                //printf("A to B\n");
 
2745
                break;
 
2746
            }
 
2747
        }
 
2748
        if (!start) {//didn't work?  let's try the other direction
 
2749
            for (curr = b->n.other ; curr && curr!=b ; curr=curr->n.other) {
 
2750
                if (curr==a) {
 
2751
                    start = b;  //did it!  we go from b to a
 
2752
                    end   = a;
 
2753
                    //printf("B to A\n");
 
2754
                    break;
 
2755
                }
 
2756
            }
 
2757
        }
 
2758
        if (!start) {
 
2759
            nodepath->desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE,
 
2760
                                                     _("Cannot find path between nodes."));
 
2761
            return;
 
2762
        }
 
2763
 
 
2764
 
 
2765
 
 
2766
        //Copy everything after 'end' to a new subpath
 
2767
       Inkscape::NodePath::SubPath *t = sp_nodepath_subpath_new(nodepath);
 
2768
        for (curr=end ; curr ; curr=curr->n.other) {
 
2769
            NRPathcode code = (NRPathcode) curr->code;
 
2770
            if (curr == end)
 
2771
                code = NR_MOVETO;
 
2772
            sp_nodepath_node_new(t, NULL, (Inkscape::NodePath::NodeType)curr->type, code,
 
2773
                                 &curr->p.pos, &curr->pos, &curr->n.pos);
 
2774
        }
 
2775
 
 
2776
        //Now let us do our deletion.  Since the tail has been saved, go all the way to the end of the list
 
2777
        for (curr = start->n.other ; curr  ; curr=next) {
 
2778
            next = curr->n.other;
 
2779
            sp_nodepath_node_destroy(curr);
 
2780
        }
 
2781
 
 
2782
    }
 
2783
    //###########################################
 
2784
    //# END EDITS
 
2785
    //###########################################
 
2786
 
 
2787
    //clean up the nodepath (such as for trivial subpaths)
 
2788
    sp_nodepath_cleanup(nodepath);
 
2789
 
 
2790
    sp_nodepath_update_handles(nodepath);
 
2791
 
 
2792
    sp_nodepath_update_repr(nodepath, _("Delete segment"));
 
2793
 
 
2794
    sp_nodepath_update_statusbar(nodepath);
 
2795
}
 
2796
 
 
2797
/**
 
2798
 * Call sp_nodepath_set_line() for all selected segments.
 
2799
 */
 
2800
void
 
2801
sp_node_selected_set_line_type(Inkscape::NodePath::Path *nodepath, NRPathcode code)
 
2802
{
 
2803
    if (nodepath == NULL) return;
 
2804
 
 
2805
    for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
2806
       Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
2807
        g_assert(n->selected);
 
2808
        if (n->p.other && n->p.other->selected) {
 
2809
            sp_nodepath_set_line_type(n, code);
 
2810
        }
 
2811
    }
 
2812
 
 
2813
    sp_nodepath_update_repr(nodepath, _("Change segment type"));
 
2814
}
 
2815
 
 
2816
/**
 
2817
 * Call sp_nodepath_convert_node_type() for all selected nodes.
 
2818
 */
 
2819
void
 
2820
sp_node_selected_set_type(Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::NodeType type)
 
2821
{
 
2822
    if (nodepath == NULL) return;
 
2823
 
 
2824
    if (nodepath->straight_path) return; // don't change type when it is a straight path!
 
2825
 
 
2826
    for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
2827
        sp_nodepath_convert_node_type((Inkscape::NodePath::Node *) l->data, type);
 
2828
    }
 
2829
 
 
2830
    sp_nodepath_update_repr(nodepath, _("Change node type"));
 
2831
}
 
2832
 
 
2833
/**
 
2834
 * Change select status of node, update its own and neighbour handles.
 
2835
 */
 
2836
static void sp_node_set_selected(Inkscape::NodePath::Node *node, gboolean selected)
 
2837
{
 
2838
    node->selected = selected;
 
2839
 
 
2840
    if (selected) {
 
2841
        node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 11 : 9);
 
2842
        node->knot->setFill(NODE_FILL_SEL, NODE_FILL_SEL_HI, NODE_FILL_SEL_HI);
 
2843
        node->knot->setStroke(NODE_STROKE_SEL, NODE_STROKE_SEL_HI, NODE_STROKE_SEL_HI);
 
2844
        sp_knot_update_ctrl(node->knot);
 
2845
    } else {
 
2846
        node->knot->setSize ((node->type == Inkscape::NodePath::NODE_CUSP || node->type == Inkscape::NodePath::NODE_AUTO) ? 9 : 7);
 
2847
        node->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
 
2848
        node->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
 
2849
        sp_knot_update_ctrl(node->knot);
 
2850
    }
 
2851
 
 
2852
    sp_node_update_handles(node);
 
2853
    if (node->n.other) sp_node_update_handles(node->n.other);
 
2854
    if (node->p.other) sp_node_update_handles(node->p.other);
 
2855
}
 
2856
 
 
2857
/**
 
2858
\brief Select a node
 
2859
\param node     The node to select
 
2860
\param incremental   If true, add to selection, otherwise deselect others
 
2861
\param override   If true, always select this node, otherwise toggle selected status
 
2862
*/
 
2863
static void sp_nodepath_node_select(Inkscape::NodePath::Node *node, gboolean incremental, gboolean override)
 
2864
{
 
2865
    Inkscape::NodePath::Path *nodepath = node->subpath->nodepath;
 
2866
 
 
2867
    if (incremental) {
 
2868
        if (override) {
 
2869
            if (!g_list_find(nodepath->selected, node)) {
 
2870
                nodepath->selected = g_list_prepend(nodepath->selected, node);
 
2871
            }
 
2872
            sp_node_set_selected(node, TRUE);
 
2873
        } else { // toggle
 
2874
            if (node->selected) {
 
2875
                g_assert(g_list_find(nodepath->selected, node));
 
2876
                nodepath->selected = g_list_remove(nodepath->selected, node);
 
2877
            } else {
 
2878
                g_assert(!g_list_find(nodepath->selected, node));
 
2879
                nodepath->selected = g_list_prepend(nodepath->selected, node);
 
2880
            }
 
2881
            sp_node_set_selected(node, !node->selected);
 
2882
        }
 
2883
    } else {
 
2884
        sp_nodepath_deselect(nodepath);
 
2885
        nodepath->selected = g_list_prepend(nodepath->selected, node);
 
2886
        sp_node_set_selected(node, TRUE);
 
2887
    }
 
2888
 
 
2889
    sp_nodepath_update_statusbar(nodepath);
 
2890
}
 
2891
 
 
2892
 
 
2893
/**
 
2894
\brief Deselect all nodes in the nodepath
 
2895
*/
 
2896
void
 
2897
sp_nodepath_deselect(Inkscape::NodePath::Path *nodepath)
 
2898
{
 
2899
    if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
 
2900
 
 
2901
    while (nodepath->selected) {
 
2902
        sp_node_set_selected((Inkscape::NodePath::Node *) nodepath->selected->data, FALSE);
 
2903
        nodepath->selected = g_list_remove(nodepath->selected, nodepath->selected->data);
 
2904
    }
 
2905
    sp_nodepath_update_statusbar(nodepath);
 
2906
}
 
2907
 
 
2908
/**
 
2909
\brief Select or invert selection of all nodes in the nodepath
 
2910
*/
 
2911
void
 
2912
sp_nodepath_select_all(Inkscape::NodePath::Path *nodepath, bool invert)
 
2913
{
 
2914
    if (!nodepath) return;
 
2915
 
 
2916
    for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
 
2917
       Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
 
2918
        for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
 
2919
           Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
 
2920
           sp_nodepath_node_select(node, TRUE, invert? !node->selected : TRUE);
 
2921
        }
 
2922
    }
 
2923
}
 
2924
 
 
2925
/**
 
2926
 * If nothing selected, does the same as sp_nodepath_select_all();
 
2927
 * otherwise selects/inverts all nodes in all subpaths that have selected nodes
 
2928
 * (i.e., similar to "select all in layer", with the "selected" subpaths
 
2929
 * being treated as "layers" in the path).
 
2930
 */
 
2931
void
 
2932
sp_nodepath_select_all_from_subpath(Inkscape::NodePath::Path *nodepath, bool invert)
 
2933
{
 
2934
    if (!nodepath) return;
 
2935
 
 
2936
    if (g_list_length (nodepath->selected) == 0) {
 
2937
        sp_nodepath_select_all (nodepath, invert);
 
2938
        return;
 
2939
    }
 
2940
 
 
2941
    GList *copy = g_list_copy (nodepath->selected); // copy initial selection so that selecting in the loop does not affect us
 
2942
    GSList *subpaths = NULL;
 
2943
 
 
2944
    for (GList *l = copy; l != NULL; l = l->next) {
 
2945
        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
2946
        Inkscape::NodePath::SubPath *subpath = n->subpath;
 
2947
        if (!g_slist_find (subpaths, subpath))
 
2948
            subpaths = g_slist_prepend (subpaths, subpath);
 
2949
    }
 
2950
 
 
2951
    for (GSList *sp = subpaths; sp != NULL; sp = sp->next) {
 
2952
        Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) sp->data;
 
2953
        for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
 
2954
            Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
 
2955
            sp_nodepath_node_select(node, TRUE, invert? !g_list_find(copy, node) : TRUE);
 
2956
        }
 
2957
    }
 
2958
 
 
2959
    g_slist_free (subpaths);
 
2960
    g_list_free (copy);
 
2961
}
 
2962
 
 
2963
/**
 
2964
 * \brief Select the node after the last selected; if none is selected,
 
2965
 * select the first within path.
 
2966
 */
 
2967
void sp_nodepath_select_next(Inkscape::NodePath::Path *nodepath)
 
2968
{
 
2969
    if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
 
2970
 
 
2971
   Inkscape::NodePath::Node *last = NULL;
 
2972
    if (nodepath->selected) {
 
2973
        for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
 
2974
           Inkscape::NodePath::SubPath *subpath, *subpath_next;
 
2975
            subpath = (Inkscape::NodePath::SubPath *) spl->data;
 
2976
            for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
 
2977
               Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
 
2978
                if (node->selected) {
 
2979
                    if (node->n.other == (Inkscape::NodePath::Node *) subpath->last) {
 
2980
                        if (node->n.other == (Inkscape::NodePath::Node *) subpath->first) { // closed subpath
 
2981
                            if (spl->next) { // there's a next subpath
 
2982
                                subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
 
2983
                                last = subpath_next->first;
 
2984
                            } else if (spl->prev) { // there's a previous subpath
 
2985
                                last = NULL; // to be set later to the first node of first subpath
 
2986
                            } else {
 
2987
                                last = node->n.other;
 
2988
                            }
 
2989
                        } else {
 
2990
                            last = node->n.other;
 
2991
                        }
 
2992
                    } else {
 
2993
                        if (node->n.other) {
 
2994
                            last = node->n.other;
 
2995
                        } else {
 
2996
                            if (spl->next) { // there's a next subpath
 
2997
                                subpath_next = (Inkscape::NodePath::SubPath *) spl->next->data;
 
2998
                                last = subpath_next->first;
 
2999
                            } else if (spl->prev) { // there's a previous subpath
 
3000
                                last = NULL; // to be set later to the first node of first subpath
 
3001
                            } else {
 
3002
                                last = (Inkscape::NodePath::Node *) subpath->first;
 
3003
                            }
 
3004
                        }
 
3005
                    }
 
3006
                }
 
3007
            }
 
3008
        }
 
3009
        sp_nodepath_deselect(nodepath);
 
3010
    }
 
3011
 
 
3012
    if (last) { // there's at least one more node after selected
 
3013
        sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
 
3014
    } else { // no more nodes, select the first one in first subpath
 
3015
       Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) nodepath->subpaths->data;
 
3016
        sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->first, TRUE, TRUE);
 
3017
    }
 
3018
}
 
3019
 
 
3020
/**
 
3021
 * \brief Select the node before the first selected; if none is selected,
 
3022
 * select the last within path
 
3023
 */
 
3024
void sp_nodepath_select_prev(Inkscape::NodePath::Path *nodepath)
 
3025
{
 
3026
    if (!nodepath) return; // there's no nodepath when editing rects, stars, spirals or ellipses
 
3027
 
 
3028
   Inkscape::NodePath::Node *last = NULL;
 
3029
    if (nodepath->selected) {
 
3030
        for (GList *spl = g_list_last(nodepath->subpaths); spl != NULL; spl = spl->prev) {
 
3031
           Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
 
3032
            for (GList *nl = g_list_last(subpath->nodes); nl != NULL; nl = nl->prev) {
 
3033
               Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
 
3034
                if (node->selected) {
 
3035
                    if (node->p.other == (Inkscape::NodePath::Node *) subpath->first) {
 
3036
                        if (node->p.other == (Inkscape::NodePath::Node *) subpath->last) { // closed subpath
 
3037
                            if (spl->prev) { // there's a prev subpath
 
3038
                               Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
 
3039
                                last = subpath_prev->last;
 
3040
                            } else if (spl->next) { // there's a next subpath
 
3041
                                last = NULL; // to be set later to the last node of last subpath
 
3042
                            } else {
 
3043
                                last = node->p.other;
 
3044
                            }
 
3045
                        } else {
 
3046
                            last = node->p.other;
 
3047
                        }
 
3048
                    } else {
 
3049
                        if (node->p.other) {
 
3050
                            last = node->p.other;
 
3051
                        } else {
 
3052
                            if (spl->prev) { // there's a prev subpath
 
3053
                               Inkscape::NodePath::SubPath *subpath_prev = (Inkscape::NodePath::SubPath *) spl->prev->data;
 
3054
                                last = subpath_prev->last;
 
3055
                            } else if (spl->next) { // there's a next subpath
 
3056
                                last = NULL; // to be set later to the last node of last subpath
 
3057
                            } else {
 
3058
                                last = (Inkscape::NodePath::Node *) subpath->last;
 
3059
                            }
 
3060
                        }
 
3061
                    }
 
3062
                }
 
3063
            }
 
3064
        }
 
3065
        sp_nodepath_deselect(nodepath);
 
3066
    }
 
3067
 
 
3068
    if (last) { // there's at least one more node before selected
 
3069
        sp_nodepath_node_select((Inkscape::NodePath::Node *) last, TRUE, TRUE);
 
3070
    } else { // no more nodes, select the last one in last subpath
 
3071
        GList *spl = g_list_last(nodepath->subpaths);
 
3072
       Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
 
3073
        sp_nodepath_node_select((Inkscape::NodePath::Node *) subpath->last, TRUE, TRUE);
 
3074
    }
 
3075
}
 
3076
 
 
3077
/**
 
3078
 * \brief Select all nodes that are within the rectangle.
 
3079
 */
 
3080
void sp_nodepath_select_rect(Inkscape::NodePath::Path *nodepath, Geom::Rect const &b, gboolean incremental)
 
3081
{
 
3082
    if (!incremental) {
 
3083
        sp_nodepath_deselect(nodepath);
 
3084
    }
 
3085
 
 
3086
    for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
 
3087
       Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
 
3088
        for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
 
3089
           Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
 
3090
 
 
3091
            if (b.contains(node->pos)) {
 
3092
                sp_nodepath_node_select(node, TRUE, TRUE);
 
3093
            }
 
3094
        }
 
3095
    }
 
3096
}
 
3097
 
 
3098
 
 
3099
void
 
3100
nodepath_grow_selection_linearly (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
 
3101
{
 
3102
    g_assert (n);
 
3103
    g_assert (nodepath);
 
3104
    g_assert (n->subpath->nodepath == nodepath);
 
3105
 
 
3106
    if (g_list_length (nodepath->selected) == 0) {
 
3107
        if (grow > 0) {
 
3108
            sp_nodepath_node_select(n, TRUE, TRUE);
 
3109
        }
 
3110
        return;
 
3111
    }
 
3112
 
 
3113
    if (g_list_length (nodepath->selected) == 1) {
 
3114
        if (grow < 0) {
 
3115
            sp_nodepath_deselect (nodepath);
 
3116
            return;
 
3117
        }
 
3118
    }
 
3119
 
 
3120
        double n_sel_range = 0, p_sel_range = 0;
 
3121
            Inkscape::NodePath::Node *farthest_n_node = n;
 
3122
            Inkscape::NodePath::Node *farthest_p_node = n;
 
3123
 
 
3124
        // Calculate ranges
 
3125
        {
 
3126
            double n_range = 0, p_range = 0;
 
3127
            bool n_going = true, p_going = true;
 
3128
            Inkscape::NodePath::Node *n_node = n;
 
3129
            Inkscape::NodePath::Node *p_node = n;
 
3130
            do {
 
3131
                // Do one step in both directions from n, until reaching the end of subpath or bumping into each other
 
3132
                if (n_node && n_going)
 
3133
                    n_node = n_node->n.other;
 
3134
                if (n_node == NULL) {
 
3135
                    n_going = false;
 
3136
                } else {
 
3137
                    n_range += bezier_length (n_node->p.other->pos, n_node->p.other->n.pos, n_node->p.pos, n_node->pos);
 
3138
                    if (n_node->selected) {
 
3139
                        n_sel_range = n_range;
 
3140
                        farthest_n_node = n_node;
 
3141
                    }
 
3142
                    if (n_node == p_node) {
 
3143
                        n_going = false;
 
3144
                        p_going = false;
 
3145
                    }
 
3146
                }
 
3147
                if (p_node && p_going)
 
3148
                    p_node = p_node->p.other;
 
3149
                if (p_node == NULL) {
 
3150
                    p_going = false;
 
3151
                } else {
 
3152
                    p_range += bezier_length (p_node->n.other->pos, p_node->n.other->p.pos, p_node->n.pos, p_node->pos);
 
3153
                    if (p_node->selected) {
 
3154
                        p_sel_range = p_range;
 
3155
                        farthest_p_node = p_node;
 
3156
                    }
 
3157
                    if (p_node == n_node) {
 
3158
                        n_going = false;
 
3159
                        p_going = false;
 
3160
                    }
 
3161
                }
 
3162
            } while (n_going || p_going);
 
3163
        }
 
3164
 
 
3165
    if (grow > 0) {
 
3166
        if (n_sel_range < p_sel_range && farthest_n_node && farthest_n_node->n.other && !(farthest_n_node->n.other->selected)) {
 
3167
                sp_nodepath_node_select(farthest_n_node->n.other, TRUE, TRUE);
 
3168
        } else if (farthest_p_node && farthest_p_node->p.other && !(farthest_p_node->p.other->selected)) {
 
3169
                sp_nodepath_node_select(farthest_p_node->p.other, TRUE, TRUE);
 
3170
        }
 
3171
    } else {
 
3172
        if (n_sel_range > p_sel_range && farthest_n_node && farthest_n_node->selected) {
 
3173
                sp_nodepath_node_select(farthest_n_node, TRUE, FALSE);
 
3174
        } else if (farthest_p_node && farthest_p_node->selected) {
 
3175
                sp_nodepath_node_select(farthest_p_node, TRUE, FALSE);
 
3176
        }
 
3177
    }
 
3178
}
 
3179
 
 
3180
void
 
3181
nodepath_grow_selection_spatially (Inkscape::NodePath::Path *nodepath, Inkscape::NodePath::Node *n, int grow)
 
3182
{
 
3183
    g_assert (n);
 
3184
    g_assert (nodepath);
 
3185
    g_assert (n->subpath->nodepath == nodepath);
 
3186
 
 
3187
    if (g_list_length (nodepath->selected) == 0) {
 
3188
        if (grow > 0) {
 
3189
            sp_nodepath_node_select(n, TRUE, TRUE);
 
3190
        }
 
3191
        return;
 
3192
    }
 
3193
 
 
3194
    if (g_list_length (nodepath->selected) == 1) {
 
3195
        if (grow < 0) {
 
3196
            sp_nodepath_deselect (nodepath);
 
3197
            return;
 
3198
        }
 
3199
    }
 
3200
 
 
3201
    Inkscape::NodePath::Node *farthest_selected = NULL;
 
3202
    double farthest_dist = 0;
 
3203
 
 
3204
    Inkscape::NodePath::Node *closest_unselected = NULL;
 
3205
    double closest_dist = NR_HUGE;
 
3206
 
 
3207
    for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
 
3208
       Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
 
3209
        for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
 
3210
           Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
 
3211
           if (node == n)
 
3212
               continue;
 
3213
           if (node->selected) {
 
3214
               if (Geom::L2(node->pos - n->pos) > farthest_dist) {
 
3215
                   farthest_dist = Geom::L2(node->pos - n->pos);
 
3216
                   farthest_selected = node;
 
3217
               }
 
3218
           } else {
 
3219
               if (Geom::L2(node->pos - n->pos) < closest_dist) {
 
3220
                   closest_dist = Geom::L2(node->pos - n->pos);
 
3221
                   closest_unselected = node;
 
3222
               }
 
3223
           }
 
3224
        }
 
3225
    }
 
3226
 
 
3227
    if (grow > 0) {
 
3228
        if (closest_unselected) {
 
3229
            sp_nodepath_node_select(closest_unselected, TRUE, TRUE);
 
3230
        }
 
3231
    } else {
 
3232
        if (farthest_selected) {
 
3233
            sp_nodepath_node_select(farthest_selected, TRUE, FALSE);
 
3234
        }
 
3235
    }
 
3236
}
 
3237
 
 
3238
 
 
3239
/**
 
3240
\brief  Saves all nodes' and handles' current positions in their origin members
 
3241
*/
 
3242
void
 
3243
sp_nodepath_remember_origins(Inkscape::NodePath::Path *nodepath)
 
3244
{
 
3245
    for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
 
3246
       Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
 
3247
        for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
 
3248
           Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nl->data;
 
3249
           n->origin = n->pos;
 
3250
           n->p.origin = n->p.pos;
 
3251
           n->n.origin = n->n.pos;
 
3252
        }
 
3253
    }
 
3254
}
 
3255
 
 
3256
/**
 
3257
\brief  Saves selected nodes in a nodepath into a list containing integer positions of all selected nodes
 
3258
*/
 
3259
GList *save_nodepath_selection(Inkscape::NodePath::Path *nodepath)
 
3260
{
 
3261
    GList *r = NULL;
 
3262
    if (nodepath->selected) {
 
3263
        guint i = 0;
 
3264
        for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
 
3265
            Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
 
3266
            for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
 
3267
                Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
 
3268
                i++;
 
3269
                if (node->selected) {
 
3270
                    r = g_list_append(r, GINT_TO_POINTER(i));
 
3271
                }
 
3272
            }
 
3273
        }
 
3274
    }
 
3275
    return r;
 
3276
}
 
3277
 
 
3278
/**
 
3279
\brief  Restores selection by selecting nodes whose positions are in the list
 
3280
*/
 
3281
void restore_nodepath_selection(Inkscape::NodePath::Path *nodepath, GList *r)
 
3282
{
 
3283
    sp_nodepath_deselect(nodepath);
 
3284
 
 
3285
    guint i = 0;
 
3286
    for (GList *spl = nodepath->subpaths; spl != NULL; spl = spl->next) {
 
3287
       Inkscape::NodePath::SubPath *subpath = (Inkscape::NodePath::SubPath *) spl->data;
 
3288
        for (GList *nl = subpath->nodes; nl != NULL; nl = nl->next) {
 
3289
           Inkscape::NodePath::Node *node = (Inkscape::NodePath::Node *) nl->data;
 
3290
            i++;
 
3291
            if (g_list_find(r, GINT_TO_POINTER(i))) {
 
3292
                sp_nodepath_node_select(node, TRUE, TRUE);
 
3293
            }
 
3294
        }
 
3295
    }
 
3296
}
 
3297
 
 
3298
 
 
3299
/**
 
3300
\brief Adjusts handle according to node type and line code.
 
3301
*/
 
3302
static void sp_node_adjust_handle(Inkscape::NodePath::Node *node, gint which_adjust)
 
3303
{
 
3304
    g_assert(node);
 
3305
 
 
3306
    // nothing to do for auto nodes (sp_node_adjust_handles() does the job)
 
3307
    if (node->type == Inkscape::NodePath::NODE_AUTO)
 
3308
        return;
 
3309
 
 
3310
   Inkscape::NodePath::NodeSide *me = sp_node_get_side(node, which_adjust);
 
3311
   Inkscape::NodePath::NodeSide *other = sp_node_opposite_side(node, me);
 
3312
 
 
3313
   // nothing to do if we are an end node
 
3314
    if (me->other == NULL) return;
 
3315
    if (other->other == NULL) return;
 
3316
 
 
3317
    // nothing to do if we are a cusp node
 
3318
    if (node->type == Inkscape::NodePath::NODE_CUSP) return;
 
3319
 
 
3320
    // nothing to do if it's a line from the specified side of the node (i.e. no handle to adjust)
 
3321
    NRPathcode mecode;
 
3322
    if (which_adjust == 1) {
 
3323
        mecode = (NRPathcode)me->other->code;
 
3324
    } else {
 
3325
        mecode = (NRPathcode)node->code;
 
3326
    }
 
3327
    if (mecode == NR_LINETO) return;
 
3328
 
 
3329
    if (sp_node_side_is_line(node, other)) {
 
3330
        // other is a line, and we are either smooth or symm
 
3331
       Inkscape::NodePath::Node *othernode = other->other;
 
3332
        double len = Geom::L2(me->pos - node->pos);
 
3333
        Geom::Point delta = node->pos - othernode->pos;
 
3334
        double linelen = Geom::L2(delta);
 
3335
        if (linelen < 1e-18)
 
3336
            return;
 
3337
        me->pos = node->pos + (len / linelen)*delta;
 
3338
        return;
 
3339
    }
 
3340
 
 
3341
    if (node->type == Inkscape::NodePath::NODE_SYMM) {
 
3342
        // symmetrize
 
3343
        me->pos = 2 * node->pos - other->pos;
 
3344
        return;
 
3345
    } else {
 
3346
        // smoothify
 
3347
        double len = Geom::L2(me->pos - node->pos);
 
3348
        Geom::Point delta = other->pos - node->pos;
 
3349
        double otherlen = Geom::L2(delta);
 
3350
        if (otherlen < 1e-18) return;
 
3351
        me->pos = node->pos - (len / otherlen) * delta;
 
3352
    }
 
3353
}
 
3354
 
 
3355
/**
 
3356
 \brief Adjusts both handles according to node type and line code
 
3357
 */
 
3358
static void sp_node_adjust_handles(Inkscape::NodePath::Node *node)
 
3359
{
 
3360
    g_assert(node);
 
3361
 
 
3362
    if (node->type == Inkscape::NodePath::NODE_CUSP) return;
 
3363
 
 
3364
    /* we are either smooth or symm */
 
3365
 
 
3366
    if (node->p.other == NULL) return;
 
3367
    if (node->n.other == NULL) return;
 
3368
 
 
3369
    if (node->type == Inkscape::NodePath::NODE_AUTO) {
 
3370
        sp_node_adjust_handles_auto(node);
 
3371
        return;
 
3372
    }
 
3373
 
 
3374
    if (sp_node_side_is_line(node, &node->p)) {
 
3375
        sp_node_adjust_handle(node, 1);
 
3376
        return;
 
3377
    }
 
3378
 
 
3379
    if (sp_node_side_is_line(node, &node->n)) {
 
3380
        sp_node_adjust_handle(node, -1);
 
3381
        return;
 
3382
    }
 
3383
 
 
3384
    /* both are curves */
 
3385
    Geom::Point const delta( node->n.pos - node->p.pos );
 
3386
 
 
3387
    if (node->type == Inkscape::NodePath::NODE_SYMM) {
 
3388
        node->p.pos = node->pos - delta / 2;
 
3389
        node->n.pos = node->pos + delta / 2;
 
3390
        return;
 
3391
    }
 
3392
 
 
3393
    /* We are smooth */
 
3394
    double plen = Geom::L2(node->p.pos - node->pos);
 
3395
    if (plen < 1e-18) return;
 
3396
    double nlen = Geom::L2(node->n.pos - node->pos);
 
3397
    if (nlen < 1e-18) return;
 
3398
    node->p.pos = node->pos - (plen / (plen + nlen)) * delta;
 
3399
    node->n.pos = node->pos + (nlen / (plen + nlen)) * delta;
 
3400
}
 
3401
 
 
3402
static void sp_node_adjust_handles_auto(Inkscape::NodePath::Node *node)
 
3403
{
 
3404
    if (node->p.other == NULL || node->n.other == NULL) {
 
3405
        node->p.pos = node->pos;
 
3406
        node->n.pos = node->pos;
 
3407
        return;
 
3408
    }
 
3409
 
 
3410
    Geom::Point leg_prev = to_2geom(node->p.other->pos - node->pos);
 
3411
    Geom::Point leg_next = to_2geom(node->n.other->pos - node->pos);
 
3412
 
 
3413
    double norm_leg_prev = Geom::L2(leg_prev);
 
3414
    double norm_leg_next = Geom::L2(leg_next);
 
3415
 
 
3416
    Geom::Point delta;
 
3417
    if (norm_leg_next > 0.0) {
 
3418
        delta = (norm_leg_prev / norm_leg_next) * leg_next - leg_prev;
 
3419
        delta.normalize();
 
3420
    }
 
3421
 
 
3422
    node->p.pos = node->pos - norm_leg_prev / 3 * delta;
 
3423
    node->n.pos = node->pos + norm_leg_next / 3 * delta;
 
3424
}
 
3425
 
 
3426
/**
 
3427
 * Node event callback.
 
3428
 */
 
3429
static gboolean node_event(SPKnot */*knot*/, GdkEvent *event, Inkscape::NodePath::Node *n)
 
3430
{
 
3431
    gboolean ret = FALSE;
 
3432
    switch (event->type) {
 
3433
        case GDK_ENTER_NOTIFY:
 
3434
            Inkscape::NodePath::Path::active_node = n;
 
3435
            break;
 
3436
        case GDK_LEAVE_NOTIFY:
 
3437
            Inkscape::NodePath::Path::active_node = NULL;
 
3438
            break;
 
3439
        case GDK_SCROLL:
 
3440
            if ((event->scroll.state & GDK_CONTROL_MASK) && !(event->scroll.state & GDK_SHIFT_MASK)) { // linearly
 
3441
                switch (event->scroll.direction) {
 
3442
                    case GDK_SCROLL_UP:
 
3443
                        nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
 
3444
                        break;
 
3445
                    case GDK_SCROLL_DOWN:
 
3446
                        nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
 
3447
                        break;
 
3448
                    default:
 
3449
                        break;
 
3450
                }
 
3451
                ret = TRUE;
 
3452
            } else if (!(event->scroll.state & GDK_SHIFT_MASK)) { // spatially
 
3453
                switch (event->scroll.direction) {
 
3454
                    case GDK_SCROLL_UP:
 
3455
                        nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
 
3456
                        break;
 
3457
                    case GDK_SCROLL_DOWN:
 
3458
                        nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
 
3459
                        break;
 
3460
                    default:
 
3461
                        break;
 
3462
                }
 
3463
                ret = TRUE;
 
3464
            }
 
3465
            break;
 
3466
        case GDK_KEY_PRESS:
 
3467
            switch (get_group0_keyval (&event->key)) {
 
3468
                case GDK_space:
 
3469
                    if (event->key.state & GDK_BUTTON1_MASK) {
 
3470
                        Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
 
3471
                        stamp_repr(nodepath);
 
3472
                        ret = TRUE;
 
3473
                    }
 
3474
                    break;
 
3475
                case GDK_Page_Up:
 
3476
                    if (event->key.state & GDK_CONTROL_MASK) {
 
3477
                        nodepath_grow_selection_linearly (n->subpath->nodepath, n, +1);
 
3478
                    } else {
 
3479
                        nodepath_grow_selection_spatially (n->subpath->nodepath, n, +1);
 
3480
                    }
 
3481
                    break;
 
3482
                case GDK_Page_Down:
 
3483
                    if (event->key.state & GDK_CONTROL_MASK) {
 
3484
                        nodepath_grow_selection_linearly (n->subpath->nodepath, n, -1);
 
3485
                    } else {
 
3486
                        nodepath_grow_selection_spatially (n->subpath->nodepath, n, -1);
 
3487
                    }
 
3488
                    break;
 
3489
                default:
 
3490
                    break;
 
3491
            }
 
3492
            break;
 
3493
        default:
 
3494
            break;
 
3495
    }
 
3496
 
 
3497
    return ret;
 
3498
}
 
3499
 
 
3500
/**
 
3501
 * Handle keypress on node; directly called.
 
3502
 */
 
3503
gboolean node_key(GdkEvent *event)
 
3504
{
 
3505
    Inkscape::NodePath::Path *np;
 
3506
 
 
3507
    // there is no way to verify nodes so set active_node to nil when deleting!!
 
3508
    if (Inkscape::NodePath::Path::active_node == NULL) return FALSE;
 
3509
 
 
3510
    if ((event->type == GDK_KEY_PRESS) && !(event->key.state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))) {
 
3511
        gint ret = FALSE;
 
3512
        switch (get_group0_keyval (&event->key)) {
 
3513
            /// \todo FIXME: this does not seem to work, the keys are stolen by tool contexts!
 
3514
            case GDK_BackSpace:
 
3515
                np = Inkscape::NodePath::Path::active_node->subpath->nodepath;
 
3516
                sp_nodepath_node_destroy(Inkscape::NodePath::Path::active_node);
 
3517
                sp_nodepath_update_repr(np, _("Delete node"));
 
3518
                Inkscape::NodePath::Path::active_node = NULL;
 
3519
                ret = TRUE;
 
3520
                break;
 
3521
            case GDK_c:
 
3522
                sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_CUSP);
 
3523
                ret = TRUE;
 
3524
                break;
 
3525
            case GDK_s:
 
3526
                sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SMOOTH);
 
3527
                ret = TRUE;
 
3528
                break;
 
3529
            case GDK_a:
 
3530
                sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_AUTO);
 
3531
                ret = TRUE;
 
3532
                break;
 
3533
            case GDK_y:
 
3534
                sp_nodepath_set_node_type(Inkscape::NodePath::Path::active_node,Inkscape::NodePath::NODE_SYMM);
 
3535
                ret = TRUE;
 
3536
                break;
 
3537
            case GDK_b:
 
3538
                sp_nodepath_node_break(Inkscape::NodePath::Path::active_node);
 
3539
                ret = TRUE;
 
3540
                break;
 
3541
        }
 
3542
        return ret;
 
3543
    }
 
3544
    return FALSE;
 
3545
}
 
3546
 
 
3547
/**
 
3548
 * Mouseclick on node callback.
 
3549
 */
 
3550
static void node_clicked(SPKnot */*knot*/, guint state, gpointer data)
 
3551
{
 
3552
   Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
 
3553
 
 
3554
    if (state & GDK_CONTROL_MASK) {
 
3555
        Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
 
3556
 
 
3557
        if (!(state & GDK_MOD1_MASK)) { // ctrl+click: toggle node type
 
3558
            if (n->type == Inkscape::NodePath::NODE_CUSP) {
 
3559
                sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SMOOTH);
 
3560
            } else if (n->type == Inkscape::NodePath::NODE_SMOOTH) {
 
3561
                sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_SYMM);
 
3562
            } else if (n->type == Inkscape::NodePath::NODE_SYMM) {
 
3563
                sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_AUTO);
 
3564
            } else {
 
3565
                sp_nodepath_convert_node_type (n,Inkscape::NodePath::NODE_CUSP);
 
3566
            }
 
3567
            sp_nodepath_update_repr(nodepath, _("Change node type"));
 
3568
            sp_nodepath_update_statusbar(nodepath);
 
3569
 
 
3570
        } else { //ctrl+alt+click: delete node
 
3571
            GList *node_to_delete = NULL;
 
3572
            node_to_delete = g_list_append(node_to_delete, n);
 
3573
            sp_node_delete_preserve(node_to_delete);
 
3574
        }
 
3575
 
 
3576
    } else {
 
3577
        sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
 
3578
    }
 
3579
}
 
3580
 
 
3581
/**
 
3582
 * Mouse grabbed node callback.
 
3583
 */
 
3584
static void node_grabbed(SPKnot *knot, guint state, gpointer data)
 
3585
{
 
3586
   Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
 
3587
 
 
3588
    if (!n->selected) {
 
3589
        sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
 
3590
    }
 
3591
 
 
3592
    n->is_dragging = true;
 
3593
    //sp_event_context_snap_window_open(n->subpath->nodepath->desktop->canvas);
 
3594
    // Reconstruct and store the location of the mouse pointer at the time when we started dragging (needed for snapping)
 
3595
    n->subpath->nodepath->drag_origin_mouse = knot->grabbed_rel_pos + knot->drag_origin;
 
3596
 
 
3597
    sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
 
3598
 
 
3599
    sp_nodepath_remember_origins (n->subpath->nodepath);
 
3600
}
 
3601
 
 
3602
/**
 
3603
 * Mouse ungrabbed node callback.
 
3604
 */
 
3605
static void node_ungrabbed(SPKnot */*knot*/, guint /*state*/, gpointer data)
 
3606
{
 
3607
   Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
 
3608
 
 
3609
   n->dragging_out = NULL;
 
3610
   n->is_dragging = false;
 
3611
   //sp_event_context_snap_window_closed(n->subpath->nodepath->desktop->canvas);
 
3612
   n->subpath->nodepath->drag_origin_mouse = Geom::Point(NR_HUGE, NR_HUGE);
 
3613
   sp_canvas_end_forced_full_redraws(n->subpath->nodepath->desktop->canvas);
 
3614
 
 
3615
   sp_nodepath_update_repr(n->subpath->nodepath, _("Move nodes"));
 
3616
}
 
3617
 
 
3618
/**
 
3619
 * The point on a line, given by its angle, closest to the given point.
 
3620
 * \param p  A point.
 
3621
 * \param a  Angle of the line; it is assumed to go through coordinate origin.
 
3622
 * \param closest  Pointer to the point struct where the result is stored.
 
3623
 * \todo FIXME: use dot product perhaps?
 
3624
 */
 
3625
static void point_line_closest(Geom::Point *p, double a, Geom::Point *closest)
 
3626
{
 
3627
    if (a == HUGE_VAL) { // vertical
 
3628
        *closest = Geom::Point(0, (*p)[Geom::Y]);
 
3629
    } else {
 
3630
        (*closest)[Geom::X] = ( a * (*p)[Geom::Y] + (*p)[Geom::X]) / (a*a + 1);
 
3631
        (*closest)[Geom::Y] = a * (*closest)[Geom::X];
 
3632
    }
 
3633
}
 
3634
 
 
3635
/**
 
3636
 * Distance from the point to a line given by its angle.
 
3637
 * \param p  A point.
 
3638
 * \param a  Angle of the line; it is assumed to go through coordinate origin.
 
3639
 */
 
3640
static double point_line_distance(Geom::Point *p, double a)
 
3641
{
 
3642
    Geom::Point c;
 
3643
    point_line_closest(p, a, &c);
 
3644
    return sqrt(((*p)[Geom::X] - c[Geom::X])*((*p)[Geom::X] - c[Geom::X]) + ((*p)[Geom::Y] - c[Geom::Y])*((*p)[Geom::Y] - c[Geom::Y]));
 
3645
}
 
3646
 
 
3647
/**
 
3648
 * Callback for node "request" signal.
 
3649
 * \todo fixme: This goes to "moved" event? (lauris)
 
3650
 */
 
3651
static gboolean
 
3652
node_request(SPKnot */*knot*/, Geom::Point const &p, guint state, gpointer data)
 
3653
{
 
3654
    double yn, xn, yp, xp;
 
3655
    double an, ap, na, pa;
 
3656
    double d_an, d_ap, d_na, d_pa;
 
3657
    gboolean collinear = FALSE;
 
3658
    Geom::Point c;
 
3659
    Geom::Point pr;
 
3660
 
 
3661
    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
 
3662
 
 
3663
    n->subpath->nodepath->desktop->snapindicator->remove_snaptarget();
 
3664
 
 
3665
    // If either (Shift and some handle retracted), or (we're already dragging out a handle)
 
3666
    if ( (!n->subpath->nodepath->straight_path) &&
 
3667
         ( ((state & GDK_SHIFT_MASK) && ((n->n.other && n->n.pos == n->pos) || (n->p.other && n->p.pos == n->pos)))
 
3668
           || n->dragging_out ) )
 
3669
    {
 
3670
       Geom::Point mouse = p;
 
3671
 
 
3672
       if (!n->dragging_out) {
 
3673
           // This is the first drag-out event; find out which handle to drag out
 
3674
           double appr_n = (n->n.other ? Geom::L2(n->n.other->pos - n->pos) - Geom::L2(n->n.other->pos - p) : -HUGE_VAL);
 
3675
           double appr_p = (n->p.other ? Geom::L2(n->p.other->pos - n->pos) - Geom::L2(n->p.other->pos - p) : -HUGE_VAL);
 
3676
 
 
3677
           if (appr_p == -HUGE_VAL && appr_n == -HUGE_VAL) // orphan node?
 
3678
               return FALSE;
 
3679
 
 
3680
           Inkscape::NodePath::NodeSide *opposite;
 
3681
           if (appr_p > appr_n) { // closer to p
 
3682
               n->dragging_out = &n->p;
 
3683
               opposite = &n->n;
 
3684
               n->code = NR_CURVETO;
 
3685
           } else if (appr_p < appr_n) { // closer to n
 
3686
               n->dragging_out = &n->n;
 
3687
               opposite = &n->p;
 
3688
               n->n.other->code = NR_CURVETO;
 
3689
           } else { // p and n nodes are the same
 
3690
               if (n->n.pos != n->pos) { // n handle already dragged, drag p
 
3691
                   n->dragging_out = &n->p;
 
3692
                   opposite = &n->n;
 
3693
                   n->code = NR_CURVETO;
 
3694
               } else if (n->p.pos != n->pos) { // p handle already dragged, drag n
 
3695
                   n->dragging_out = &n->n;
 
3696
                   opposite = &n->p;
 
3697
                   n->n.other->code = NR_CURVETO;
 
3698
               } else { // find out to which handle of the adjacent node we're closer; note that n->n.other == n->p.other
 
3699
                   double appr_other_n = (n->n.other ? Geom::L2(n->n.other->n.pos - n->pos) - Geom::L2(n->n.other->n.pos - p) : -HUGE_VAL);
 
3700
                   double appr_other_p = (n->n.other ? Geom::L2(n->n.other->p.pos - n->pos) - Geom::L2(n->n.other->p.pos - p) : -HUGE_VAL);
 
3701
                   if (appr_other_p > appr_other_n) { // closer to other's p handle
 
3702
                       n->dragging_out = &n->n;
 
3703
                       opposite = &n->p;
 
3704
                       n->n.other->code = NR_CURVETO;
 
3705
                   } else { // closer to other's n handle
 
3706
                       n->dragging_out = &n->p;
 
3707
                       opposite = &n->n;
 
3708
                       n->code = NR_CURVETO;
 
3709
                   }
 
3710
               }
 
3711
           }
 
3712
 
 
3713
           // if there's another handle, make sure the one we drag out starts parallel to it
 
3714
           if (opposite->pos != n->pos) {
 
3715
               mouse = n->pos - Geom::L2(mouse - n->pos) * Geom::unit_vector(opposite->pos - n->pos);
 
3716
           }
 
3717
 
 
3718
           // knots might not be created yet!
 
3719
           sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, n->dragging_out);
 
3720
           sp_node_ensure_knot_exists (n->subpath->nodepath->desktop, n, opposite);
 
3721
       }
 
3722
 
 
3723
       // pass this on to the handle-moved callback
 
3724
       node_handle_moved(n->dragging_out->knot, mouse, state, (gpointer) n);
 
3725
       sp_node_update_handles(n);
 
3726
       return TRUE;
 
3727
   }
 
3728
 
 
3729
    if (state & GDK_CONTROL_MASK) { // constrained motion
 
3730
 
 
3731
        // calculate relative distances of handles
 
3732
        // n handle:
 
3733
        yn = n->n.pos[Geom::Y] - n->pos[Geom::Y];
 
3734
        xn = n->n.pos[Geom::X] - n->pos[Geom::X];
 
3735
        // if there's no n handle (straight line), see if we can use the direction to the next point on path
 
3736
        if ((n->n.other && n->n.other->code == NR_LINETO) || fabs(yn) + fabs(xn) < 1e-6) {
 
3737
            if (n->n.other) { // if there is the next point
 
3738
                if (L2(n->n.other->p.pos - n->n.other->pos) < 1e-6) // and the next point has no handle either
 
3739
                    yn = n->n.other->origin[Geom::Y] - n->origin[Geom::Y]; // use origin because otherwise the direction will change as you drag
 
3740
                    xn = n->n.other->origin[Geom::X] - n->origin[Geom::X];
 
3741
            }
 
3742
        }
 
3743
        if (xn < 0) { xn = -xn; yn = -yn; } // limit the angle to between 0 and pi
 
3744
        if (yn < 0) { xn = -xn; yn = -yn; }
 
3745
 
 
3746
        // p handle:
 
3747
        yp = n->p.pos[Geom::Y] - n->pos[Geom::Y];
 
3748
        xp = n->p.pos[Geom::X] - n->pos[Geom::X];
 
3749
        // if there's no p handle (straight line), see if we can use the direction to the prev point on path
 
3750
        if (n->code == NR_LINETO || fabs(yp) + fabs(xp) < 1e-6) {
 
3751
            if (n->p.other) {
 
3752
                if (L2(n->p.other->n.pos - n->p.other->pos) < 1e-6)
 
3753
                    yp = n->p.other->origin[Geom::Y] - n->origin[Geom::Y];
 
3754
                    xp = n->p.other->origin[Geom::X] - n->origin[Geom::X];
 
3755
            }
 
3756
        }
 
3757
        if (xp < 0) { xp = -xp; yp = -yp; } // limit the angle to between 0 and pi
 
3758
        if (yp < 0) { xp = -xp; yp = -yp; }
 
3759
 
 
3760
        if (state & GDK_MOD1_MASK && !(xn == 0 && xp == 0)) {
 
3761
            // sliding on handles, only if at least one of the handles is non-vertical
 
3762
            // (otherwise it's the same as ctrl+drag anyway)
 
3763
 
 
3764
            // calculate angles of the handles
 
3765
            if (xn == 0) {
 
3766
                if (yn == 0) { // no handle, consider it the continuation of the other one
 
3767
                    an = 0;
 
3768
                    collinear = TRUE;
 
3769
                }
 
3770
                else an = 0; // vertical; set the angle to horizontal
 
3771
            } else an = yn/xn;
 
3772
 
 
3773
            if (xp == 0) {
 
3774
                if (yp == 0) { // no handle, consider it the continuation of the other one
 
3775
                    ap = an;
 
3776
                }
 
3777
                else ap = 0; // vertical; set the angle to horizontal
 
3778
            } else  ap = yp/xp;
 
3779
 
 
3780
            if (collinear) an = ap;
 
3781
 
 
3782
            // angles of the perpendiculars; HUGE_VAL means vertical
 
3783
            if (an == 0) na = HUGE_VAL; else na = -1/an;
 
3784
            if (ap == 0) pa = HUGE_VAL; else pa = -1/ap;
 
3785
 
 
3786
            // mouse point relative to the node's original pos
 
3787
            pr = p - n->origin;
 
3788
 
 
3789
            // distances to the four lines (two handles and two perpendiculars)
 
3790
            d_an = point_line_distance(&pr, an);
 
3791
            d_na = point_line_distance(&pr, na);
 
3792
            d_ap = point_line_distance(&pr, ap);
 
3793
            d_pa = point_line_distance(&pr, pa);
 
3794
 
 
3795
            // find out which line is the closest, save its closest point in c
 
3796
            if (d_an <= d_na && d_an <= d_ap && d_an <= d_pa) {
 
3797
                point_line_closest(&pr, an, &c);
 
3798
            } else if (d_ap <= d_an && d_ap <= d_na && d_ap <= d_pa) {
 
3799
                point_line_closest(&pr, ap, &c);
 
3800
            } else if (d_na <= d_an && d_na <= d_ap && d_na <= d_pa) {
 
3801
                point_line_closest(&pr, na, &c);
 
3802
            } else if (d_pa <= d_an && d_pa <= d_ap && d_pa <= d_na) {
 
3803
                point_line_closest(&pr, pa, &c);
 
3804
            }
 
3805
 
 
3806
            // move the node to the closest point
 
3807
            sp_nodepath_selected_nodes_move(n->subpath->nodepath,
 
3808
                                            n->origin[Geom::X] + c[Geom::X] - n->pos[Geom::X],
 
3809
                                            n->origin[Geom::Y] + c[Geom::Y] - n->pos[Geom::Y],
 
3810
                                            true);
 
3811
 
 
3812
        } else {  // constraining to hor/vert
 
3813
 
 
3814
            if (fabs(p[Geom::X] - n->origin[Geom::X]) > fabs(p[Geom::Y] - n->origin[Geom::Y])) { // snap to hor
 
3815
                sp_nodepath_selected_nodes_move(n->subpath->nodepath,
 
3816
                                                p[Geom::X] - n->pos[Geom::X],
 
3817
                                                n->origin[Geom::Y] - n->pos[Geom::Y],
 
3818
                                                true,
 
3819
                                                true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::X]));
 
3820
            } else { // snap to vert
 
3821
                sp_nodepath_selected_nodes_move(n->subpath->nodepath,
 
3822
                                                n->origin[Geom::X] - n->pos[Geom::X],
 
3823
                                                p[Geom::Y] - n->pos[Geom::Y],
 
3824
                                                true,
 
3825
                                                true, Inkscape::Snapper::ConstraintLine(component_vectors[Geom::Y]));
 
3826
            }
 
3827
        }
 
3828
    } else { // move freely
 
3829
        if (n->is_dragging) {
 
3830
            if (state & GDK_MOD1_MASK) { // sculpt
 
3831
                sp_nodepath_selected_nodes_sculpt(n->subpath->nodepath, n, p - n->origin);
 
3832
            } else {
 
3833
                sp_nodepath_selected_nodes_move(n->subpath->nodepath,
 
3834
                                            p[Geom::X] - n->pos[Geom::X],
 
3835
                                            p[Geom::Y] - n->pos[Geom::Y],
 
3836
                                            (state & GDK_SHIFT_MASK) == 0);
 
3837
            }
 
3838
        }
 
3839
    }
 
3840
 
 
3841
    n->subpath->nodepath->desktop->scroll_to_point(p);
 
3842
 
 
3843
    return TRUE;
 
3844
}
 
3845
 
 
3846
/**
 
3847
 * Node handle clicked callback.
 
3848
 */
 
3849
static void node_handle_clicked(SPKnot *knot, guint state, gpointer data)
 
3850
{
 
3851
   Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
 
3852
 
 
3853
    if (state & GDK_CONTROL_MASK) { // "delete" handle
 
3854
        if (n->p.knot == knot) {
 
3855
            n->p.pos = n->pos;
 
3856
        } else if (n->n.knot == knot) {
 
3857
            n->n.pos = n->pos;
 
3858
        }
 
3859
        sp_node_update_handles(n);
 
3860
        Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
 
3861
        sp_nodepath_update_repr(nodepath, _("Retract handle"));
 
3862
        sp_nodepath_update_statusbar(nodepath);
 
3863
 
 
3864
    } else { // just select or add to selection, depending in Shift
 
3865
        sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
 
3866
    }
 
3867
}
 
3868
 
 
3869
/**
 
3870
 * Node handle grabbed callback.
 
3871
 */
 
3872
static void node_handle_grabbed(SPKnot *knot, guint state, gpointer data)
 
3873
{
 
3874
   Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
 
3875
 
 
3876
    // convert auto -> smooth when dragging handle
 
3877
   if (n->type == Inkscape::NodePath::NODE_AUTO) {
 
3878
        n->type = Inkscape::NodePath::NODE_SMOOTH;
 
3879
        sp_nodepath_update_node_knot (n);
 
3880
   }
 
3881
 
 
3882
    if (!n->selected) {
 
3883
        sp_nodepath_node_select(n, (state & GDK_SHIFT_MASK), FALSE);
 
3884
    }
 
3885
 
 
3886
    // remember the origin point of the handle
 
3887
    if (n->p.knot == knot) {
 
3888
        n->p.origin_radial = n->p.pos - n->pos;
 
3889
    } else if (n->n.knot == knot) {
 
3890
        n->n.origin_radial = n->n.pos - n->pos;
 
3891
    } else {
 
3892
        g_assert_not_reached();
 
3893
    }
 
3894
 
 
3895
    sp_canvas_force_full_redraw_after_interruptions(n->subpath->nodepath->desktop->canvas, 5);
 
3896
}
 
3897
 
 
3898
/**
 
3899
 * Node handle ungrabbed callback.
 
3900
 */
 
3901
static void node_handle_ungrabbed(SPKnot *knot, guint state, gpointer data)
 
3902
{
 
3903
   Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
 
3904
 
 
3905
    // forget origin and set knot position once more (because it can be wrong now due to restrictions)
 
3906
    if (n->p.knot == knot) {
 
3907
        n->p.origin_radial.a = 0;
 
3908
        sp_knot_set_position(knot, n->p.pos, state);
 
3909
    } else if (n->n.knot == knot) {
 
3910
        n->n.origin_radial.a = 0;
 
3911
        sp_knot_set_position(knot, n->n.pos, state);
 
3912
    } else {
 
3913
        g_assert_not_reached();
 
3914
    }
 
3915
 
 
3916
    sp_nodepath_update_repr(n->subpath->nodepath, _("Move node handle"));
 
3917
}
 
3918
 
 
3919
/**
 
3920
 * Node handle "request" signal callback.
 
3921
 */
 
3922
static gboolean node_handle_request(SPKnot *knot, Geom::Point &p, guint state, gpointer data)
 
3923
{
 
3924
    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
 
3925
 
 
3926
    Inkscape::NodePath::NodeSide *me, *opposite;
 
3927
    gint which;
 
3928
    if (n->p.knot == knot) {
 
3929
        me = &n->p;
 
3930
        opposite = &n->n;
 
3931
        which = -1;
 
3932
    } else if (n->n.knot == knot) {
 
3933
        me = &n->n;
 
3934
        opposite = &n->p;
 
3935
        which = 1;
 
3936
    } else {
 
3937
        me = opposite = NULL;
 
3938
        which = 0;
 
3939
        g_assert_not_reached();
 
3940
    }
 
3941
 
 
3942
    SPDesktop *desktop = n->subpath->nodepath->desktop;
 
3943
    SnapManager &m = desktop->namedview->snap_manager;
 
3944
    m.setup(desktop, true, n->subpath->nodepath->item);
 
3945
    Inkscape::SnappedPoint s;
 
3946
 
 
3947
    if ((state & GDK_SHIFT_MASK) != 0) {
 
3948
        // We will not try to snap when the shift-key is pressed
 
3949
        // so remove the old snap indicator and don't wait for it to time-out
 
3950
        desktop->snapindicator->remove_snaptarget();
 
3951
    }
 
3952
 
 
3953
    Inkscape::NodePath::Node *othernode = opposite->other;
 
3954
    Inkscape::SnapSourceType source_type = (n->type == Inkscape::NodePath::NODE_SMOOTH ? Inkscape::SNAPSOURCE_NODE_SMOOTH : Inkscape::SNAPSOURCE_NODE_CUSP);
 
3955
    if (othernode) {
 
3956
        if ((n->type != Inkscape::NodePath::NODE_CUSP) && sp_node_side_is_line(n, opposite)) {
 
3957
            /* We are smooth node adjacent with line */
 
3958
            Geom::Point const delta = p - n->pos;
 
3959
            Geom::Coord const len = Geom::L2(delta);
 
3960
            Inkscape::NodePath::Node *othernode = opposite->other;
 
3961
            Geom::Point const ndelta = n->pos - othernode->pos;
 
3962
            Geom::Coord const linelen = Geom::L2(ndelta);
 
3963
            if (len > NR_EPSILON && linelen > NR_EPSILON) {
 
3964
                Geom::Coord const scal = dot(delta, ndelta) / linelen;
 
3965
                p = n->pos + (scal / linelen) * ndelta;
 
3966
            }
 
3967
            if ((state & GDK_SHIFT_MASK) == 0) {
 
3968
                s = m.constrainedSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type, Inkscape::Snapper::ConstraintLine(p, ndelta), false);
 
3969
            }
 
3970
        } else {
 
3971
            if ((state & GDK_SHIFT_MASK) == 0) {
 
3972
                s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
 
3973
            }
 
3974
        }
 
3975
    } else {
 
3976
        if ((state & GDK_SHIFT_MASK) == 0) {
 
3977
            s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, p, source_type);
 
3978
        }
 
3979
    }
 
3980
 
 
3981
    s.getPoint(p);
 
3982
 
 
3983
    sp_node_adjust_handle(n, -which);
 
3984
 
 
3985
    return FALSE;
 
3986
}
 
3987
 
 
3988
/**
 
3989
 * Node handle moved callback.
 
3990
 */
 
3991
static void node_handle_moved(SPKnot *knot, Geom::Point const &p, guint state, gpointer data)
 
3992
{
 
3993
   Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) data;
 
3994
   Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
3995
 
 
3996
   Inkscape::NodePath::NodeSide *me;
 
3997
   Inkscape::NodePath::NodeSide *other;
 
3998
    if (n->p.knot == knot) {
 
3999
        me = &n->p;
 
4000
        other = &n->n;
 
4001
    } else if (n->n.knot == knot) {
 
4002
        me = &n->n;
 
4003
        other = &n->p;
 
4004
    } else {
 
4005
        me = NULL;
 
4006
        other = NULL;
 
4007
        g_assert_not_reached();
 
4008
    }
 
4009
 
 
4010
    // calculate radial coordinates of the grabbed handle, its other handle, and the mouse point
 
4011
    Radial rme(me->pos - n->pos);
 
4012
    Radial rother(other->pos - n->pos);
 
4013
    Radial rnew(p - n->pos);
 
4014
 
 
4015
    if (state & GDK_CONTROL_MASK && rnew.a != HUGE_VAL) {
 
4016
        int const snaps = prefs->getInt("/options/rotationsnapsperpi/value", 12);
 
4017
        /* 0 interpreted as "no snapping". */
 
4018
 
 
4019
        // 1. Snap to the closest PI/snaps angle, starting from zero.
 
4020
        double a_snapped = floor(rnew.a/(M_PI/snaps) + 0.5) * (M_PI/snaps);
 
4021
 
 
4022
        // 2. Snap to the original angle, its opposite and perpendiculars
 
4023
        if (me->origin_radial.a != HUGE_VAL) { // otherwise ortho doesn't exist: original handle was zero length
 
4024
            /* The closest PI/2 angle, starting from original angle */
 
4025
            double const a_ortho = me->origin_radial.a + floor((rnew.a - me->origin_radial.a)/(M_PI/2) + 0.5) * (M_PI/2);
 
4026
 
 
4027
            // Snap to the closest.
 
4028
            a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_ortho - rnew.a)
 
4029
                       ? a_snapped
 
4030
                       : a_ortho );
 
4031
        }
 
4032
 
 
4033
        // 3. Snap to the angle of the opposite line, if any
 
4034
        Inkscape::NodePath::Node *othernode = other->other;
 
4035
        if (othernode) {
 
4036
            Geom::Point other_to_snap(0,0);
 
4037
            if (sp_node_side_is_line(n, other)) {
 
4038
                other_to_snap = othernode->pos - n->pos;
 
4039
            } else {
 
4040
                other_to_snap = other->pos - n->pos;
 
4041
            }
 
4042
            if (Geom::L2(other_to_snap) > 1e-3) {
 
4043
                Radial rother_to_snap(other_to_snap);
 
4044
                /* The closest PI/2 angle, starting from the angle of the opposite line segment */
 
4045
                double const a_oppo = rother_to_snap.a + floor((rnew.a - rother_to_snap.a)/(M_PI/2) + 0.5) * (M_PI/2);
 
4046
 
 
4047
                // Snap to the closest.
 
4048
                a_snapped = ( fabs(a_snapped - rnew.a) < fabs(a_oppo - rnew.a)
 
4049
                       ? a_snapped
 
4050
                       : a_oppo );
 
4051
            }
 
4052
        }
 
4053
 
 
4054
        rnew.a = a_snapped;
 
4055
    }
 
4056
 
 
4057
    if (state & GDK_MOD1_MASK) {
 
4058
        // lock handle length
 
4059
        rnew.r = me->origin_radial.r;
 
4060
    }
 
4061
 
 
4062
    if (( n->type !=Inkscape::NodePath::NODE_CUSP || (!n->dragging_out && (state & GDK_SHIFT_MASK)))
 
4063
        && (rme.a != HUGE_VAL) && (rnew.a != HUGE_VAL) && ((fabs(rme.a - rnew.a) > 0.001) || (n->type ==Inkscape::NodePath::NODE_SYMM))) {
 
4064
        // rotate the other handle correspondingly, if both old and new angles exist and are not the same
 
4065
        rother.a += rnew.a - rme.a;
 
4066
        other->pos = Geom::Point(rother) + n->pos;
 
4067
        if (other->knot) {
 
4068
            sp_ctrlline_set_coords(SP_CTRLLINE(other->line), n->pos, other->pos);
 
4069
            sp_knot_moveto(other->knot, other->pos);
 
4070
        }
 
4071
    }
 
4072
 
 
4073
    me->pos = Geom::Point(rnew) + n->pos;
 
4074
    sp_ctrlline_set_coords(SP_CTRLLINE(me->line), n->pos, me->pos);
 
4075
 
 
4076
    // move knot, but without emitting the signal:
 
4077
    // we cannot emit a "moved" signal because we're now processing it
 
4078
    sp_knot_moveto(me->knot, me->pos);
 
4079
 
 
4080
    update_object(n->subpath->nodepath);
 
4081
 
 
4082
    /* status text */
 
4083
    SPDesktop *desktop = n->subpath->nodepath->desktop;
 
4084
    if (!desktop) return;
 
4085
    SPEventContext *ec = desktop->event_context;
 
4086
    if (!ec) return;
 
4087
 
 
4088
    Inkscape::MessageContext *mc = get_message_context(ec);
 
4089
 
 
4090
    if (!mc) return;
 
4091
 
 
4092
    double degrees = 180 / M_PI * rnew.a;
 
4093
    if (degrees > 180) degrees -= 360;
 
4094
    if (degrees < -180) degrees += 360;
 
4095
    if (prefs->getBool("/options/compassangledisplay/value"))
 
4096
        degrees = angle_to_compass (degrees);
 
4097
 
 
4098
    GString *length = SP_PX_TO_METRIC_STRING(rnew.r, desktop->namedview->getDefaultMetric());
 
4099
 
 
4100
    mc->setF(Inkscape::IMMEDIATE_MESSAGE,
 
4101
         _("<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);
 
4102
 
 
4103
    g_string_free(length, TRUE);
 
4104
}
 
4105
 
 
4106
/**
 
4107
 * Node handle event callback.
 
4108
 */
 
4109
static gboolean node_handle_event(SPKnot *knot, GdkEvent *event,Inkscape::NodePath::Node *n)
 
4110
{
 
4111
    gboolean ret = FALSE;
 
4112
    switch (event->type) {
 
4113
        case GDK_KEY_PRESS:
 
4114
            switch (get_group0_keyval (&event->key)) {
 
4115
                case GDK_space:
 
4116
                    if (event->key.state & GDK_BUTTON1_MASK) {
 
4117
                        Inkscape::NodePath::Path *nodepath = n->subpath->nodepath;
 
4118
                        stamp_repr(nodepath);
 
4119
                        ret = TRUE;
 
4120
                    }
 
4121
                    break;
 
4122
                default:
 
4123
                    break;
 
4124
            }
 
4125
            break;
 
4126
        case GDK_ENTER_NOTIFY:
 
4127
            // we use an experimentally determined threshold that seems to work fine
 
4128
            if (Geom::L2(n->pos - knot->pos) < 0.75)
 
4129
                Inkscape::NodePath::Path::active_node = n;
 
4130
            break;
 
4131
        case GDK_LEAVE_NOTIFY:
 
4132
            // we use an experimentally determined threshold that seems to work fine
 
4133
            if (Geom::L2(n->pos - knot->pos) < 0.75)
 
4134
                Inkscape::NodePath::Path::active_node = NULL;
 
4135
            break;
 
4136
        default:
 
4137
            break;
 
4138
    }
 
4139
 
 
4140
    return ret;
 
4141
}
 
4142
 
 
4143
static void node_rotate_one_internal(Inkscape::NodePath::Node const &n, gdouble const angle,
 
4144
                                 Radial &rme, Radial &rother, gboolean const both)
 
4145
{
 
4146
    rme.a += angle;
 
4147
    if ( both
 
4148
         || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
 
4149
         || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
 
4150
    {
 
4151
        rother.a += angle;
 
4152
    }
 
4153
}
 
4154
 
 
4155
static void node_rotate_one_internal_screen(Inkscape::NodePath::Node const &n, gdouble const angle,
 
4156
                                        Radial &rme, Radial &rother, gboolean const both)
 
4157
{
 
4158
    gdouble const norm_angle = angle / n.subpath->nodepath->desktop->current_zoom();
 
4159
 
 
4160
    gdouble r;
 
4161
    if ( both
 
4162
         || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
 
4163
         || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
 
4164
    {
 
4165
        r = MAX(rme.r, rother.r);
 
4166
    } else {
 
4167
        r = rme.r;
 
4168
    }
 
4169
 
 
4170
    gdouble const weird_angle = atan2(norm_angle, r);
 
4171
/* Bulia says norm_angle is just the visible distance that the
 
4172
 * object's end must travel on the screen.  Left as 'angle' for want of
 
4173
 * a better name.*/
 
4174
 
 
4175
    rme.a += weird_angle;
 
4176
    if ( both
 
4177
         || ( n.type == Inkscape::NodePath::NODE_SMOOTH )
 
4178
         || ( n.type == Inkscape::NodePath::NODE_SYMM )  )
 
4179
    {
 
4180
        rother.a += weird_angle;
 
4181
    }
 
4182
}
 
4183
 
 
4184
/**
 
4185
 * Rotate one node.
 
4186
 */
 
4187
static void node_rotate_one (Inkscape::NodePath::Node *n, gdouble angle, int which, gboolean screen)
 
4188
{
 
4189
    Inkscape::NodePath::NodeSide *me, *other;
 
4190
    bool both = false;
 
4191
 
 
4192
    double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
 
4193
    double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
 
4194
 
 
4195
    if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
 
4196
        me = &(n->p);
 
4197
        other = &(n->n);
 
4198
    } else if (!n->p.other) {
 
4199
        me = &(n->n);
 
4200
        other = &(n->p);
 
4201
    } else {
 
4202
        if (which > 0) { // right handle
 
4203
            if (xn > xp) {
 
4204
                me = &(n->n);
 
4205
                other = &(n->p);
 
4206
            } else {
 
4207
                me = &(n->p);
 
4208
                other = &(n->n);
 
4209
            }
 
4210
        } else if (which < 0){ // left handle
 
4211
            if (xn <= xp) {
 
4212
                me = &(n->n);
 
4213
                other = &(n->p);
 
4214
            } else {
 
4215
                me = &(n->p);
 
4216
                other = &(n->n);
 
4217
            }
 
4218
        } else { // both handles
 
4219
            me = &(n->n);
 
4220
            other = &(n->p);
 
4221
            both = true;
 
4222
        }
 
4223
    }
 
4224
 
 
4225
    Radial rme(me->pos - n->pos);
 
4226
    Radial rother(other->pos - n->pos);
 
4227
 
 
4228
    if (screen) {
 
4229
        node_rotate_one_internal_screen (*n, angle, rme, rother, both);
 
4230
    } else {
 
4231
        node_rotate_one_internal (*n, angle, rme, rother, both);
 
4232
    }
 
4233
 
 
4234
    me->pos = n->pos + Geom::Point(rme);
 
4235
 
 
4236
    if (both || n->type == Inkscape::NodePath::NODE_SMOOTH || n->type == Inkscape::NodePath::NODE_SYMM) {
 
4237
        other->pos =  n->pos + Geom::Point(rother);
 
4238
    }
 
4239
 
 
4240
    // this function is only called from sp_nodepath_selected_nodes_rotate that will update display at the end,
 
4241
    // so here we just move all the knots without emitting move signals, for speed
 
4242
    sp_node_update_handles(n, false);
 
4243
}
 
4244
 
 
4245
/**
 
4246
 * Rotate selected nodes.
 
4247
 */
 
4248
void sp_nodepath_selected_nodes_rotate(Inkscape::NodePath::Path *nodepath, gdouble angle, int which, bool screen)
 
4249
{
 
4250
    if (!nodepath || !nodepath->selected) return;
 
4251
 
 
4252
    if (g_list_length(nodepath->selected) == 1) {
 
4253
       Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
 
4254
        node_rotate_one (n, angle, which, screen);
 
4255
    } else {
 
4256
       // rotate as an object:
 
4257
 
 
4258
        Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
 
4259
        Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
 
4260
        for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
4261
            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
4262
            box.expandTo (n->pos); // contain all selected nodes
 
4263
        }
 
4264
 
 
4265
        gdouble rot;
 
4266
        if (screen) {
 
4267
            gdouble const zoom = nodepath->desktop->current_zoom();
 
4268
            gdouble const zmove = angle / zoom;
 
4269
            gdouble const r = Geom::L2(box.max() - box.midpoint());
 
4270
            rot = atan2(zmove, r);
 
4271
        } else {
 
4272
            rot = angle;
 
4273
        }
 
4274
 
 
4275
        Geom::Point rot_center;
 
4276
        if (Inkscape::NodePath::Path::active_node == NULL)
 
4277
            rot_center = box.midpoint();
 
4278
        else
 
4279
            rot_center = Inkscape::NodePath::Path::active_node->pos;
 
4280
 
 
4281
        Geom::Matrix t =
 
4282
            Geom::Matrix (Geom::Translate(-rot_center)) *
 
4283
            Geom::Matrix (Geom::Rotate(rot)) *
 
4284
            Geom::Matrix (Geom::Translate(rot_center));
 
4285
 
 
4286
        for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
4287
            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
4288
            n->pos *= t;
 
4289
            n->n.pos *= t;
 
4290
            n->p.pos *= t;
 
4291
            sp_node_update_handles(n, false);
 
4292
        }
 
4293
    }
 
4294
 
 
4295
    sp_nodepath_update_repr_keyed(nodepath, angle > 0 ? "nodes:rot:p" : "nodes:rot:n", _("Rotate nodes"));
 
4296
}
 
4297
 
 
4298
/**
 
4299
 * Scale one node.
 
4300
 */
 
4301
static void node_scale_one (Inkscape::NodePath::Node *n, gdouble grow, int which)
 
4302
{
 
4303
    bool both = false;
 
4304
    Inkscape::NodePath::NodeSide *me, *other;
 
4305
 
 
4306
    double xn = n->n.other? n->n.other->pos[Geom::X] : n->pos[Geom::X];
 
4307
    double xp = n->p.other? n->p.other->pos[Geom::X] : n->pos[Geom::X];
 
4308
 
 
4309
    if (!n->n.other) { // if this is an endnode, select its single handle regardless of "which"
 
4310
        me = &(n->p);
 
4311
        other = &(n->n);
 
4312
        n->code = NR_CURVETO;
 
4313
    } else if (!n->p.other) {
 
4314
        me = &(n->n);
 
4315
        other = &(n->p);
 
4316
        if (n->n.other)
 
4317
            n->n.other->code = NR_CURVETO;
 
4318
    } else {
 
4319
        if (which > 0) { // right handle
 
4320
            if (xn > xp) {
 
4321
                me = &(n->n);
 
4322
                other = &(n->p);
 
4323
                if (n->n.other)
 
4324
                    n->n.other->code = NR_CURVETO;
 
4325
            } else {
 
4326
                me = &(n->p);
 
4327
                other = &(n->n);
 
4328
                n->code = NR_CURVETO;
 
4329
            }
 
4330
        } else if (which < 0){ // left handle
 
4331
            if (xn <= xp) {
 
4332
                me = &(n->n);
 
4333
                other = &(n->p);
 
4334
                if (n->n.other)
 
4335
                    n->n.other->code = NR_CURVETO;
 
4336
            } else {
 
4337
                me = &(n->p);
 
4338
                other = &(n->n);
 
4339
                n->code = NR_CURVETO;
 
4340
            }
 
4341
        } else { // both handles
 
4342
            me = &(n->n);
 
4343
            other = &(n->p);
 
4344
            both = true;
 
4345
            n->code = NR_CURVETO;
 
4346
            if (n->n.other)
 
4347
                n->n.other->code = NR_CURVETO;
 
4348
        }
 
4349
    }
 
4350
 
 
4351
    Radial rme(me->pos - n->pos);
 
4352
    Radial rother(other->pos - n->pos);
 
4353
 
 
4354
    rme.r += grow;
 
4355
    if (rme.r < 0) rme.r = 0;
 
4356
    if (rme.a == HUGE_VAL) {
 
4357
        if (me->other) { // if direction is unknown, initialize it towards the next node
 
4358
            Radial rme_next(me->other->pos - n->pos);
 
4359
            rme.a = rme_next.a;
 
4360
        } else { // if there's no next, initialize to 0
 
4361
            rme.a = 0;
 
4362
        }
 
4363
    }
 
4364
    if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
 
4365
        rother.r += grow;
 
4366
        if (rother.r < 0) rother.r = 0;
 
4367
        if (rother.a == HUGE_VAL) {
 
4368
            rother.a = rme.a + M_PI;
 
4369
        }
 
4370
    }
 
4371
 
 
4372
    me->pos = n->pos + Geom::Point(rme);
 
4373
 
 
4374
    if (both || n->type == Inkscape::NodePath::NODE_SYMM) {
 
4375
        other->pos = n->pos + Geom::Point(rother);
 
4376
    }
 
4377
 
 
4378
    // this function is only called from sp_nodepath_selected_nodes_scale that will update display at the end,
 
4379
    // so here we just move all the knots without emitting move signals, for speed
 
4380
    sp_node_update_handles(n, false);
 
4381
}
 
4382
 
 
4383
/**
 
4384
 * Scale selected nodes.
 
4385
 */
 
4386
void sp_nodepath_selected_nodes_scale(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
 
4387
{
 
4388
    if (!nodepath || !nodepath->selected) return;
 
4389
 
 
4390
    if (g_list_length(nodepath->selected) == 1) {
 
4391
        // scale handles of the single selected node
 
4392
        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
 
4393
        node_scale_one (n, grow, which);
 
4394
    } else {
 
4395
        // scale nodes as an "object":
 
4396
 
 
4397
        Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
 
4398
        Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
 
4399
        for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
4400
            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
4401
            box.expandTo (n->pos); // contain all selected nodes
 
4402
        }
 
4403
 
 
4404
        if ( Geom::are_near(box.maxExtent(), 0) ) {
 
4405
            SPEventContext *ec = nodepath->desktop->event_context;
 
4406
            if (!ec) return;
 
4407
            Inkscape::MessageContext *mc = get_message_context(ec);
 
4408
            if (!mc) return;
 
4409
            mc->setF(Inkscape::WARNING_MESSAGE,
 
4410
                             _("Cannot scale nodes when all are at the same location."));
 
4411
            return;
 
4412
        }
 
4413
        double scale = (box.maxExtent() + grow)/box.maxExtent();
 
4414
 
 
4415
 
 
4416
        Geom::Point scale_center;
 
4417
        if (Inkscape::NodePath::Path::active_node == NULL)
 
4418
            scale_center = box.midpoint();
 
4419
        else
 
4420
            scale_center = Inkscape::NodePath::Path::active_node->pos;
 
4421
 
 
4422
        Geom::Matrix t =
 
4423
            Geom::Translate(-scale_center) *
 
4424
            Geom::Scale(scale, scale) *
 
4425
            Geom::Translate(scale_center);
 
4426
 
 
4427
        for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
4428
            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
4429
            n->pos *= t;
 
4430
            n->n.pos *= t;
 
4431
            n->p.pos *= t;
 
4432
            sp_node_update_handles(n, false);
 
4433
        }
 
4434
    }
 
4435
 
 
4436
    sp_nodepath_update_repr_keyed(nodepath, grow > 0 ? "nodes:scale:p" : "nodes:scale:n", _("Scale nodes"));
 
4437
}
 
4438
 
 
4439
void sp_nodepath_selected_nodes_scale_screen(Inkscape::NodePath::Path *nodepath, gdouble const grow, int const which)
 
4440
{
 
4441
    if (!nodepath) return;
 
4442
    sp_nodepath_selected_nodes_scale(nodepath, grow / nodepath->desktop->current_zoom(), which);
 
4443
}
 
4444
 
 
4445
/**
 
4446
 * Flip selected nodes horizontally/vertically.
 
4447
 */
 
4448
void sp_nodepath_flip (Inkscape::NodePath::Path *nodepath, Geom::Dim2 axis, boost::optional<Geom::Point> center)
 
4449
{
 
4450
    if (!nodepath || !nodepath->selected) return;
 
4451
 
 
4452
    if (g_list_length(nodepath->selected) == 1 && !center) {
 
4453
        // flip handles of the single selected node
 
4454
        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) nodepath->selected->data;
 
4455
        double temp = n->p.pos[axis];
 
4456
        n->p.pos[axis] = n->n.pos[axis];
 
4457
        n->n.pos[axis] = temp;
 
4458
        sp_node_update_handles(n, false);
 
4459
    } else {
 
4460
        // scale nodes as an "object":
 
4461
 
 
4462
        Geom::Rect box = sp_node_selected_bbox (nodepath);
 
4463
        if (!center) {
 
4464
            center = box.midpoint();
 
4465
        }
 
4466
        Geom::Matrix t =
 
4467
            Geom::Matrix (Geom::Translate(- *center)) *
 
4468
            Geom::Matrix ((axis == Geom::X)? Geom::Scale(-1, 1) : Geom::Scale(1, -1)) *
 
4469
            Geom::Matrix (Geom::Translate(*center));
 
4470
 
 
4471
        for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
4472
            Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
4473
            n->pos *= t;
 
4474
            n->n.pos *= t;
 
4475
            n->p.pos *= t;
 
4476
            sp_node_update_handles(n, false);
 
4477
        }
 
4478
    }
 
4479
 
 
4480
    sp_nodepath_update_repr(nodepath, _("Flip nodes"));
 
4481
}
 
4482
 
 
4483
Geom::Rect sp_node_selected_bbox (Inkscape::NodePath::Path *nodepath)
 
4484
{
 
4485
    g_assert (nodepath->selected);
 
4486
 
 
4487
    Inkscape::NodePath::Node *n0 = (Inkscape::NodePath::Node *) nodepath->selected->data;
 
4488
    Geom::Rect box (n0->pos, n0->pos); // originally includes the first selected node
 
4489
    for (GList *l = nodepath->selected; l != NULL; l = l->next) {
 
4490
        Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node *) l->data;
 
4491
        box.expandTo (n->pos); // contain all selected nodes
 
4492
    }
 
4493
    return box;
 
4494
}
 
4495
 
 
4496
//-----------------------------------------------
 
4497
/**
 
4498
 * Return new subpath under given nodepath.
 
4499
 */
 
4500
static Inkscape::NodePath::SubPath *sp_nodepath_subpath_new(Inkscape::NodePath::Path *nodepath)
 
4501
{
 
4502
    g_assert(nodepath);
 
4503
    g_assert(nodepath->desktop);
 
4504
 
 
4505
   Inkscape::NodePath::SubPath *s = g_new(Inkscape::NodePath::SubPath, 1);
 
4506
 
 
4507
    s->nodepath = nodepath;
 
4508
    s->closed = FALSE;
 
4509
    s->nodes = NULL;
 
4510
    s->first = NULL;
 
4511
    s->last = NULL;
 
4512
 
 
4513
    // using prepend here saves up to 10% of time on paths with many subpaths, but requires that
 
4514
    // the caller reverses the list after it's ready (this is done in sp_nodepath_new)
 
4515
    nodepath->subpaths = g_list_prepend (nodepath->subpaths, s);
 
4516
 
 
4517
    return s;
 
4518
}
 
4519
 
 
4520
/**
 
4521
 * Destroy nodes in subpath, then subpath itself.
 
4522
 */
 
4523
static void sp_nodepath_subpath_destroy(Inkscape::NodePath::SubPath *subpath)
 
4524
{
 
4525
    g_assert(subpath);
 
4526
    g_assert(subpath->nodepath);
 
4527
    g_assert(g_list_find(subpath->nodepath->subpaths, subpath));
 
4528
 
 
4529
    while (subpath->nodes) {
 
4530
        sp_nodepath_node_destroy((Inkscape::NodePath::Node *) subpath->nodes->data);
 
4531
    }
 
4532
 
 
4533
    subpath->nodepath->subpaths = g_list_remove(subpath->nodepath->subpaths, subpath);
 
4534
 
 
4535
    g_free(subpath);
 
4536
}
 
4537
 
 
4538
/**
 
4539
 * Link head to tail in subpath.
 
4540
 */
 
4541
static void sp_nodepath_subpath_close(Inkscape::NodePath::SubPath *sp)
 
4542
{
 
4543
    g_assert(!sp->closed);
 
4544
    g_assert(sp->last != sp->first);
 
4545
    g_assert(sp->first->code == NR_MOVETO);
 
4546
 
 
4547
    sp->closed = TRUE;
 
4548
 
 
4549
    //Link the head to the tail
 
4550
    sp->first->p.other = sp->last;
 
4551
    sp->last->n.other  = sp->first;
 
4552
    sp->last->n.pos    = sp->last->pos + (sp->first->n.pos - sp->first->pos);
 
4553
    sp->first          = sp->last;
 
4554
 
 
4555
    //Remove the extra end node
 
4556
    sp_nodepath_node_destroy(sp->last->n.other);
 
4557
}
 
4558
 
 
4559
/**
 
4560
 * Open closed (loopy) subpath at node.
 
4561
 */
 
4562
static void sp_nodepath_subpath_open(Inkscape::NodePath::SubPath *sp,Inkscape::NodePath::Node *n)
 
4563
{
 
4564
    g_assert(sp->closed);
 
4565
    g_assert(n->subpath == sp);
 
4566
    g_assert(sp->first == sp->last);
 
4567
 
 
4568
    /* We create new startpoint, current node will become last one */
 
4569
 
 
4570
   Inkscape::NodePath::Node *new_path = sp_nodepath_node_new(sp, n->n.other,Inkscape::NodePath::NODE_CUSP, NR_MOVETO,
 
4571
                                                &n->pos, &n->pos, &n->n.pos);
 
4572
 
 
4573
 
 
4574
    sp->closed        = FALSE;
 
4575
 
 
4576
    //Unlink to make a head and tail
 
4577
    sp->first         = new_path;
 
4578
    sp->last          = n;
 
4579
    n->n.other        = NULL;
 
4580
    new_path->p.other = NULL;
 
4581
}
 
4582
 
 
4583
/**
 
4584
 * Return new node in subpath with given properties.
 
4585
 * \param pos Position of node.
 
4586
 * \param ppos Handle position in previous direction
 
4587
 * \param npos Handle position in previous direction
 
4588
 */
 
4589
Inkscape::NodePath::Node *
 
4590
sp_nodepath_node_new(Inkscape::NodePath::SubPath *sp, Inkscape::NodePath::Node *next, Inkscape::NodePath::NodeType type, NRPathcode code, Geom::Point *ppos, Geom::Point *pos, Geom::Point *npos)
 
4591
{
 
4592
    g_assert(sp);
 
4593
    g_assert(sp->nodepath);
 
4594
    g_assert(sp->nodepath->desktop);
 
4595
 
 
4596
    if (nodechunk == NULL)
 
4597
        nodechunk = g_mem_chunk_create(Inkscape::NodePath::Node, 32, G_ALLOC_AND_FREE);
 
4598
 
 
4599
    Inkscape::NodePath::Node *n = (Inkscape::NodePath::Node*)g_mem_chunk_alloc(nodechunk);
 
4600
 
 
4601
    n->subpath  = sp;
 
4602
 
 
4603
    if (type != Inkscape::NodePath::NODE_NONE) {
 
4604
        // use the type from sodipodi:nodetypes
 
4605
        n->type = type;
 
4606
    } else {
 
4607
        if (fabs (Inkscape::Util::triangle_area (*pos, *ppos, *npos)) < 1e-2) {
 
4608
            // points are (almost) collinear
 
4609
            if (Geom::L2(*pos - *ppos) < 1e-6 || Geom::L2(*pos - *npos) < 1e-6) {
 
4610
                // endnode, or a node with a retracted handle
 
4611
                n->type = Inkscape::NodePath::NODE_CUSP;
 
4612
            } else {
 
4613
                n->type = Inkscape::NodePath::NODE_SMOOTH;
 
4614
            }
 
4615
        } else {
 
4616
            n->type = Inkscape::NodePath::NODE_CUSP;
 
4617
        }
 
4618
    }
 
4619
 
 
4620
    n->code     = code;
 
4621
    n->selected = FALSE;
 
4622
    n->pos      = *pos;
 
4623
    n->p.pos    = *ppos;
 
4624
    n->n.pos    = *npos;
 
4625
 
 
4626
    n->dragging_out = NULL;
 
4627
 
 
4628
    Inkscape::NodePath::Node *prev;
 
4629
    if (next) {
 
4630
        //g_assert(g_list_find(sp->nodes, next));
 
4631
        prev = next->p.other;
 
4632
    } else {
 
4633
        prev = sp->last;
 
4634
    }
 
4635
 
 
4636
    if (prev)
 
4637
        prev->n.other = n;
 
4638
    else
 
4639
        sp->first = n;
 
4640
 
 
4641
    if (next)
 
4642
        next->p.other = n;
 
4643
    else
 
4644
        sp->last = n;
 
4645
 
 
4646
    n->p.other = prev;
 
4647
    n->n.other = next;
 
4648
 
 
4649
    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"));
 
4650
    sp_knot_set_position(n->knot, *pos, 0);
 
4651
 
 
4652
    n->knot->setAnchor (GTK_ANCHOR_CENTER);
 
4653
    n->knot->setFill(NODE_FILL, NODE_FILL_HI, NODE_FILL_HI);
 
4654
    n->knot->setStroke(NODE_STROKE, NODE_STROKE_HI, NODE_STROKE_HI);
 
4655
 
 
4656
    sp_nodepath_update_node_knot(n);
 
4657
 
 
4658
    g_signal_connect(G_OBJECT(n->knot), "event", G_CALLBACK(node_event), n);
 
4659
    g_signal_connect(G_OBJECT(n->knot), "clicked", G_CALLBACK(node_clicked), n);
 
4660
    g_signal_connect(G_OBJECT(n->knot), "grabbed", G_CALLBACK(node_grabbed), n);
 
4661
    g_signal_connect(G_OBJECT(n->knot), "ungrabbed", G_CALLBACK(node_ungrabbed), n);
 
4662
    g_signal_connect(G_OBJECT(n->knot), "request", G_CALLBACK(node_request), n);
 
4663
    sp_knot_show(n->knot);
 
4664
 
 
4665
    // We only create handle knots and lines on demand
 
4666
    n->p.knot = NULL;
 
4667
    n->p.line = NULL;
 
4668
    n->n.knot = NULL;
 
4669
    n->n.line = NULL;
 
4670
 
 
4671
    sp->nodes = g_list_prepend(sp->nodes, n);
 
4672
 
 
4673
    return n;
 
4674
}
 
4675
 
 
4676
/**
 
4677
 * Destroy node and its knots, link neighbors in subpath.
 
4678
 */
 
4679
static void sp_nodepath_node_destroy(Inkscape::NodePath::Node *node)
 
4680
{
 
4681
    g_assert(node);
 
4682
    g_assert(node->subpath);
 
4683
    g_assert(SP_IS_KNOT(node->knot));
 
4684
 
 
4685
   Inkscape::NodePath::SubPath *sp = node->subpath;
 
4686
 
 
4687
    if (node->selected) { // first, deselect
 
4688
        g_assert(g_list_find(node->subpath->nodepath->selected, node));
 
4689
        node->subpath->nodepath->selected = g_list_remove(node->subpath->nodepath->selected, node);
 
4690
    }
 
4691
 
 
4692
    node->subpath->nodes = g_list_remove(node->subpath->nodes, node);
 
4693
 
 
4694
    g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_event), node);
 
4695
    g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_clicked), node);
 
4696
    g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_grabbed), node);
 
4697
    g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_ungrabbed), node);
 
4698
    g_signal_handlers_disconnect_by_func(G_OBJECT(node->knot), (gpointer) G_CALLBACK(node_request), node);
 
4699
    g_object_unref(G_OBJECT(node->knot));
 
4700
 
 
4701
    if (node->p.knot) {
 
4702
        g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
 
4703
        g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
 
4704
        g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
 
4705
        g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_request), node);
 
4706
        g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
 
4707
        g_signal_handlers_disconnect_by_func(G_OBJECT(node->p.knot), (gpointer) G_CALLBACK(node_handle_event), node);
 
4708
        g_object_unref(G_OBJECT(node->p.knot));
 
4709
        node->p.knot = NULL;
 
4710
    }
 
4711
 
 
4712
    if (node->n.knot) {
 
4713
        g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_clicked), node);
 
4714
        g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_grabbed), node);
 
4715
        g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_ungrabbed), node);
 
4716
        g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_request), node);
 
4717
        g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_moved), node);
 
4718
        g_signal_handlers_disconnect_by_func(G_OBJECT(node->n.knot), (gpointer) G_CALLBACK(node_handle_event), node);
 
4719
        g_object_unref(G_OBJECT(node->n.knot));
 
4720
        node->n.knot = NULL;
 
4721
    }
 
4722
 
 
4723
    if (node->p.line)
 
4724
        gtk_object_destroy(GTK_OBJECT(node->p.line));
 
4725
    if (node->n.line)
 
4726
        gtk_object_destroy(GTK_OBJECT(node->n.line));
 
4727
 
 
4728
    if (sp->nodes) { // there are others nodes on the subpath
 
4729
        if (sp->closed) {
 
4730
            if (sp->first == node) {
 
4731
                g_assert(sp->last == node);
 
4732
                sp->first = node->n.other;
 
4733
                sp->last = sp->first;
 
4734
            }
 
4735
            node->p.other->n.other = node->n.other;
 
4736
            node->n.other->p.other = node->p.other;
 
4737
        } else {
 
4738
            if (sp->first == node) {
 
4739
                sp->first = node->n.other;
 
4740
                sp->first->code = NR_MOVETO;
 
4741
            }
 
4742
            if (sp->last == node) sp->last = node->p.other;
 
4743
            if (node->p.other) node->p.other->n.other = node->n.other;
 
4744
            if (node->n.other) node->n.other->p.other = node->p.other;
 
4745
        }
 
4746
    } else { // this was the last node on subpath
 
4747
        sp->nodepath->subpaths = g_list_remove(sp->nodepath->subpaths, sp);
 
4748
    }
 
4749
 
 
4750
    g_mem_chunk_free(nodechunk, node);
 
4751
}
 
4752
 
 
4753
/**
 
4754
 * Returns one of the node's two sides.
 
4755
 * \param which Indicates which side.
 
4756
 * \return Pointer to previous node side if which==-1, next if which==1.
 
4757
 */
 
4758
static Inkscape::NodePath::NodeSide *sp_node_get_side(Inkscape::NodePath::Node *node, gint which)
 
4759
{
 
4760
    g_assert(node);
 
4761
    Inkscape::NodePath::NodeSide * result = 0;
 
4762
    switch (which) {
 
4763
        case -1:
 
4764
            result = &node->p;
 
4765
            break;
 
4766
        case 1:
 
4767
            result = &node->n;
 
4768
            break;
 
4769
        default:
 
4770
            g_assert_not_reached();
 
4771
    }
 
4772
 
 
4773
    return result;
 
4774
}
 
4775
 
 
4776
/**
 
4777
 * Return the other side of the node, given one of its sides.
 
4778
 */
 
4779
static Inkscape::NodePath::NodeSide *sp_node_opposite_side(Inkscape::NodePath::Node *node, Inkscape::NodePath::NodeSide *me)
 
4780
{
 
4781
    g_assert(node);
 
4782
    Inkscape::NodePath::NodeSide *result = 0;
 
4783
 
 
4784
    if (me == &node->p) {
 
4785
        result = &node->n;
 
4786
    } else if (me == &node->n) {
 
4787
        result = &node->p;
 
4788
    } else {
 
4789
        g_assert_not_reached();
 
4790
    }
 
4791
 
 
4792
    return result;
 
4793
}
 
4794
 
 
4795
/**
 
4796
 * Return NRPathcode on the given side of the node.
 
4797
 */
 
4798
static NRPathcode sp_node_path_code_from_side(Inkscape::NodePath::Node *node,Inkscape::NodePath::NodeSide *me)
 
4799
{
 
4800
    g_assert(node);
 
4801
 
 
4802
    NRPathcode result = NR_END;
 
4803
    if (me == &node->p) {
 
4804
        if (node->p.other) {
 
4805
            result = (NRPathcode)node->code;
 
4806
        } else {
 
4807
            result = NR_MOVETO;
 
4808
        }
 
4809
    } else if (me == &node->n) {
 
4810
        if (node->n.other) {
 
4811
            result = (NRPathcode)node->n.other->code;
 
4812
        } else {
 
4813
            result = NR_MOVETO;
 
4814
        }
 
4815
    } else {
 
4816
        g_assert_not_reached();
 
4817
    }
 
4818
 
 
4819
    return result;
 
4820
}
 
4821
 
 
4822
/**
 
4823
 * Return node with the given index
 
4824
 */
 
4825
Inkscape::NodePath::Node *
 
4826
sp_nodepath_get_node_by_index(Inkscape::NodePath::Path *nodepath, int index)
 
4827
{
 
4828
    Inkscape::NodePath::Node *e = NULL;
 
4829
 
 
4830
    if (!nodepath) {
 
4831
        return e;
 
4832
    }
 
4833
 
 
4834
    //find segment
 
4835
    for (GList *l = nodepath->subpaths; l ; l=l->next) {
 
4836
 
 
4837
        Inkscape::NodePath::SubPath *sp = (Inkscape::NodePath::SubPath *)l->data;
 
4838
        int n = g_list_length(sp->nodes);
 
4839
        if (sp->closed) {
 
4840
            n++;
 
4841
        }
 
4842
 
 
4843
        //if the piece belongs to this subpath grab it
 
4844
        //otherwise move onto the next subpath
 
4845
        if (index < n) {
 
4846
            e = sp->first;
 
4847
            for (int i = 0; i < index; ++i) {
 
4848
                e = e->n.other;
 
4849
            }
 
4850
            break;
 
4851
        } else {
 
4852
            if (sp->closed) {
 
4853
                index -= (n+1);
 
4854
            } else {
 
4855
                index -= n;
 
4856
            }
 
4857
        }
 
4858
    }
 
4859
 
 
4860
    return e;
 
4861
}
 
4862
 
 
4863
/**
 
4864
 * Returns plain text meaning of node type.
 
4865
 */
 
4866
static gchar const *sp_node_type_description(Inkscape::NodePath::Node *node)
 
4867
{
 
4868
    unsigned retracted = 0;
 
4869
    bool endnode = false;
 
4870
 
 
4871
    for (int which = -1; which <= 1; which += 2) {
 
4872
        Inkscape::NodePath::NodeSide *side = sp_node_get_side(node, which);
 
4873
        if (side->other && Geom::L2(side->pos - node->pos) < 1e-6)
 
4874
            retracted ++;
 
4875
        if (!side->other)
 
4876
            endnode = true;
 
4877
    }
 
4878
 
 
4879
    if (retracted == 0) {
 
4880
        if (endnode) {
 
4881
                // TRANSLATORS: "end" is an adjective here (NOT a verb)
 
4882
                return _("end node");
 
4883
        } else {
 
4884
            switch (node->type) {
 
4885
                case Inkscape::NodePath::NODE_CUSP:
 
4886
                    // TRANSLATORS: "cusp" means "sharp" (cusp node); see also the Advanced Tutorial
 
4887
                    return _("cusp");
 
4888
                case Inkscape::NodePath::NODE_SMOOTH:
 
4889
                    // TRANSLATORS: "smooth" is an adjective here
 
4890
                    return _("smooth");
 
4891
                case Inkscape::NodePath::NODE_AUTO:
 
4892
                    return _("auto");
 
4893
                case Inkscape::NodePath::NODE_SYMM:
 
4894
                    return _("symmetric");
 
4895
            }
 
4896
        }
 
4897
    } else if (retracted == 1) {
 
4898
        if (endnode) {
 
4899
            // TRANSLATORS: "end" is an adjective here (NOT a verb)
 
4900
            return _("end node, handle retracted (drag with <b>Shift</b> to extend)");
 
4901
        } else {
 
4902
            return _("one handle retracted (drag with <b>Shift</b> to extend)");
 
4903
        }
 
4904
    } else {
 
4905
        return _("both handles retracted (drag with <b>Shift</b> to extend)");
 
4906
    }
 
4907
 
 
4908
    return NULL;
 
4909
}
 
4910
 
 
4911
/**
 
4912
 * Handles content of statusbar as long as node tool is active.
 
4913
 */
 
4914
void
 
4915
sp_nodepath_update_statusbar(Inkscape::NodePath::Path *nodepath)//!!!move to ShapeEditorsCollection
 
4916
{
 
4917
    gchar const *when_selected = _("<b>Drag</b> nodes or node handles; <b>Alt+drag</b> nodes to sculpt; <b>arrow</b> keys to move nodes, <b>&lt; &gt;</b> to scale, <b>[ ]</b> to rotate");
 
4918
    gchar const *when_selected_one = _("<b>Drag</b> the node or its handles; <b>arrow</b> keys to move the node");
 
4919
 
 
4920
    gint total_nodes = sp_nodepath_get_node_count(nodepath);
 
4921
    gint selected_nodes = sp_nodepath_selection_get_node_count(nodepath);
 
4922
    gint total_subpaths = sp_nodepath_get_subpath_count(nodepath);
 
4923
    gint selected_subpaths = sp_nodepath_selection_get_subpath_count(nodepath);
 
4924
 
 
4925
    SPDesktop *desktop = NULL;
 
4926
    if (nodepath) {
 
4927
        desktop = nodepath->desktop;
 
4928
    } else {
 
4929
        desktop = SP_ACTIVE_DESKTOP; // when this is eliminated also remove #include "inkscape.h" above
 
4930
    }
 
4931
 
 
4932
    SPEventContext *ec = desktop->event_context;
 
4933
    if (!ec) return;
 
4934
 
 
4935
    Inkscape::MessageContext *mc = get_message_context(ec);
 
4936
    if (!mc) return;
 
4937
 
 
4938
    inkscape_active_desktop()->emitToolSubselectionChanged(NULL);
 
4939
 
 
4940
    if (selected_nodes == 0) {
 
4941
        Inkscape::Selection *sel = desktop->selection;
 
4942
        if (!sel || sel->isEmpty()) {
 
4943
            mc->setF(Inkscape::NORMAL_MESSAGE,
 
4944
                     _("Select a single object to edit its nodes or handles."));
 
4945
        } else {
 
4946
            if (nodepath) {
 
4947
            mc->setF(Inkscape::NORMAL_MESSAGE,
 
4948
                     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.",
 
4949
                              "<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.",
 
4950
                              total_nodes),
 
4951
                     total_nodes);
 
4952
            } else {
 
4953
                if (g_slist_length((GSList *)sel->itemList()) == 1) {
 
4954
                    mc->setF(Inkscape::NORMAL_MESSAGE, _("Drag the handles of the object to modify it."));
 
4955
                } else {
 
4956
                    mc->setF(Inkscape::NORMAL_MESSAGE, _("Select a single object to edit its nodes or handles."));
 
4957
                }
 
4958
            }
 
4959
        }
 
4960
    } else if (nodepath && selected_nodes == 1) {
 
4961
        mc->setF(Inkscape::NORMAL_MESSAGE,
 
4962
                 ngettext("<b>%i</b> of <b>%i</b> node selected; %s. %s.",
 
4963
                          "<b>%i</b> of <b>%i</b> nodes selected; %s. %s.",
 
4964
                          total_nodes),
 
4965
                 selected_nodes, total_nodes, sp_node_type_description((Inkscape::NodePath::Node *) nodepath->selected->data), when_selected_one);
 
4966
    } else {
 
4967
        if (selected_subpaths > 1) {
 
4968
            mc->setF(Inkscape::NORMAL_MESSAGE,
 
4969
                     ngettext("<b>%i</b> of <b>%i</b> node selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
 
4970
                              "<b>%i</b> of <b>%i</b> nodes selected in <b>%i</b> of <b>%i</b> subpaths. %s.",
 
4971
                              total_nodes),
 
4972
                     selected_nodes, total_nodes, selected_subpaths, total_subpaths, when_selected);
 
4973
        } else {
 
4974
            mc->setF(Inkscape::NORMAL_MESSAGE,
 
4975
                     ngettext("<b>%i</b> of <b>%i</b> node selected. %s.",
 
4976
                              "<b>%i</b> of <b>%i</b> nodes selected. %s.",
 
4977
                              total_nodes),
 
4978
                     selected_nodes, total_nodes, when_selected);
 
4979
        }
 
4980
    }
 
4981
}
 
4982
 
 
4983
/*
 
4984
 * returns a *copy* of the curve of that object.
 
4985
 */
 
4986
SPCurve* sp_nodepath_object_get_curve(SPObject *object, const gchar *key) {
 
4987
    if (!object)
 
4988
        return NULL;
 
4989
 
 
4990
    SPCurve *curve = NULL;
 
4991
    if (SP_IS_PATH(object)) {
 
4992
        SPCurve *curve_new = sp_path_get_curve_for_edit(SP_PATH(object));
 
4993
        curve = curve_new->copy();
 
4994
    } else if ( IS_LIVEPATHEFFECT(object) && key) {
 
4995
        const gchar *svgd = object->repr->attribute(key);
 
4996
        if (svgd) {
 
4997
            Geom::PathVector pv = sp_svg_read_pathv(svgd);
 
4998
            SPCurve *curve_new = new SPCurve(pv);
 
4999
            if (curve_new) {
 
5000
                curve = curve_new; // don't do curve_copy because curve_new is already only created for us!
 
5001
            }
 
5002
        }
 
5003
    }
 
5004
 
 
5005
    return curve;
 
5006
}
 
5007
 
 
5008
void sp_nodepath_set_curve (Inkscape::NodePath::Path *np, SPCurve *curve) {
 
5009
    if (!np || !np->object || !curve)
 
5010
        return;
 
5011
 
 
5012
    if (SP_IS_PATH(np->object)) {
 
5013
        if (sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object))) {
 
5014
            sp_path_set_original_curve(SP_PATH(np->object), curve, true, false);
 
5015
        } else {
 
5016
            sp_shape_set_curve(SP_SHAPE(np->object), curve, true);
 
5017
        }
 
5018
    } else if ( IS_LIVEPATHEFFECT(np->object) ) {
 
5019
        Inkscape::LivePathEffect::Effect * lpe = LIVEPATHEFFECT(np->object)->get_lpe();
 
5020
        if (lpe) {
 
5021
            Inkscape::LivePathEffect::PathParam *pathparam = dynamic_cast<Inkscape::LivePathEffect::PathParam *>( lpe->getParameter(np->repr_key) );
 
5022
            if (pathparam) {
 
5023
                pathparam->set_new_value(np->curve->get_pathvector(), false); // do not write to SVG
 
5024
                np->object->requestModified(SP_OBJECT_MODIFIED_FLAG);
 
5025
            }
 
5026
        }
 
5027
    }
 
5028
}
 
5029
 
 
5030
/*
 
5031
SPCanvasItem *
 
5032
sp_nodepath_path_to_canvasitem(Inkscape::NodePath::Path *np, SPPath *path) {
 
5033
    return sp_nodepath_make_helper_item(np, sp_path_get_curve_for_edit(path));
 
5034
}
 
5035
*/
 
5036
 
 
5037
 
 
5038
/// \todo this code to generate a helper canvasitem from an spcurve should be moved to different file
 
5039
SPCanvasItem *
 
5040
sp_nodepath_generate_helperpath(SPDesktop *desktop, SPCurve *curve, const Geom::Matrix & i2d, guint32 color = 0xff0000ff) {
 
5041
    SPCurve *flash_curve = curve->copy();
 
5042
    flash_curve->transform(i2d);
 
5043
    SPCanvasItem * canvasitem = sp_canvas_bpath_new(sp_desktop_tempgroup(desktop), flash_curve);
 
5044
    // would be nice if its color could be XORed or something, now it is invisible for red stroked objects...
 
5045
    // unless we also flash the nodes...
 
5046
    sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(canvasitem), color, 1.0, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
 
5047
    sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(canvasitem), 0, SP_WIND_RULE_NONZERO);
 
5048
    sp_canvas_item_show(canvasitem);
 
5049
    flash_curve->unref();
 
5050
    return canvasitem;
 
5051
}
 
5052
 
 
5053
SPCanvasItem *
 
5054
sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item) {
 
5055
    if (!item || !desktop) {
 
5056
        return NULL;
 
5057
    }
 
5058
 
 
5059
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
5060
    guint32 color = prefs->getInt("/tools/nodes/highlight_color", 0xff0000ff);
 
5061
 
 
5062
    Geom::Matrix i2d = sp_item_i2d_affine(item);
 
5063
 
 
5064
    SPCurve *curve = NULL;
 
5065
    if (SP_IS_PATH(item)) {
 
5066
        curve = sp_path_get_curve_for_edit(SP_PATH(item));
 
5067
    } else if ( SP_IS_SHAPE(item) && SP_SHAPE(item)->curve ) {
 
5068
        curve = sp_shape_get_curve (SP_SHAPE(item));
 
5069
    } else if ( SP_IS_TEXT(item) ) {
 
5070
        // do not display helperpath for text - we cannot do anything with it in Node tool anyway
 
5071
        // curve = SP_TEXT(item)->getNormalizedBpath();
 
5072
        return NULL;
 
5073
    } else {
 
5074
        g_warning ("-----> sp_nodepath_generate_helperpath(SPDesktop *desktop, SPItem *item): TODO: generate the helper path for this item type!\n");
 
5075
        return NULL;
 
5076
    }
 
5077
 
 
5078
    SPCanvasItem * helperpath = sp_nodepath_generate_helperpath(desktop, curve, i2d, color);
 
5079
 
 
5080
    curve->unref();
 
5081
 
 
5082
    return helperpath;
 
5083
}
 
5084
 
 
5085
 
 
5086
// TODO: Merge this with sp_nodepath_make_helper_item()!
 
5087
void sp_nodepath_show_helperpath(Inkscape::NodePath::Path *np, bool show) {
 
5088
    np->show_helperpath = show;
 
5089
 
 
5090
    if (show) {
 
5091
        SPCurve *helper_curve = np->curve->copy();
 
5092
        helper_curve->transform(np->i2d);
 
5093
        if (!np->helper_path) {
 
5094
            //np->helper_path = sp_nodepath_make_helper_item(np, desktop, helper_curve, true); // Caution: this applies the transform np->i2d twice!!
 
5095
 
 
5096
            np->helper_path = sp_canvas_bpath_new(sp_desktop_controls(np->desktop), helper_curve);
 
5097
            sp_canvas_bpath_set_stroke(SP_CANVAS_BPATH(np->helper_path), np->helperpath_rgba, np->helperpath_width, SP_STROKE_LINEJOIN_MITER, SP_STROKE_LINECAP_BUTT);
 
5098
            sp_canvas_bpath_set_fill(SP_CANVAS_BPATH(np->helper_path), 0, SP_WIND_RULE_NONZERO);
 
5099
            sp_canvas_item_move_to_z(np->helper_path, 0);
 
5100
            sp_canvas_item_show(np->helper_path);
 
5101
        } else {
 
5102
            sp_canvas_bpath_set_bpath(SP_CANVAS_BPATH(np->helper_path), helper_curve);
 
5103
        }
 
5104
        helper_curve->unref();
 
5105
    } else {
 
5106
        if (np->helper_path) {
 
5107
            GtkObject *temp = np->helper_path;
 
5108
            np->helper_path = NULL;
 
5109
            gtk_object_destroy(temp);
 
5110
        }
 
5111
    }
 
5112
}
 
5113
 
 
5114
/* sp_nodepath_make_straight_path:
 
5115
 *   Prevents user from curving the path by dragging a segment or activating handles etc.
 
5116
 *   The resulting path is a linear interpolation between nodal points, with only straight segments.
 
5117
 * !!! this function does not work completely yet: it does not actively straighten the path, only prevents the path from being curved
 
5118
 */
 
5119
void sp_nodepath_make_straight_path(Inkscape::NodePath::Path *np) {
 
5120
    np->straight_path = true;
 
5121
    np->show_handles = false;
 
5122
    g_message("add code to make the path straight.");
 
5123
    // do sp_nodepath_convert_node_type on all nodes?
 
5124
    // coding tip: search for this text : "Make selected segments lines"
 
5125
}
 
5126
 
 
5127
/*
 
5128
  Local Variables:
 
5129
  mode:c++
 
5130
  c-file-style:"stroustrup"
 
5131
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
5132
  indent-tabs-mode:nil
 
5133
  fill-column:99
 
5134
  End:
 
5135
*/
 
5136
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :