~ubuntu-branches/ubuntu/oneiric/inkscape/oneiric-updates

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/sp-use.cpp

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

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __SP_USE_C__
 
2
 
 
3
/*
 
4
 * SVG <use> implementation
 
5
 *
 
6
 * Authors:
 
7
 *   Lauris Kaplinski <lauris@kaplinski.com>
 
8
 *   bulia byak <buliabyak@users.sf.net>
 
9
 *
 
10
 * Copyright (C) 1999-2005 authors
 
11
 * Copyright (C) 2000-2001 Ximian, Inc.
 
12
 *
 
13
 * Released under GNU GPL, read the file 'COPYING' for more information
 
14
 */
 
15
 
 
16
#ifdef HAVE_CONFIG_H
 
17
# include <config.h>
 
18
#endif
 
19
 
 
20
#include <cstring>
 
21
#include <string>
 
22
 
 
23
#include <libnr/nr-matrix-ops.h>
 
24
#include <libnr/nr-matrix-fns.h>
 
25
#include <2geom/transforms.h>
 
26
#include <glibmm/i18n.h>
 
27
#include "display/nr-arena-group.h"
 
28
#include "attributes.h"
 
29
#include "document.h"
 
30
#include "sp-object-repr.h"
 
31
#include "sp-flowregion.h"
 
32
#include "uri.h"
 
33
#include "print.h"
 
34
#include "xml/repr.h"
 
35
#include "preferences.h"
 
36
#include "style.h"
 
37
#include "sp-symbol.h"
 
38
#include "sp-use.h"
 
39
#include "sp-use-reference.h"
 
40
 
 
41
/* fixme: */
 
42
 
 
43
static void sp_use_class_init(SPUseClass *classname);
 
44
static void sp_use_init(SPUse *use);
 
45
static void sp_use_finalize(GObject *obj);
 
46
 
 
47
static void sp_use_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
 
48
static void sp_use_release(SPObject *object);
 
49
static void sp_use_set(SPObject *object, unsigned key, gchar const *value);
 
50
static Inkscape::XML::Node *sp_use_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
 
51
static void sp_use_update(SPObject *object, SPCtx *ctx, guint flags);
 
52
static void sp_use_modified(SPObject *object, guint flags);
 
53
 
 
54
static void sp_use_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags);
 
55
static void sp_use_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs);
 
56
static void sp_use_print(SPItem *item, SPPrintContext *ctx);
 
57
static gchar *sp_use_description(SPItem *item);
 
58
static NRArenaItem *sp_use_show(SPItem *item, NRArena *arena, unsigned key, unsigned flags);
 
59
static void sp_use_hide(SPItem *item, unsigned key);
 
60
 
 
61
static void sp_use_href_changed(SPObject *old_ref, SPObject *ref, SPUse *use);
 
62
 
 
63
static void sp_use_delete_self(SPObject *deleted, SPUse *self);
 
64
 
 
65
static SPItemClass *parent_class;
 
66
 
 
67
//void m_print(gchar *say, Geom::Matrix m)
 
68
//{ g_print("%s %g %g %g %g %g %g\n", say, m[0], m[1], m[2], m[3], m[4], m[5]); }
 
69
 
 
70
GType
 
71
sp_use_get_type(void)
 
72
{
 
73
    static GType use_type = 0;
 
74
    if (!use_type) {
 
75
        GTypeInfo use_info = {
 
76
            sizeof(SPUseClass),
 
77
            NULL,   /* base_init */
 
78
            NULL,   /* base_finalize */
 
79
            (GClassInitFunc) sp_use_class_init,
 
80
            NULL,   /* class_finalize */
 
81
            NULL,   /* class_data */
 
82
            sizeof(SPUse),
 
83
            16,     /* n_preallocs */
 
84
            (GInstanceInitFunc) sp_use_init,
 
85
            NULL,   /* value_table */
 
86
        };
 
87
        use_type = g_type_register_static(SP_TYPE_ITEM, "SPUse", &use_info, (GTypeFlags)0);
 
88
    }
 
89
    return use_type;
 
90
}
 
91
 
 
92
static void
 
93
sp_use_class_init(SPUseClass *classname)
 
94
{
 
95
    GObjectClass *gobject_class = (GObjectClass *) classname;
 
96
    SPObjectClass *sp_object_class = (SPObjectClass *) classname;
 
97
    SPItemClass *item_class = (SPItemClass *) classname;
 
98
 
 
99
    parent_class = (SPItemClass*) g_type_class_ref(SP_TYPE_ITEM);
 
100
 
 
101
    gobject_class->finalize = sp_use_finalize;
 
102
 
 
103
    sp_object_class->build = sp_use_build;
 
104
    sp_object_class->release = sp_use_release;
 
105
    sp_object_class->set = sp_use_set;
 
106
    sp_object_class->write = sp_use_write;
 
107
    sp_object_class->update = sp_use_update;
 
108
    sp_object_class->modified = sp_use_modified;
 
109
 
 
110
    item_class->bbox = sp_use_bbox;
 
111
    item_class->description = sp_use_description;
 
112
    item_class->print = sp_use_print;
 
113
    item_class->show = sp_use_show;
 
114
    item_class->hide = sp_use_hide;
 
115
    item_class->snappoints = sp_use_snappoints;
 
116
}
 
117
 
 
118
static void
 
119
sp_use_init(SPUse *use)
 
120
{
 
121
    use->x.unset();
 
122
    use->y.unset();
 
123
    use->width.unset(SVGLength::PERCENT, 1.0, 1.0);
 
124
    use->height.unset(SVGLength::PERCENT, 1.0, 1.0);
 
125
    use->href = NULL;
 
126
 
 
127
    new (&use->_delete_connection) sigc::connection();
 
128
    new (&use->_changed_connection) sigc::connection();
 
129
 
 
130
    new (&use->_transformed_connection) sigc::connection();
 
131
 
 
132
    use->ref = new SPUseReference(SP_OBJECT(use));
 
133
 
 
134
    use->_changed_connection = use->ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_use_href_changed), use));
 
135
}
 
136
 
 
137
static void
 
138
sp_use_finalize(GObject *obj)
 
139
{
 
140
    SPUse *use = (SPUse *) obj;
 
141
 
 
142
    if (use->child) {
 
143
        sp_object_detach(SP_OBJECT(obj), use->child);
 
144
        use->child = NULL;
 
145
    }
 
146
 
 
147
    use->ref->detach();
 
148
    delete use->ref;
 
149
 
 
150
    use->_delete_connection.~connection();
 
151
    use->_changed_connection.~connection();
 
152
 
 
153
    use->_transformed_connection.~connection();
 
154
}
 
155
 
 
156
static void
 
157
sp_use_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
 
158
{
 
159
    if (((SPObjectClass *) parent_class)->build) {
 
160
        (* ((SPObjectClass *) parent_class)->build)(object, document, repr);
 
161
    }
 
162
 
 
163
    sp_object_read_attr(object, "x");
 
164
    sp_object_read_attr(object, "y");
 
165
    sp_object_read_attr(object, "width");
 
166
    sp_object_read_attr(object, "height");
 
167
    sp_object_read_attr(object, "xlink:href");
 
168
 
 
169
    // We don't need to create child here:
 
170
    // reading xlink:href will attach ref, and that will cause the changed signal to be emitted,
 
171
    // which will call sp_use_href_changed, and that will take care of the child
 
172
}
 
173
 
 
174
static void
 
175
sp_use_release(SPObject *object)
 
176
{
 
177
    SPUse *use = SP_USE(object);
 
178
 
 
179
    if (use->child) {
 
180
        sp_object_detach(object, use->child);
 
181
        use->child = NULL;
 
182
    }
 
183
 
 
184
    use->_delete_connection.disconnect();
 
185
    use->_changed_connection.disconnect();
 
186
    use->_transformed_connection.disconnect();
 
187
 
 
188
    g_free(use->href);
 
189
    use->href = NULL;
 
190
 
 
191
    use->ref->detach();
 
192
 
 
193
    if (((SPObjectClass *) parent_class)->release) {
 
194
        ((SPObjectClass *) parent_class)->release(object);
 
195
    }
 
196
}
 
197
 
 
198
static void
 
199
sp_use_set(SPObject *object, unsigned key, gchar const *value)
 
200
{
 
201
    SPUse *use = SP_USE(object);
 
202
 
 
203
    switch (key) {
 
204
        case SP_ATTR_X:
 
205
            use->x.readOrUnset(value);
 
206
            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
 
207
            break;
 
208
        case SP_ATTR_Y:
 
209
            use->y.readOrUnset(value);
 
210
            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
 
211
            break;
 
212
        case SP_ATTR_WIDTH:
 
213
            use->width.readOrUnset(value, SVGLength::PERCENT, 1.0, 1.0);
 
214
            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
 
215
            break;
 
216
        case SP_ATTR_HEIGHT:
 
217
            use->height.readOrUnset(value, SVGLength::PERCENT, 1.0, 1.0);
 
218
            object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
 
219
            break;
 
220
 
 
221
        case SP_ATTR_XLINK_HREF: {
 
222
            if ( value && use->href && ( strcmp(value, use->href) == 0 ) ) {
 
223
                /* No change, do nothing. */
 
224
            } else {
 
225
                g_free(use->href);
 
226
                use->href = NULL;
 
227
                if (value) {
 
228
                    // First, set the href field, because sp_use_href_changed will need it.
 
229
                    use->href = g_strdup(value);
 
230
 
 
231
                    // Now do the attaching, which emits the changed signal.
 
232
                    try {
 
233
                        use->ref->attach(Inkscape::URI(value));
 
234
                    } catch (Inkscape::BadURIException &e) {
 
235
                        g_warning("%s", e.what());
 
236
                        use->ref->detach();
 
237
                    }
 
238
                } else {
 
239
                    use->ref->detach();
 
240
                }
 
241
            }
 
242
            break;
 
243
        }
 
244
 
 
245
        default:
 
246
            if (((SPObjectClass *) parent_class)->set) {
 
247
                ((SPObjectClass *) parent_class)->set(object, key, value);
 
248
            }
 
249
            break;
 
250
    }
 
251
}
 
252
 
 
253
static Inkscape::XML::Node *
 
254
sp_use_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
 
255
{
 
256
    SPUse *use = SP_USE(object);
 
257
 
 
258
    if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
 
259
        repr = xml_doc->createElement("svg:use");
 
260
    }
 
261
 
 
262
    if (((SPObjectClass *) (parent_class))->write) {
 
263
        ((SPObjectClass *) (parent_class))->write(object, xml_doc, repr, flags);
 
264
    }
 
265
 
 
266
    sp_repr_set_svg_double(repr, "x", use->x.computed);
 
267
    sp_repr_set_svg_double(repr, "y", use->y.computed);
 
268
    sp_repr_set_svg_double(repr, "width", use->width.computed);
 
269
    sp_repr_set_svg_double(repr, "height", use->height.computed);
 
270
 
 
271
    if (use->ref->getURI()) {
 
272
        gchar *uri_string = use->ref->getURI()->toString();
 
273
        repr->setAttribute("xlink:href", uri_string);
 
274
        g_free(uri_string);
 
275
    }
 
276
 
 
277
    return repr;
 
278
}
 
279
 
 
280
static void
 
281
sp_use_bbox(SPItem const *item, NRRect *bbox, Geom::Matrix const &transform, unsigned const flags)
 
282
{
 
283
    SPUse const *use = SP_USE(item);
 
284
 
 
285
    if (use->child && SP_IS_ITEM(use->child)) {
 
286
        SPItem *child = SP_ITEM(use->child);
 
287
        Geom::Matrix const ct( child->transform
 
288
                             * Geom::Translate(use->x.computed,
 
289
                                               use->y.computed)
 
290
                             * transform );
 
291
        sp_item_invoke_bbox_full(child, bbox, ct, flags, FALSE);
 
292
    }
 
293
}
 
294
 
 
295
static void
 
296
sp_use_print(SPItem *item, SPPrintContext *ctx)
 
297
{
 
298
    bool translated = false;
 
299
    SPUse *use = SP_USE(item);
 
300
 
 
301
    if ((use->x._set && use->x.computed != 0) || (use->y._set && use->y.computed != 0)) {
 
302
        Geom::Matrix tp(Geom::Translate(use->x.computed, use->y.computed));
 
303
        sp_print_bind(ctx, tp, 1.0);
 
304
        translated = true;
 
305
    }
 
306
 
 
307
    if (use->child && SP_IS_ITEM(use->child)) {
 
308
        sp_item_invoke_print(SP_ITEM(use->child), ctx);
 
309
    }
 
310
 
 
311
    if (translated) {
 
312
        sp_print_release(ctx);
 
313
    }
 
314
}
 
315
 
 
316
static gchar *
 
317
sp_use_description(SPItem *item)
 
318
{
 
319
    SPUse *use = SP_USE(item);
 
320
 
 
321
    char *ret;
 
322
    if (use->child) {
 
323
        static unsigned recursion_depth = 0;
 
324
        if (recursion_depth >= 4) {
 
325
            /* TRANSLATORS: Used for statusbar description for long <use> chains:
 
326
             * "Clone of: Clone of: ... in Layer 1". */
 
327
            return g_strdup(_("..."));
 
328
            /* We could do better, e.g. chasing the href chain until we reach something other than
 
329
             * a <use>, and giving its description. */
 
330
        }
 
331
        ++recursion_depth;
 
332
        char *child_desc = sp_item_description(SP_ITEM(use->child));
 
333
        --recursion_depth;
 
334
 
 
335
        ret = g_strdup_printf(_("<b>Clone</b> of: %s"), child_desc);
 
336
        g_free(child_desc);
 
337
        return ret;
 
338
    } else {
 
339
        return g_strdup(_("<b>Orphaned clone</b>"));
 
340
    }
 
341
}
 
342
 
 
343
static NRArenaItem *
 
344
sp_use_show(SPItem *item, NRArena *arena, unsigned key, unsigned flags)
 
345
{
 
346
    SPUse *use = SP_USE(item);
 
347
 
 
348
    NRArenaItem *ai = NRArenaGroup::create(arena);
 
349
    nr_arena_group_set_transparent(NR_ARENA_GROUP(ai), FALSE);
 
350
    nr_arena_group_set_style(NR_ARENA_GROUP(ai), SP_OBJECT_STYLE(item));
 
351
 
 
352
    if (use->child) {
 
353
        NRArenaItem *ac = sp_item_invoke_show(SP_ITEM(use->child), arena, key, flags);
 
354
        if (ac) {
 
355
            nr_arena_item_add_child(ai, ac, NULL);
 
356
        }
 
357
        Geom::Translate t(use->x.computed,
 
358
                        use->y.computed);
 
359
        nr_arena_group_set_child_transform(NR_ARENA_GROUP(ai), Geom::Matrix(t));
 
360
    }
 
361
 
 
362
    return ai;
 
363
}
 
364
 
 
365
static void
 
366
sp_use_hide(SPItem *item, unsigned key)
 
367
{
 
368
    SPUse *use = SP_USE(item);
 
369
 
 
370
    if (use->child) {
 
371
        sp_item_invoke_hide(SP_ITEM(use->child), key);
 
372
    }
 
373
 
 
374
    if (((SPItemClass *) parent_class)->hide) {
 
375
        ((SPItemClass *) parent_class)->hide(item, key);
 
376
    }
 
377
}
 
378
 
 
379
/**
 
380
 * Returns the ultimate original of a SPUse (i.e. the first object in the chain of its originals
 
381
 * which is not an SPUse). If no original is found, NULL is returned (it is the responsibility
 
382
 * of the caller to make sure that this is handled correctly).
 
383
 *
 
384
 * Note that the returned is the clone object, i.e. the child of an SPUse (of the argument one for
 
385
 * the trivial case) and not the "true original".
 
386
 */
 
387
SPItem *
 
388
sp_use_root(SPUse *use)
 
389
{
 
390
    SPObject *orig = use->child;
 
391
    while (orig && SP_IS_USE(orig)) {
 
392
        orig = SP_USE(orig)->child;
 
393
    }
 
394
    if (!orig || !SP_IS_ITEM(orig))
 
395
        return NULL;
 
396
    return SP_ITEM(orig);
 
397
}
 
398
 
 
399
/**
 
400
 * Returns the effective transform that goes from the ultimate original to given SPUse, both ends
 
401
 * included.
 
402
 */
 
403
Geom::Matrix
 
404
sp_use_get_root_transform(SPUse *use)
 
405
{
 
406
    //track the ultimate source of a chain of uses
 
407
    SPObject *orig = use->child;
 
408
    GSList *chain = NULL;
 
409
    chain = g_slist_prepend(chain, use);
 
410
    while (SP_IS_USE(orig)) {
 
411
        chain = g_slist_prepend(chain, orig);
 
412
        orig = SP_USE(orig)->child;
 
413
    }
 
414
    chain = g_slist_prepend(chain, orig);
 
415
 
 
416
 
 
417
    //calculate the accummulated transform, starting from the original
 
418
    Geom::Matrix t(Geom::identity());
 
419
    for (GSList *i = chain; i != NULL; i = i->next) {
 
420
        SPItem *i_tem = SP_ITEM(i->data);
 
421
 
 
422
        // "An additional transformation translate(x,y) is appended to the end (i.e.,
 
423
        // right-side) of the transform attribute on the generated 'g', where x and y
 
424
        // represent the values of the x and y attributes on the 'use' element." - http://www.w3.org/TR/SVG11/struct.html#UseElement
 
425
        if (SP_IS_USE(i_tem)) {
 
426
            SPUse *i_use = SP_USE(i_tem);
 
427
            if ((i_use->x._set && i_use->x.computed != 0) || (i_use->y._set && i_use->y.computed != 0)) {
 
428
                t = t * Geom::Translate(i_use->x._set ? i_use->x.computed : 0, i_use->y._set ? i_use->y.computed : 0);
 
429
            }
 
430
        }
 
431
 
 
432
        t *= i_tem->transform;
 
433
    }
 
434
 
 
435
    g_slist_free(chain);
 
436
    return t;
 
437
}
 
438
 
 
439
/**
 
440
 * Returns the transform that leads to the use from its immediate original.
 
441
 * Does not inlcude the original's transform if any.
 
442
 */
 
443
Geom::Matrix
 
444
sp_use_get_parent_transform(SPUse *use)
 
445
{
 
446
    Geom::Matrix t(Geom::identity());
 
447
    if ((use->x._set && use->x.computed != 0) || (use->y._set && use->y.computed != 0)) {
 
448
        t *= Geom::Translate(use->x._set ? use->x.computed : 0,
 
449
                           use->y._set ? use->y.computed : 0);
 
450
    }
 
451
 
 
452
    t *= SP_ITEM(use)->transform;
 
453
    return t;
 
454
}
 
455
 
 
456
/**
 
457
 * Sensing a movement of the original, this function attempts to compensate for it in such a way
 
458
 * that the clone stays unmoved or moves in parallel (depending on user setting) regardless of the
 
459
 * clone's transform.
 
460
 */
 
461
static void
 
462
sp_use_move_compensate(Geom::Matrix const *mp, SPItem */*original*/, SPUse *self)
 
463
{
 
464
    // the clone is orphaned; or this is not a real use, but a clone of another use;
 
465
    // we skip it, otherwise duplicate compensation will occur
 
466
    if (SP_OBJECT_IS_CLONED(self)) {
 
467
        return;
 
468
    }
 
469
 
 
470
    // never compensate uses which are used in flowtext
 
471
    if (SP_OBJECT_PARENT(self) && SP_IS_FLOWREGION(SP_OBJECT_PARENT(self))) {
 
472
        return;
 
473
    }
 
474
 
 
475
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
476
    guint mode = prefs->getInt("/options/clonecompensation/value", SP_CLONE_COMPENSATION_PARALLEL);
 
477
    // user wants no compensation
 
478
    if (mode == SP_CLONE_COMPENSATION_NONE)
 
479
        return;
 
480
 
 
481
    Geom::Matrix m(*mp);
 
482
 
 
483
    // this is not a simple move, do not try to compensate
 
484
    if (!(m.isTranslation()))
 
485
        return;
 
486
 
 
487
    // restore item->transform field from the repr, in case it was changed by seltrans
 
488
    sp_object_read_attr (SP_OBJECT (self), "transform");
 
489
 
 
490
    Geom::Matrix t = sp_use_get_parent_transform(self);
 
491
    Geom::Matrix clone_move = t.inverse() * m * t;
 
492
 
 
493
    // calculate the compensation matrix and the advertized movement matrix
 
494
    Geom::Matrix advertized_move;
 
495
    if (mode == SP_CLONE_COMPENSATION_PARALLEL) {
 
496
        clone_move = clone_move.inverse() * m;
 
497
        advertized_move = m;
 
498
    } else if (mode == SP_CLONE_COMPENSATION_UNMOVED) {
 
499
        clone_move = clone_move.inverse();
 
500
        advertized_move.setIdentity();
 
501
    } else {
 
502
        g_assert_not_reached();
 
503
    }
 
504
 
 
505
    // commit the compensation
 
506
    SPItem *item = SP_ITEM(self);
 
507
    item->transform *= clone_move;
 
508
    sp_item_write_transform(item, SP_OBJECT_REPR(item), item->transform, &advertized_move);
 
509
    SP_OBJECT(item)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
 
510
}
 
511
 
 
512
static void
 
513
sp_use_href_changed(SPObject */*old_ref*/, SPObject */*ref*/, SPUse *use)
 
514
{
 
515
    SPItem *item = SP_ITEM(use);
 
516
 
 
517
    use->_delete_connection.disconnect();
 
518
    use->_transformed_connection.disconnect();
 
519
 
 
520
    if (use->child) {
 
521
        sp_object_detach(SP_OBJECT(use), use->child);
 
522
        use->child = NULL;
 
523
    }
 
524
 
 
525
    if (use->href) {
 
526
        SPItem *refobj = use->ref->getObject();
 
527
        if (refobj) {
 
528
            Inkscape::XML::Node *childrepr = SP_OBJECT_REPR(refobj);
 
529
            GType type = sp_repr_type_lookup(childrepr);
 
530
            g_return_if_fail(type > G_TYPE_NONE);
 
531
            if (g_type_is_a(type, SP_TYPE_ITEM)) {
 
532
                use->child = (SPObject*) g_object_new(type, 0);
 
533
                sp_object_attach(SP_OBJECT(use), use->child, use->lastChild());
 
534
                sp_object_unref(use->child, SP_OBJECT(use));
 
535
                sp_object_invoke_build(use->child, SP_OBJECT(use)->document, childrepr, TRUE);
 
536
 
 
537
                for (SPItemView *v = item->display; v != NULL; v = v->next) {
 
538
                    NRArenaItem *ai;
 
539
                    ai = sp_item_invoke_show(SP_ITEM(use->child), NR_ARENA_ITEM_ARENA(v->arenaitem), v->key, v->flags);
 
540
                    if (ai) {
 
541
                        nr_arena_item_add_child(v->arenaitem, ai, NULL);
 
542
                    }
 
543
                }
 
544
 
 
545
            }
 
546
            use->_delete_connection = SP_OBJECT(refobj)->connectDelete(sigc::bind(sigc::ptr_fun(&sp_use_delete_self), use));
 
547
            use->_transformed_connection = SP_ITEM(refobj)->connectTransformed(sigc::bind(sigc::ptr_fun(&sp_use_move_compensate), use));
 
548
        }
 
549
    }
 
550
}
 
551
 
 
552
static void
 
553
sp_use_delete_self(SPObject */*deleted*/, SPUse *self)
 
554
{
 
555
    // always delete uses which are used in flowtext
 
556
    if (SP_OBJECT_PARENT(self) && SP_IS_FLOWREGION(SP_OBJECT_PARENT(self))) {
 
557
        SP_OBJECT(self)->deleteObject();
 
558
        return;
 
559
    }
 
560
 
 
561
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
562
    guint const mode = prefs->getInt("/options/cloneorphans/value",
 
563
                                               SP_CLONE_ORPHANS_UNLINK);
 
564
 
 
565
    if (mode == SP_CLONE_ORPHANS_UNLINK) {
 
566
        sp_use_unlink(self);
 
567
    } else if (mode == SP_CLONE_ORPHANS_DELETE) {
 
568
        SP_OBJECT(self)->deleteObject();
 
569
    }
 
570
}
 
571
 
 
572
static void
 
573
sp_use_update(SPObject *object, SPCtx *ctx, unsigned flags)
 
574
{
 
575
    SPItem *item = SP_ITEM(object);
 
576
    SPUse *use = SP_USE(object);
 
577
    SPItemCtx *ictx = (SPItemCtx *) ctx;
 
578
    SPItemCtx cctx = *ictx;
 
579
 
 
580
    if (((SPObjectClass *) (parent_class))->update)
 
581
        ((SPObjectClass *) (parent_class))->update(object, ctx, flags);
 
582
 
 
583
    if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
 
584
    flags &= SP_OBJECT_MODIFIED_CASCADE;
 
585
 
 
586
    if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
 
587
      for (SPItemView *v = SP_ITEM(object)->display; v != NULL; v = v->next) {
 
588
        nr_arena_group_set_style(NR_ARENA_GROUP(v->arenaitem), SP_OBJECT_STYLE(object));
 
589
      }
 
590
    }
 
591
 
 
592
    /* Set up child viewport */
 
593
    if (use->x.unit == SVGLength::PERCENT) {
 
594
        use->x.computed = use->x.value * (ictx->vp.x1 - ictx->vp.x0);
 
595
    }
 
596
    if (use->y.unit == SVGLength::PERCENT) {
 
597
        use->y.computed = use->y.value * (ictx->vp.y1 - ictx->vp.y0);
 
598
    }
 
599
    if (use->width.unit == SVGLength::PERCENT) {
 
600
        use->width.computed = use->width.value * (ictx->vp.x1 - ictx->vp.x0);
 
601
    }
 
602
    if (use->height.unit == SVGLength::PERCENT) {
 
603
        use->height.computed = use->height.value * (ictx->vp.y1 - ictx->vp.y0);
 
604
    }
 
605
    cctx.vp.x0 = 0.0;
 
606
    cctx.vp.y0 = 0.0;
 
607
    cctx.vp.x1 = use->width.computed;
 
608
    cctx.vp.y1 = use->height.computed;
 
609
    cctx.i2vp = Geom::identity();
 
610
    flags&=~SP_OBJECT_USER_MODIFIED_FLAG_B;
 
611
 
 
612
    if (use->child) {
 
613
        g_object_ref(G_OBJECT(use->child));
 
614
        if (flags || (use->child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
 
615
            if (SP_IS_ITEM(use->child)) {
 
616
                SPItem const &chi = *SP_ITEM(use->child);
 
617
                cctx.i2doc = chi.transform * ictx->i2doc;
 
618
                cctx.i2vp = chi.transform * ictx->i2vp;
 
619
                use->child->updateDisplay((SPCtx *)&cctx, flags);
 
620
            } else {
 
621
                use->child->updateDisplay(ctx, flags);
 
622
            }
 
623
        }
 
624
        g_object_unref(G_OBJECT(use->child));
 
625
    }
 
626
 
 
627
    /* As last step set additional transform of arena group */
 
628
    for (SPItemView *v = item->display; v != NULL; v = v->next) {
 
629
        Geom::Matrix t(Geom::Translate(use->x.computed, use->y.computed));
 
630
        nr_arena_group_set_child_transform(NR_ARENA_GROUP(v->arenaitem), t);
 
631
    }
 
632
}
 
633
 
 
634
static void
 
635
sp_use_modified(SPObject *object, guint flags)
 
636
{
 
637
    SPUse *use_obj = SP_USE(object);
 
638
 
 
639
    if (flags & SP_OBJECT_MODIFIED_FLAG) {
 
640
        flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
 
641
    }
 
642
    flags &= SP_OBJECT_MODIFIED_CASCADE;
 
643
 
 
644
    if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) {
 
645
      for (SPItemView *v = SP_ITEM(object)->display; v != NULL; v = v->next) {
 
646
        nr_arena_group_set_style(NR_ARENA_GROUP(v->arenaitem), SP_OBJECT_STYLE(object));
 
647
      }
 
648
    }
 
649
 
 
650
    SPObject *child = use_obj->child;
 
651
    if (child) {
 
652
        g_object_ref(G_OBJECT(child));
 
653
        if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
 
654
            child->emitModified(flags);
 
655
        }
 
656
        g_object_unref(G_OBJECT(child));
 
657
    }
 
658
}
 
659
 
 
660
SPItem *
 
661
sp_use_unlink(SPUse *use)
 
662
{
 
663
    if (!use) return NULL;
 
664
 
 
665
    Inkscape::XML::Node *repr = SP_OBJECT_REPR(use);
 
666
    if (!repr) return NULL;
 
667
 
 
668
    Inkscape::XML::Node *parent = sp_repr_parent(repr);
 
669
    SPDocument *document = SP_OBJECT(use)->document;
 
670
    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
 
671
 
 
672
    // Track the ultimate source of a chain of uses.
 
673
    SPItem *orig = sp_use_root(use);
 
674
    g_return_val_if_fail(orig, NULL);
 
675
 
 
676
    // Calculate the accumulated transform, starting from the original.
 
677
    Geom::Matrix t = sp_use_get_root_transform(use);
 
678
 
 
679
    Inkscape::XML::Node *copy = NULL;
 
680
    if (SP_IS_SYMBOL(orig)) { // make a group, copy children
 
681
        copy = xml_doc->createElement("svg:g");
 
682
        for (Inkscape::XML::Node *child = SP_OBJECT_REPR(orig)->firstChild() ; child != NULL; child = child->next()) {
 
683
                Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
 
684
                copy->appendChild(newchild);
 
685
        }
 
686
    } else { // just copy
 
687
        copy = SP_OBJECT_REPR(orig)->duplicate(xml_doc);
 
688
    }
 
689
 
 
690
    // Add the duplicate repr just after the existing one.
 
691
    parent->addChild(copy, repr);
 
692
 
 
693
    // Retrieve the SPItem of the resulting repr.
 
694
    SPObject *unlinked = document->getObjectByRepr(copy);
 
695
 
 
696
    // Merge style from the use.
 
697
    SPStyle *unli_sty = SP_OBJECT_STYLE(unlinked);
 
698
    SPStyle const *use_sty = SP_OBJECT_STYLE(use);
 
699
    sp_style_merge_from_dying_parent(unli_sty, use_sty);
 
700
    sp_style_merge_from_parent(unli_sty, unlinked->parent->style);
 
701
 
 
702
    SP_OBJECT(unlinked)->updateRepr();
 
703
 
 
704
    // Hold onto our SPObject and repr for now.
 
705
    sp_object_ref(SP_OBJECT(use), NULL);
 
706
    Inkscape::GC::anchor(repr);
 
707
 
 
708
    // Remove ourselves, not propagating delete events to avoid a
 
709
    // chain-reaction with other elements that might reference us.
 
710
    SP_OBJECT(use)->deleteObject(false);
 
711
 
 
712
    // Give the copy our old id and let go of our old repr.
 
713
    copy->setAttribute("id", repr->attribute("id"));
 
714
    Inkscape::GC::release(repr);
 
715
 
 
716
    // Remove tiled clone attrs.
 
717
    copy->setAttribute("inkscape:tiled-clone-of", NULL);
 
718
    copy->setAttribute("inkscape:tile-w", NULL);
 
719
    copy->setAttribute("inkscape:tile-h", NULL);
 
720
    copy->setAttribute("inkscape:tile-cx", NULL);
 
721
    copy->setAttribute("inkscape:tile-cy", NULL);
 
722
 
 
723
    // Establish the succession and let go of our object.
 
724
    SP_OBJECT(use)->setSuccessor(unlinked);
 
725
    sp_object_unref(SP_OBJECT(use), NULL);
 
726
 
 
727
    SPItem *item = SP_ITEM(unlinked);
 
728
    // Set the accummulated transform.
 
729
    {
 
730
        Geom::Matrix nomove(Geom::identity());
 
731
        // Advertise ourselves as not moving.
 
732
        sp_item_write_transform(item, SP_OBJECT_REPR(item), t, &nomove);
 
733
    }
 
734
    return item;
 
735
}
 
736
 
 
737
SPItem *
 
738
sp_use_get_original(SPUse *use)
 
739
{
 
740
    SPItem *ref = use->ref->getObject();
 
741
    return ref;
 
742
}
 
743
 
 
744
static void
 
745
sp_use_snappoints(SPItem const *item, bool const target, SnapPointsWithType &p, Inkscape::SnapPreferences const *snapprefs)
 
746
{
 
747
    g_assert (item != NULL);
 
748
    g_assert (SP_IS_ITEM(item));
 
749
    g_assert (SP_IS_USE(item));
 
750
 
 
751
    SPUse *use = SP_USE(item);
 
752
    SPItem *root = sp_use_root(use);
 
753
    if (!root)
 
754
        return;
 
755
 
 
756
    SPItemClass const &item_class = *(SPItemClass const *) G_OBJECT_GET_CLASS(root);
 
757
    if (item_class.snappoints) {
 
758
        item_class.snappoints(root, target, p, snapprefs);
 
759
    }
 
760
}
 
761
 
 
762
 
 
763
/*
 
764
  Local Variables:
 
765
  mode:c++
 
766
  c-file-style:"stroustrup"
 
767
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
768
  indent-tabs-mode:nil
 
769
  fill-column:99
 
770
  End:
 
771
*/
 
772
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :