~ubuntu-branches/ubuntu/natty/inkscape/natty

« back to all changes in this revision

Viewing changes to src/nodepath.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alex Valavanis
  • Date: 2010-09-12 19:44:58 UTC
  • mfrom: (1.1.12 upstream) (45.1.3 maverick)
  • Revision ID: james.westby@ubuntu.com-20100912194458-4sjwmbl7dlsrk5dc
Tags: 0.48.0-1ubuntu1
* Merge with Debian unstable (LP: #628048, LP: #401567, LP: #456248, 
  LP: #463602, LP: #591986)
* debian/control: 
  - Ubuntu maintainers
  - Promote python-lxml, python-numpy, python-uniconvertor to Recommends.
  - Demote pstoedit to Suggests (universe package).
  - Suggests ttf-dejavu instead of ttf-bitstream-vera (LP: #513319)
* debian/rules:
  - Run intltool-update on build (Ubuntu-specific).
  - Add translation domain to .desktop files (Ubuntu-specific).
* debian/dirs:
  - Add usr/share/pixmaps.  Allow inkscape.xpm installation
* drop 50-poppler-API.dpatch (now upstream)
* drop 51-paste-in-unwritable-directory.dpatch (now upstream) 

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