~ubuntu-branches/debian/experimental/inkscape/experimental

« back to all changes in this revision

Viewing changes to src/shape-editor.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Thomas Viehmann
  • Date: 2008-09-09 23:29:02 UTC
  • mfrom: (1.1.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20080909232902-c50iujhk1w79u8e7
Tags: 0.46-2.1
* Non-maintainer upload.
* Add upstream patch fixing a crash in the open dialog
  in the zh_CN.utf8 locale. Closes: #487623.
  Thanks to Luca Bruno for the patch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __SHAPE_EDITOR_CPP__
 
2
 
 
3
/*
 
4
 * Inkscape::ShapeEditor
 
5
 *
 
6
 * Authors:
 
7
 *   bulia byak <buliabyak@users.sf.net>
 
8
 *
 
9
 */
 
10
 
 
11
#ifdef HAVE_CONFIG_H
 
12
#include "config.h"
 
13
#endif
 
14
 
 
15
#include <string.h>
 
16
#include <glibmm/i18n.h>
 
17
 
 
18
#include "sp-object.h"
 
19
#include "sp-item.h"
 
20
#include "live_effects/lpeobject.h"
 
21
#include "selection.h"
 
22
#include "desktop.h"
 
23
#include "desktop-handles.h"
 
24
#include "knotholder.h"
 
25
#include "node-context.h"
 
26
#include "xml/node-event-vector.h"
 
27
#include "prefs-utils.h"
 
28
#include "object-edit.h"
 
29
#include "splivarot.h"
 
30
#include "style.h"
 
31
 
 
32
#include "shape-editor.h"
 
33
 
 
34
 
 
35
ShapeEditorsCollective::ShapeEditorsCollective(SPDesktop */*dt*/) {
 
36
}
 
37
 
 
38
ShapeEditorsCollective::~ShapeEditorsCollective() {
 
39
}
 
40
 
 
41
 
 
42
void ShapeEditorsCollective::update_statusbar() {
 
43
 
 
44
//!!! move from nodepath: sp_nodepath_update_statusbar but summing for all nodepaths
 
45
 
 
46
}
 
47
 
 
48
ShapeEditor::ShapeEditor(SPDesktop *dt) {
 
49
    this->desktop = dt;
 
50
    this->grab_node = -1;
 
51
    this->nodepath = NULL;
 
52
    this->knotholder = NULL;
 
53
    this->hit = false;
 
54
}
 
55
 
 
56
ShapeEditor::~ShapeEditor() {
 
57
    unset_item();
 
58
}
 
59
 
 
60
void ShapeEditor::unset_item() {
 
61
 
 
62
    Inkscape::XML::Node *old_repr = NULL;
 
63
 
 
64
    if (this->nodepath) {
 
65
        old_repr = this->nodepath->repr;
 
66
    }
 
67
 
 
68
    if (!old_repr && this->knotholder) {
 
69
        old_repr = this->knotholder->repr;
 
70
    }
 
71
 
 
72
    if (old_repr) { // remove old listener
 
73
        sp_repr_remove_listener_by_data(old_repr, this);
 
74
        Inkscape::GC::release(old_repr);
 
75
    }
 
76
 
 
77
    if (this->nodepath) {
 
78
        this->grab_node = -1;
 
79
        sp_nodepath_destroy(this->nodepath);
 
80
        this->nodepath = NULL;
 
81
    }
 
82
 
 
83
    if (this->knotholder) {
 
84
        sp_knot_holder_destroy(this->knotholder);
 
85
        this->knotholder = NULL;
 
86
    }
 
87
}
 
88
 
 
89
bool ShapeEditor::has_nodepath () {
 
90
    return (this->nodepath != NULL);
 
91
}
 
92
 
 
93
bool ShapeEditor::has_knotholder () {
 
94
    return (this->knotholder != NULL);
 
95
}
 
96
 
 
97
bool ShapeEditor::has_local_change () {
 
98
    if (this->nodepath)
 
99
        return (this->nodepath->local_change != 0);
 
100
    else if (this->knotholder)
 
101
        return (this->knotholder->local_change != 0);
 
102
    else
 
103
        return false;
 
104
}
 
105
 
 
106
void ShapeEditor::decrement_local_change () {
 
107
    if (this->nodepath) {
 
108
        if (this->nodepath->local_change > 0)
 
109
            this->nodepath->local_change--;
 
110
    } else if (this->knotholder) {
 
111
        this->knotholder->local_change = FALSE;
 
112
    }
 
113
}
 
114
 
 
115
SPItem *ShapeEditor::get_item () {
 
116
    SPItem *item = NULL;
 
117
    if (this->has_nodepath()) {
 
118
        item = this->nodepath->item;
 
119
    } else if (this->has_knotholder()) {
 
120
        item = SP_ITEM(this->knotholder->item);
 
121
    }
 
122
    return item;
 
123
}
 
124
 
 
125
GList *ShapeEditor::save_nodepath_selection () {
 
126
    if (this->nodepath)
 
127
        return ::save_nodepath_selection (this->nodepath);
 
128
    return NULL;
 
129
}
 
130
 
 
131
void ShapeEditor::restore_nodepath_selection (GList *saved) {
 
132
    if (this->nodepath && saved)
 
133
        ::restore_nodepath_selection (this->nodepath, saved);
 
134
}
 
135
 
 
136
bool ShapeEditor::nodepath_edits_repr_key(gchar const *name) {
 
137
    if (nodepath && name) {
 
138
        return ( !strcmp(name, nodepath->repr_key) || !strcmp(name, nodepath->repr_nodetypes_key) );
 
139
    }
 
140
 
 
141
    return false;
 
142
}
 
143
 
 
144
static void shapeeditor_event_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *name,
 
145
                                           gchar const */*old_value*/, gchar const */*new_value*/,
 
146
                                           bool /*is_interactive*/, gpointer data)
 
147
{
 
148
    gboolean changed = FALSE;
 
149
 
 
150
    g_assert(data);
 
151
    ShapeEditor *sh = ((ShapeEditor *) data);
 
152
 
 
153
    if ( sh->has_knotholder() || ( sh->has_nodepath() && sh->nodepath_edits_repr_key(name) ) )
 
154
    {
 
155
        changed = !sh->has_local_change();
 
156
        sh->decrement_local_change();
 
157
    }
 
158
 
 
159
    if (changed) {
 
160
        GList *saved = NULL;
 
161
        if (sh->has_nodepath()) {
 
162
            saved = sh->save_nodepath_selection();
 
163
        }
 
164
 
 
165
        sh->reset_item ();
 
166
 
 
167
        if (sh->has_nodepath() && saved) {
 
168
            sh->restore_nodepath_selection(saved);
 
169
            g_list_free (saved);
 
170
        }
 
171
    }
 
172
 
 
173
    sh->update_statusbar(); //TODO: sh->get_container()->update_statusbar();
 
174
}
 
175
 
 
176
static Inkscape::XML::NodeEventVector shapeeditor_repr_events = {
 
177
    NULL, /* child_added */
 
178
    NULL, /* child_removed */
 
179
    shapeeditor_event_attr_changed,
 
180
    NULL, /* content_changed */
 
181
    NULL  /* order_changed */
 
182
};
 
183
 
 
184
 
 
185
void ShapeEditor::set_item(SPItem *item) {
 
186
 
 
187
    unset_item();
 
188
 
 
189
    this->grab_node = -1;
 
190
 
 
191
    if (item) {
 
192
        this->nodepath = sp_nodepath_new(desktop, item, (prefs_get_int_attribute("tools.nodes", "show_handles", 1) != 0));
 
193
        if (this->nodepath) {
 
194
            this->nodepath->shape_editor = this;
 
195
        }
 
196
        this->knotholder = sp_item_knot_holder(item, desktop);
 
197
 
 
198
        if (this->nodepath || this->knotholder) {
 
199
            // setting new listener
 
200
            Inkscape::XML::Node *repr;
 
201
            if (this->knotholder)
 
202
                repr = this->knotholder->repr;
 
203
            else
 
204
                repr = SP_OBJECT_REPR(item);
 
205
            if (repr) {
 
206
                Inkscape::GC::anchor(repr);
 
207
                sp_repr_add_listener(repr, &shapeeditor_repr_events, this);
 
208
            }
 
209
        }
 
210
    }
 
211
}
 
212
 
 
213
/** Please note that this function only works for path parameters.
 
214
*  All other parameters probably will crash Inkscape!
 
215
*  Fortunately, there are no other on-canvas edittable objects at this moment :)
 
216
*/
 
217
void ShapeEditor::set_item_livepatheffect_parameter(SPItem *item, SPObject *lpeobject, const char * key) {
 
218
 
 
219
    unset_item();
 
220
 
 
221
    this->grab_node = -1;
 
222
 
 
223
    if (lpeobject) {
 
224
        this->knotholder = NULL; // it's a path, no special knotholder needed.
 
225
        this->nodepath = sp_nodepath_new( desktop, lpeobject,
 
226
                                          (prefs_get_int_attribute("tools.nodes", "show_handles", 1) != 0),
 
227
                                          key, item);
 
228
        if (this->nodepath) {
 
229
            this->nodepath->shape_editor = this;
 
230
 
 
231
            // setting new listener
 
232
            Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeobject);
 
233
            if (repr) {
 
234
                Inkscape::GC::anchor(repr);
 
235
                sp_repr_add_listener(repr, &shapeeditor_repr_events, this);
 
236
            }
 
237
        }
 
238
    }
 
239
}
 
240
 
 
241
/** FIXME: think about this. Is this thing only called when the item needs to be updated?
 
242
   Why not make a reload function in NodePath and in KnotHolder? */
 
243
void ShapeEditor::reset_item ()
 
244
{
 
245
    if ( (this->nodepath) && (IS_LIVEPATHEFFECT(this->nodepath->object)) ) {
 
246
        SPItem * item = this->nodepath->item;
 
247
        SPObject *obj = this->nodepath->object;
 
248
        char * key = g_strdup(this->nodepath->repr_key);
 
249
        set_item_livepatheffect_parameter(item, obj, key);
 
250
        g_free(key);
 
251
    } else {
 
252
        SPItem * item = get_item();
 
253
        set_item(item);
 
254
    }
 
255
}
 
256
 
 
257
void ShapeEditor::nodepath_destroyed () {
 
258
    this->nodepath = NULL;
 
259
}
 
260
 
 
261
void ShapeEditor::update_statusbar () {
 
262
    if (this->nodepath)
 
263
        sp_nodepath_update_statusbar(this->nodepath);
 
264
}
 
265
 
 
266
bool ShapeEditor::is_over_stroke (NR::Point event_p, bool remember) {
 
267
 
 
268
    if (!this->nodepath)
 
269
        return false; // no stroke in knotholder
 
270
 
 
271
    SPItem *item = get_item();
 
272
 
 
273
    //Translate click point into proper coord system
 
274
    this->curvepoint_doc = desktop->w2d(event_p);
 
275
    this->curvepoint_doc *= sp_item_dt2i_affine(item);
 
276
 
 
277
    sp_nodepath_ensure_livarot_path(this->nodepath);
 
278
 
 
279
    NR::Maybe<Path::cut_position> position = get_nearest_position_on_Path(this->nodepath->livarot_path, this->curvepoint_doc);
 
280
    if (!position) {
 
281
        return false;
 
282
    }
 
283
 
 
284
    NR::Point nearest = get_point_on_Path(this->nodepath->livarot_path, position->piece, position->t);
 
285
    NR::Point delta = nearest - this->curvepoint_doc;
 
286
 
 
287
    delta = desktop->d2w(delta);
 
288
 
 
289
    double stroke_tolerance =
 
290
        (( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
 
291
           desktop->current_zoom() *
 
292
           SP_OBJECT_STYLE (item)->stroke_width.computed * 0.5 *
 
293
           sp_item_i2d_affine (item).expansion()
 
294
         : 0.0)
 
295
         + prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100)) /sp_item_i2d_affine (item).expansion(); 
 
296
    bool close = (NR::L2 (delta) < stroke_tolerance);
 
297
 
 
298
    if (remember && close) {
 
299
        this->curvepoint_event[NR::X] = (gint) event_p [NR::X];
 
300
        this->curvepoint_event[NR::Y] = (gint) event_p [NR::Y];
 
301
        this->hit = true;
 
302
        this->grab_t = position->t;
 
303
        this->grab_node = position->piece;
 
304
    }
 
305
 
 
306
    return close;
 
307
}
 
308
 
 
309
void ShapeEditor::add_node_near_point() {
 
310
    if (this->nodepath) {
 
311
        sp_nodepath_add_node_near_point(this->nodepath, this->curvepoint_doc);
 
312
    } else if (this->knotholder) {
 
313
        // we do not add nodes in knotholder... yet
 
314
    }
 
315
}
 
316
 
 
317
void ShapeEditor::select_segment_near_point(bool toggle) {
 
318
    if (this->nodepath) {
 
319
        sp_nodepath_select_segment_near_point(this->nodepath, this->curvepoint_doc, toggle);
 
320
    } else if (this->knotholder) {
 
321
        // we do not select segments in knotholder... yet?
 
322
    }
 
323
}
 
324
 
 
325
void ShapeEditor::cancel_hit() {
 
326
    this->hit = false;
 
327
}
 
328
 
 
329
bool ShapeEditor::hits_curve() {
 
330
    return (this->hit);
 
331
}
 
332
 
 
333
 
 
334
void ShapeEditor::curve_drag(gdouble eventx, gdouble eventy) {
 
335
    if (this->nodepath && !this->nodepath->straight_path) {
 
336
 
 
337
        if (this->grab_node == -1) // don't know which segment to drag
 
338
            return;
 
339
 
 
340
        // We round off the extra precision in the motion coordinates provided
 
341
        // by some input devices (like tablets). As we'll store the coordinates
 
342
        // as integers in curvepoint_event we need to do this rounding before
 
343
        // comparing them with the last coordinates from curvepoint_event.
 
344
        // See bug #1593499 for details.
 
345
 
 
346
        gint x = (gint) Inkscape::round(eventx);
 
347
        gint y = (gint) Inkscape::round(eventy);
 
348
 
 
349
 
 
350
        // The coordinates hasn't changed since the last motion event, abort
 
351
        if (this->curvepoint_event[NR::X] == x &&
 
352
            this->curvepoint_event[NR::Y] == y)
 
353
            return;
 
354
 
 
355
        NR::Point const delta_w(eventx - this->curvepoint_event[NR::X],
 
356
                                eventy - this->curvepoint_event[NR::Y]);
 
357
        NR::Point const delta_dt(this->desktop->w2d(delta_w));
 
358
 
 
359
        sp_nodepath_curve_drag (this->grab_node, this->grab_t, delta_dt); //!!! FIXME: which nodepath?!!! also uses current!!!
 
360
        this->curvepoint_event[NR::X] = x;
 
361
        this->curvepoint_event[NR::Y] = y;
 
362
 
 
363
    } else if (this->knotholder) {
 
364
        // we do not drag curve in knotholder
 
365
    }
 
366
 
 
367
}
 
368
 
 
369
void ShapeEditor::finish_drag() {
 
370
    if (this->nodepath && this->hit) {
 
371
        sp_nodepath_update_repr (this->nodepath, _("Drag curve"));
 
372
    }
 
373
}
 
374
 
 
375
void ShapeEditor::select_rect(NR::Rect const &rect, bool add) {
 
376
    if (this->nodepath) {
 
377
        sp_nodepath_select_rect(this->nodepath, rect, add);
 
378
    }
 
379
}
 
380
 
 
381
bool ShapeEditor::has_selection() {
 
382
    if (this->nodepath)
 
383
        return this->nodepath->selected;
 
384
    return false; //  so far, knotholder cannot have selection
 
385
}
 
386
 
 
387
void ShapeEditor::deselect() {
 
388
    if (this->nodepath)
 
389
        sp_nodepath_deselect(this->nodepath);
 
390
}
 
391
 
 
392
void ShapeEditor::add_node () {
 
393
    sp_node_selected_add_node(this->nodepath);
 
394
}
 
395
 
 
396
void ShapeEditor::delete_nodes () {
 
397
    sp_node_selected_delete(this->nodepath);
 
398
}
 
399
 
 
400
void ShapeEditor::delete_nodes_preserving_shape () {
 
401
    if (this->nodepath && this->nodepath->selected) {
 
402
        sp_node_delete_preserve(g_list_copy(this->nodepath->selected));
 
403
    }
 
404
}
 
405
 
 
406
void ShapeEditor::delete_segment () {
 
407
    sp_node_selected_delete_segment(this->nodepath);
 
408
}
 
409
 
 
410
void ShapeEditor::set_node_type(int type) {
 
411
    sp_node_selected_set_type(this->nodepath, (Inkscape::NodePath::NodeType) type);
 
412
}
 
413
 
 
414
void ShapeEditor::break_at_nodes() {
 
415
    sp_node_selected_break(this->nodepath);
 
416
}
 
417
 
 
418
void ShapeEditor::join_nodes() {
 
419
    sp_node_selected_join(this->nodepath);
 
420
}
 
421
 
 
422
void ShapeEditor::join_segments() {
 
423
    sp_node_selected_join_segment(this->nodepath);
 
424
}
 
425
 
 
426
void ShapeEditor::duplicate_nodes() {
 
427
    sp_node_selected_duplicate(this->nodepath);
 
428
}
 
429
 
 
430
void ShapeEditor::set_type_of_segments(NRPathcode code) {
 
431
    sp_node_selected_set_line_type(this->nodepath, code);
 
432
}
 
433
 
 
434
void ShapeEditor::move_nodes_screen(gdouble dx, gdouble dy) {
 
435
    sp_node_selected_move_screen(this->nodepath, dx, dy);
 
436
}
 
437
void ShapeEditor::move_nodes(gdouble dx, gdouble dy) {
 
438
    sp_node_selected_move(this->nodepath, dx, dy);
 
439
}
 
440
 
 
441
void ShapeEditor::rotate_nodes(gdouble angle, int which, bool screen) {
 
442
    if (this->nodepath)
 
443
        sp_nodepath_selected_nodes_rotate (this->nodepath, angle, which, screen);
 
444
}
 
445
 
 
446
void ShapeEditor::scale_nodes(gdouble const grow, int const which) {
 
447
    sp_nodepath_selected_nodes_scale(this->nodepath, grow, which);
 
448
}
 
449
void ShapeEditor::scale_nodes_screen(gdouble const grow, int const which) {
 
450
    sp_nodepath_selected_nodes_scale_screen(this->nodepath, grow, which);
 
451
}
 
452
 
 
453
void ShapeEditor::select_all (bool invert) {
 
454
    if (this->nodepath)
 
455
        sp_nodepath_select_all (this->nodepath, invert);
 
456
}
 
457
void ShapeEditor::select_all_from_subpath (bool invert) {
 
458
    if (this->nodepath)
 
459
        sp_nodepath_select_all_from_subpath (this->nodepath, invert);
 
460
}
 
461
void ShapeEditor::select_next () {
 
462
    if (this->nodepath) {
 
463
        sp_nodepath_select_next (this->nodepath);
 
464
        if (this->nodepath->numSelected() >= 1) {
 
465
            this->desktop->scroll_to_point(&(this->nodepath->singleSelectedCoords()), 1.0);
 
466
        }
 
467
    }
 
468
}
 
469
void ShapeEditor::select_prev () {
 
470
    if (this->nodepath) {
 
471
        sp_nodepath_select_prev (this->nodepath);
 
472
        if (this->nodepath->numSelected() >= 1) {
 
473
            this->desktop->scroll_to_point(&(this->nodepath->singleSelectedCoords()), 1.0);
 
474
        }
 
475
    }
 
476
}
 
477
 
 
478
void ShapeEditor::show_handles (bool show) {
 
479
    if (this->nodepath && !this->nodepath->straight_path)
 
480
        sp_nodepath_show_handles (this->nodepath, show);
 
481
}
 
482
 
 
483
 
 
484
void ShapeEditor::flip (NR::Dim2 axis, NR::Maybe<NR::Point> center) {
 
485
    if (this->nodepath)
 
486
        sp_nodepath_flip (this->nodepath, axis, center);
 
487
}
 
488
 
 
489
void ShapeEditor::distribute (NR::Dim2 axis) {
 
490
    if (this->nodepath)
 
491
        sp_nodepath_selected_distribute (this->nodepath, axis);
 
492
}
 
493
void ShapeEditor::align (NR::Dim2 axis) {
 
494
    if (this->nodepath)
 
495
        sp_nodepath_selected_align (this->nodepath, axis);
 
496
}
 
497
 
 
498
 
 
499
/*
 
500
  Local Variables:
 
501
  mode:c++
 
502
  c-file-style:"stroustrup"
 
503
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
504
  indent-tabs-mode:nil
 
505
  fill-column:99
 
506
  End:
 
507
*/
 
508
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :