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

« back to all changes in this revision

Viewing changes to src/document.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:
38
38
# include "config.h"
39
39
#endif
40
40
#include <gtk/gtkmain.h>
 
41
#include <string>
 
42
#include <cstring>
41
43
#include "application/application.h"
42
44
#include "application/editor.h"
43
45
#include "libnr/nr-matrix-fns.h"
53
55
#include "libavoid/router.h"
54
56
#include "libnr/nr-rect.h"
55
57
#include "sp-item-group.h"
 
58
#include "profile-manager.h"
 
59
#include "persp3d.h"
56
60
 
57
61
#include "display/nr-arena-item.h"
58
62
 
59
63
#include "dialogs/rdf.h"
60
64
 
 
65
#include "transf_mat_3x4.h"
 
66
 
61
67
#define A4_WIDTH_STR "210mm"
62
68
#define A4_HEIGHT_STR "297mm"
63
69
 
70
76
 
71
77
static gint doc_count = 0;
72
78
 
 
79
static unsigned long next_serial = 0;
 
80
 
73
81
SPDocument::SPDocument() {
74
82
    SPDocumentPrivate *p;
75
83
 
91
99
 
92
100
    // Initialise instance of connector router.
93
101
    router = new Avoid::Router();
 
102
    // Don't use the Consolidate moves optimisation.
 
103
    router->ConsolidateMoves = false;
 
104
 
 
105
    perspectives = NULL;
94
106
 
95
107
    p = new SPDocumentPrivate();
96
108
 
 
109
    p->serial = next_serial++;
 
110
 
97
111
    p->iddef = g_hash_table_new(g_direct_hash, g_direct_equal);
98
112
    p->reprdef = g_hash_table_new(g_direct_hash, g_direct_equal);
99
113
 
104
118
    p->history_size = 0;
105
119
    p->undo = NULL;
106
120
    p->redo = NULL;
 
121
    p->seeking = false;
107
122
 
108
123
    priv = p;
 
124
 
 
125
    // Once things are set, hook in the manager
 
126
    profileManager = new Inkscape::ProfileManager(this);
 
127
 
 
128
    // XXX only for testing!
 
129
    priv->undoStackObservers.add(p->console_output_undo_observer);
109
130
}
110
131
 
111
132
SPDocument::~SPDocument() {
112
133
    collectOrphans();
113
134
 
 
135
    // kill/unhook this first
 
136
    if ( profileManager ) {
 
137
        delete profileManager;
 
138
        profileManager = 0;
 
139
    }
 
140
 
114
141
    if (priv) {
115
 
        inkscape_remove_document(this);
116
 
 
117
142
        if (priv->partial) {
118
143
            sp_repr_free_log(priv->partial);
119
144
            priv->partial = NULL;
123
148
        sp_document_clear_undo(this);
124
149
 
125
150
        if (root) {
126
 
            sp_object_invoke_release(root);
 
151
            root->releaseReferences();
127
152
            sp_object_unref(root);
128
153
            root = NULL;
129
154
        }
176
201
    }
177
202
 
178
203
    //delete this->_whiteboard_session_manager;
 
204
 
 
205
}
 
206
 
 
207
void SPDocument::add_persp3d (Persp3D * const persp)
 
208
{
 
209
    SPDefs *defs = SP_ROOT(this->root)->defs;
 
210
    for (SPObject *i = sp_object_first_child(SP_OBJECT(defs)); i != NULL; i = SP_OBJECT_NEXT(i) ) {
 
211
        if (SP_IS_PERSP3D(i)) {
 
212
            g_print ("Encountered a Persp3D in defs\n");
 
213
        }
 
214
    }
 
215
 
 
216
    g_print ("Adding Persp3D to defs\n");
 
217
    persp3d_create_xml_element (this);
 
218
}
 
219
 
 
220
void SPDocument::remove_persp3d (Persp3D * const persp)
 
221
{
 
222
    // TODO: Delete the repr, maybe perform a check if any boxes are still linked to the perspective.
 
223
    //       Anything else?
 
224
    g_print ("Please implement deletion of perspectives here.\n");
 
225
}
 
226
 
 
227
unsigned long SPDocument::serial() const {
 
228
    return priv->serial;
179
229
}
180
230
 
181
231
void SPDocument::queueForOrphanCollection(SPObject *object) {
199
249
    }
200
250
}
201
251
 
202
 
void SPDocument::reset_key (void *dummy)
 
252
void SPDocument::reset_key (void */*dummy*/)
203
253
{
204
254
    actionkey = NULL;
205
255
}
206
256
 
207
 
static SPDocument *
 
257
SPDocument *
208
258
sp_document_create(Inkscape::XML::Document *rdoc,
209
259
                   gchar const *uri,
210
260
                   gchar const *base,
215
265
    Inkscape::XML::Node *rroot;
216
266
    Inkscape::Version sodipodi_version;
217
267
 
218
 
    rroot = sp_repr_document_root(rdoc);
 
268
    rroot = rdoc->root();
219
269
 
220
270
    document = new SPDocument();
221
271
 
256
306
 
257
307
    /* Quick hack 3 - Set uri attributes */
258
308
    if (uri) {
259
 
        /* fixme: Think, what this means for images (Lauris) */
260
309
        rroot->setAttribute("sodipodi:docname", uri);
261
 
        if (document->base)
262
 
            rroot->setAttribute("sodipodi:docbase", document->base);
263
310
    }
264
311
    /* End of quick hack 3 */
265
312
 
272
319
        // see if there's a template with id="base" in the preferences
273
320
        if (!r) {
274
321
            // if there's none, create an empty element
275
 
            rnew = sp_repr_new("sodipodi:namedview");
 
322
            rnew = rdoc->createElement("sodipodi:namedview");
276
323
            rnew->setAttribute("id", "base");
277
324
        } else {
278
325
            // otherwise, take from preferences
279
 
            rnew = r->duplicate();
 
326
            rnew = r->duplicate(rroot->document());
280
327
        }
281
328
        // insert into the document
282
329
        rroot->addChild(rnew, NULL);
287
334
    /* Defs */
288
335
    if (!SP_ROOT(document->root)->defs) {
289
336
        Inkscape::XML::Node *r;
290
 
        r = sp_repr_new("svg:defs");
 
337
        r = rdoc->createElement("svg:defs");
291
338
        rroot->addChild(r, NULL);
292
339
        Inkscape::GC::release(r);
293
340
        g_assert(SP_ROOT(document->root)->defs);
300
347
        inkscape_ref();
301
348
    }
302
349
 
303
 
    sp_document_set_undo_sensitive(document, TRUE);
 
350
    // Remark: Here, we used to create a "currentpersp3d" element in the document defs.
 
351
    // But this is probably a bad idea since we need to adapt it for every change of selection, which will
 
352
    // completely clutter the undo history. Maybe rather save it to prefs on exit and re-read it on startup?
 
353
 
 
354
    document->current_persp3d = persp3d_document_first_persp(document);
 
355
    if (!document->current_persp3d) {
 
356
        document->current_persp3d = persp3d_create_xml_element (document);
 
357
    }
 
358
 
 
359
    sp_document_set_undo_sensitive(document, true);
304
360
 
305
361
    // reset undo key when selection changes, so that same-key actions on different objects are not coalesced
306
362
    if (!Inkscape::NSApplication::Application::getNewGui()) {
312
368
        document->_selection_changed_connection = Inkscape::NSApplication::Editor::connectSelectionChanged (sigc::mem_fun (*document, &SPDocument::reset_key));
313
369
        document->_desktop_activated_connection = Inkscape::NSApplication::Editor::connectDesktopActivated (sigc::mem_fun (*document, &SPDocument::reset_key));
314
370
    }
315
 
    inkscape_add_document(document);
316
371
 
317
372
    return document;
318
373
}
336
391
        rdoc = sp_repr_read_file(uri, SP_SVG_NS_URI);
337
392
        /* If file cannot be loaded, return NULL without warning */
338
393
        if (rdoc == NULL) return NULL;
339
 
        rroot = sp_repr_document_root(rdoc);
 
394
        rroot = rdoc->root();
340
395
        /* If xml file is not svg, return NULL without warning */
341
396
        /* fixme: destroy document */
342
397
        if (strcmp(rroot->name(), "svg:svg") != 0) return NULL;
385
440
    /* If it cannot be loaded, return NULL without warning */
386
441
    if (rdoc == NULL) return NULL;
387
442
 
388
 
    rroot = sp_repr_document_root(rdoc);
 
443
    rroot = rdoc->root();
389
444
    /* If xml file is not svg, return NULL without warning */
390
445
    /* fixme: destroy document */
391
446
    if (strcmp(rroot->name(), "svg:svg") != 0) return NULL;
397
452
    return doc;
398
453
}
399
454
 
400
 
SPDocument *sp_document_new_dummy() {
401
 
    SPDocument *document = new SPDocument();
402
 
    inkscape_add_document(document);
403
 
    return document;
404
 
}
405
 
 
406
455
SPDocument *
407
456
sp_document_ref(SPDocument *doc)
408
457
{
483
532
}
484
533
 
485
534
/**
486
 
 * Given an NRRect that may, for example, correspond to the bbox of an object
 
535
 * Given an NR::Rect that may, for example, correspond to the bbox of an object,
487
536
 * this function fits the canvas to that rect by resizing the canvas
488
537
 * and translating the document root into position.
489
538
 */
490
 
void SPDocument::fitToRect(NRRect const & rect)
 
539
void SPDocument::fitToRect(NR::Rect const &rect)
491
540
{
492
 
    g_return_if_fail(!empty(rect));
493
 
    
494
 
    gdouble w = rect.x1 - rect.x0;
495
 
    gdouble h = rect.y1 - rect.y0;
496
 
    gdouble old_height = sp_document_height(this);
497
 
    SPUnit unit = sp_unit_get_by_id(SP_UNIT_PX);
498
 
    sp_document_set_width(this, w, &unit);
499
 
    sp_document_set_height(this, h, &unit);
500
 
 
501
 
    NR::translate tr = NR::translate::translate(-rect.x0,-(rect.y0 + (h - old_height)));
502
 
    static_cast<SPGroup *>(root)->translateChildItems(tr);
 
541
    g_return_if_fail(!rect.isEmpty());
 
542
 
 
543
    using NR::X; using NR::Y;
 
544
    double const w = rect.extent(X);
 
545
    double const h = rect.extent(Y);
 
546
 
 
547
    double const old_height = sp_document_height(this);
 
548
    SPUnit const &px(sp_unit_get_by_id(SP_UNIT_PX));
 
549
    sp_document_set_width(this, w, &px);
 
550
    sp_document_set_height(this, h, &px);
 
551
 
 
552
    NR::translate const tr(NR::Point(0, (old_height - h))
 
553
                           - rect.min());
 
554
    SP_GROUP(root)->translateChildItems(tr);
503
555
}
504
556
 
505
557
void sp_document_set_uri(SPDocument *document, gchar const *uri)
541
593
    // Update saveable repr attributes.
542
594
    Inkscape::XML::Node *repr = sp_document_repr_root(document);
543
595
    // changing uri in the document repr must not be not undoable
544
 
    gboolean saved = sp_document_get_undo_sensitive(document);
545
 
    sp_document_set_undo_sensitive(document, FALSE);
546
 
    if (document->base)
547
 
        repr->setAttribute("sodipodi:docbase", document->base);
 
596
    bool saved = sp_document_get_undo_sensitive(document);
 
597
    sp_document_set_undo_sensitive(document, false);
548
598
 
549
599
    repr->setAttribute("sodipodi:docname", document->name);
550
600
    sp_document_set_undo_sensitive(document, saved);
738
788
    ctx->i2vp = NR::identity();
739
789
}
740
790
 
 
791
/**
 
792
 * Tries to update the document state based on the modified and 
 
793
 * "update required" flags, and return true if the document has
 
794
 * been brought fully up to date.
 
795
 */
 
796
bool
 
797
SPDocument::_updateDocument()
 
798
{
 
799
    /* Process updates */
 
800
    if (this->root->uflags || this->root->mflags) {
 
801
        if (this->root->uflags) {
 
802
            SPItemCtx ctx;
 
803
            sp_document_setup_viewport (this, &ctx);
 
804
 
 
805
            bool saved = sp_document_get_undo_sensitive(this);
 
806
            sp_document_set_undo_sensitive(this, false);
 
807
 
 
808
            this->root->updateDisplay((SPCtx *)&ctx, 0);
 
809
 
 
810
            sp_document_set_undo_sensitive(this, saved);
 
811
        }
 
812
        this->_emitModified();
 
813
    }
 
814
 
 
815
    return !(this->root->uflags || this->root->mflags);
 
816
}
 
817
 
 
818
 
 
819
/**
 
820
 * Repeatedly works on getting the document updated, since sometimes
 
821
 * it takes more than one pass to get the document updated.  But it
 
822
 * usually should not take more than a few loops, and certainly never
 
823
 * more than 32 iterations.  So we bail out if we hit 32 iterations,
 
824
 * since this typically indicates we're stuck in an update loop.
 
825
 */
741
826
gint
742
827
sp_document_ensure_up_to_date(SPDocument *doc)
743
828
{
744
 
    int lc;
745
 
    lc = 32;
746
 
    while (doc->root->uflags || doc->root->mflags) {
747
 
        lc -= 1;
748
 
        if (lc < 0) {
749
 
            g_warning("More than 32 iterations while updating document '%s'", doc->uri);
750
 
            if (doc->modified_id) {
751
 
                /* Remove handler */
752
 
                gtk_idle_remove(doc->modified_id);
753
 
                doc->modified_id = 0;
754
 
            }
755
 
            return FALSE;
756
 
        }
757
 
        /* Process updates */
758
 
        if (doc->root->uflags) {
759
 
            SPItemCtx ctx;
760
 
            sp_document_setup_viewport (doc, &ctx);
761
 
            doc->root->updateDisplay((SPCtx *)&ctx, 0);
762
 
        }
763
 
        doc->_emitModified();
 
829
    int counter = 32;
 
830
    while (!doc->_updateDocument()) {
 
831
        if (counter == 0) {
 
832
            g_warning("More than 32 iteration while updating document '%s'", doc->uri);
 
833
            break;
 
834
        }
 
835
        counter--;
764
836
    }
 
837
 
765
838
    if (doc->modified_id) {
766
839
        /* Remove handler */
767
840
        gtk_idle_remove(doc->modified_id);
768
841
        doc->modified_id = 0;
769
842
    }
770
 
    return TRUE;
 
843
    return counter>0;
771
844
}
772
845
 
 
846
/**
 
847
 * An idle handler to update the document.  Returns true if
 
848
 * the document needs further updates.
 
849
 */
773
850
static gint
774
851
sp_document_idle_handler(gpointer data)
775
852
{
776
 
    SPDocument *doc;
777
 
    int repeat;
778
 
 
779
 
    doc = static_cast<SPDocument *>(data);
780
 
 
781
 
#ifdef SP_DOCUMENT_DEBUG_IDLE
782
 
    g_print("->\n");
783
 
#endif
784
 
 
785
 
    /* Process updates */
786
 
    if (doc->root->uflags) {
787
 
        SPItemCtx ctx;
788
 
        sp_document_setup_viewport (doc, &ctx);
789
 
 
790
 
        gboolean saved = sp_document_get_undo_sensitive(doc);
791
 
        sp_document_set_undo_sensitive(doc, FALSE);
792
 
 
793
 
        doc->root->updateDisplay((SPCtx *)&ctx, 0);
794
 
 
795
 
        sp_document_set_undo_sensitive(doc, saved);
796
 
        /* if (doc->root->uflags & SP_OBJECT_MODIFIED_FLAG) return TRUE; */
 
853
    SPDocument *doc = static_cast<SPDocument *>(data);
 
854
    if (doc->_updateDocument()) {
 
855
        doc->modified_id = 0;
 
856
        return false;
 
857
    } else {
 
858
        return true;
797
859
    }
798
 
 
799
 
    doc->_emitModified();
800
 
 
801
 
    repeat = (doc->root->uflags || doc->root->mflags);
802
 
    if (!repeat) doc->modified_id = 0;
803
 
    return repeat;
804
860
}
805
861
 
806
862
static bool is_within(NR::Rect const &area, NR::Rect const &box)
826
882
            s = find_items_in_area(s, SP_GROUP(o), dkey, area, test);
827
883
        } else {
828
884
            SPItem *child = SP_ITEM(o);
829
 
            NR::Rect box = sp_item_bbox_desktop(child);
830
 
            if (test(area, box) && (take_insensitive || child->isVisibleAndUnlocked(dkey))) {
 
885
            NR::Maybe<NR::Rect> box = sp_item_bbox_desktop(child);
 
886
            if ( box && test(area, *box) && (take_insensitive || child->isVisibleAndUnlocked(dkey))) {
831
887
                s = g_slist_append(s, child);
832
888
            }
833
889
        }
993
1049
    return find_items_in_area(NULL, SP_GROUP(document->root), dkey, box, overlaps);
994
1050
}
995
1051
 
 
1052
GSList *
 
1053
sp_document_items_at_points(SPDocument *document, unsigned const key, std::vector<NR::Point> points)
 
1054
{
 
1055
    GSList *items = NULL;
 
1056
 
 
1057
    // When picking along the path, we don't want small objects close together 
 
1058
    // (such as hatching strokes) to obscure each other by their deltas, 
 
1059
    // so we temporarily set delta to a small value
 
1060
    gdouble saved_delta = prefs_get_double_attribute ("options.cursortolerance", "value", 1.0);
 
1061
    prefs_set_double_attribute ("options.cursortolerance", "value", 0.25);
 
1062
 
 
1063
    for(unsigned int i = 0; i < points.size(); i++) {
 
1064
        SPItem *item = sp_document_item_at_point(document, key, points[i],
 
1065
                                         false, NULL);
 
1066
        if (item && !g_slist_find(items, item))
 
1067
            items = g_slist_prepend (items, item);
 
1068
    }
 
1069
 
 
1070
    // and now we restore it back
 
1071
    prefs_set_double_attribute ("options.cursortolerance", "value", saved_delta);
 
1072
 
 
1073
    return items;
 
1074
}
 
1075
 
996
1076
SPItem *
997
1077
sp_document_item_at_point(SPDocument *document, unsigned const key, NR::Point const p,
998
1078
                          gboolean const into_groups, SPItem *upto)
1087
1167
/* Helpers */
1088
1168
 
1089
1169
gboolean
1090
 
sp_document_resource_list_free(gpointer key, gpointer value, gpointer data)
 
1170
sp_document_resource_list_free(gpointer /*key*/, gpointer value, gpointer /*data*/)
1091
1171
{
1092
1172
    g_slist_free((GSList *) value);
1093
1173
    return TRUE;
1149
1229
    return start - newend;
1150
1230
}
1151
1231
 
 
1232
bool SPDocument::isSeeking() const {
 
1233
    return priv->seeking;
 
1234
}
 
1235
 
1152
1236
 
1153
1237
/*
1154
1238
  Local Variables: