1 // SPDX-License-Identifier: GPL-2.0-or-later
2 #ifndef SEEN_SP_DOCUMENT_H
3 #define SEEN_SP_DOCUMENT_H
4 
5 /** \file
6  * SPDocument: Typed SVG document implementation
7  */
8 /* Authors:
9  *   Lauris Kaplinski <lauris@kaplinski.com>
10  *   MenTaLguY <mental@rydia.net>
11  *   Jon A. Cruz <jon@joncruz.org>
12  *   Abhishek Sharma
13  *
14  * Copyright (C) 2004-2005 MenTaLguY
15  * Copyright (C) 1999-2002 Lauris Kaplinski
16  * Copyright (C) 2000-2001 Ximian, Inc.
17  *
18  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
19  */
20 
21 
22 #include <cstddef>
23 #include <deque>
24 #include <map>
25 #include <memory>
26 #include <vector>
27 
28 #include <boost/ptr_container/ptr_list.hpp>
29 
30 #include <glibmm/ustring.h>
31 #include <giomm/simpleactiongroup.h>
32 #include <sigc++/sigc++.h>
33 
34 #include <2geom/affine.h>
35 #include <2geom/forward.h>
36 
37 #include "3rdparty/libcroco/cr-cascade.h"
38 
39 #include "document-undo.h"
40 #include "event.h"
41 #include "gc-anchored.h"
42 #include "gc-finalized.h"
43 #include "object/sp-namedview.h"
44 
45 #include "inkgc/gc-managed.h"
46 
47 #include "composite-undo-stack-observer.h"
48 // XXX only for testing!
49 #include "console-output-undo-observer.h"
50 
51 // This variable is introduced with 0.92.1
52 // with the introduction of automatic fix
53 // for files detected to have been created
54 // with previous versions to have a similar
55 // look in 0.92+.
56 extern bool sp_no_convert_text_baseline_spacing;
57 
58 
59 
60 // This variable is introduced with 0.92.1
61 // with the introduction of automatic fix
62 // for files detected to have been created
63 // with previous versions to have a similar
64 // look in 0.92+.
65 extern bool sp_do_not_fix_pre_92;
66 
67 
68 
69 namespace Avoid {
70 class Router;
71 }
72 
73 class SPItem;
74 class SPObject;
75 class SPGroup;
76 class SPRoot;
77 
78 namespace Inkscape {
79     class Selection;
80     class UndoStackObserver;
81     class EventLog;
82     class ProfileManager;
83     namespace XML {
84         struct Document;
85         class Node;
86     }
87     namespace Util {
88         class Unit;
89         class Quantity;
90     }
91 }
92 
93 class SPDefs;
94 class Persp3D;
95 class Persp3DImpl;
96 class SPItemCtx;
97 
98 namespace Proj {
99     class TransfMat3x4;
100 }
101 
102 /// Typed SVG document implementation.
103 class SPDocument : public Inkscape::GC::Managed<>,
104                    public Inkscape::GC::Finalized,
105                    public Inkscape::GC::Anchored
106 {
107 
108 public:
109     /// For sanity check in SPObject::requestDisplayUpdate
110     unsigned update_in_progress = 0;
111 
112     /************ Functions *****************/
113 
114     // Fundamental ------------------------
115     SPDocument();
116     ~SPDocument() override;
117     SPDocument(SPDocument const &) = delete; // no copy
118     void operator=(SPDocument const &) = delete; // no assign
119 
120 
121     // Document creation ------------------
122     static SPDocument *createDoc(Inkscape::XML::Document *rdoc, char const *uri,
123             char const *base, char const *name, bool keepalive,
124             SPDocument *parent);
125     static SPDocument *createNewDoc(char const*uri, bool keepalive,
126             bool make_new = false, SPDocument *parent=nullptr );
127     static SPDocument *createNewDocFromMem(char const*buffer, int length, bool keepalive);
128            SPDocument *createChildDoc(std::string const &uri);
129 
130     // Make a copy, you are responsible for the copy.
131     std::unique_ptr<SPDocument> copy() const;
132 
133     // Document status --------------------
setVirgin(bool Virgin)134     void setVirgin(bool Virgin) { virgin = Virgin; }
getVirgin()135     bool getVirgin() { return virgin; }
getOriginalDocument()136     const SPDocument *getOriginalDocument() const { return _original_document.get(); }
137 
138     //! Increment reference count by one and return a self-dereferencing pointer.
139     std::unique_ptr<SPDocument> doRef();
140     std::unique_ptr<SPDocument const> doRef() const;
141 
isModifiedSinceSave()142     bool isModifiedSinceSave() const { return modified_since_save; }
isModifiedSinceAutoSave()143     bool isModifiedSinceAutoSave() const { return modified_since_autosave; }
144     void setModifiedSinceSave(bool const modified = true);
setModifiedSinceAutoSaveFalse()145     void setModifiedSinceAutoSaveFalse() { modified_since_autosave = false; };
146 
147     bool idle_handler();
148     bool rerouting_handler();
149 
150     void requestModified();
151     bool _updateDocument(int flags); // Used by stand-alone sp_document_idle_handler
152     int ensureUpToDate();
153 
154     bool addResource(char const *key, SPObject *object);
155     bool removeResource(char const *key, SPObject *object);
156     std::vector<SPObject *> const getResourceList(char const *key);
157 
158     void do_change_uri(char const *const filename, bool const rebase);
159     void changeUriAndHrefs(char const *uri);
setXMLDialogSelectedObject(SPObject * activexmltree)160     void setXMLDialogSelectedObject(SPObject *activexmltree) { _activexmltree = activexmltree; }
getXMLDialogSelectedObject()161     SPObject *getXMLDialogSelectedObject() { return _activexmltree; }
162 
163   private:
164     void _importDefsNode(SPDocument *source, Inkscape::XML::Node *defs, Inkscape::XML::Node *target_defs);
165     SPObject *_activexmltree;
166 
167   public:
168     void importDefs(SPDocument *source);
169 
170     unsigned int vacuumDocument();
171 
172     /******** Getters and Setters **********/
173 
174     // Document structure -----------------
getProfileManager()175     Inkscape::ProfileManager* getProfileManager() const { return profileManager; }
getRouter()176     Avoid::Router* getRouter() const { return router; }
177 
178 
179     /** Returns our SPRoot */
getRoot()180     SPRoot *getRoot() { return root; }
getRoot()181     SPRoot const *getRoot() const { return root; }
182 
183     /** Return the main defs object for the document. */
184     SPDefs *getDefs();
185 
getReprRoot()186     Inkscape::XML::Node *getReprRoot() { return rroot; }
187     Inkscape::XML::Node *getReprNamedView();
188     SPNamedView *getNamedView();
189 
190     /** Our Inkscape::XML::Document. */
getReprDoc()191     Inkscape::XML::Document *getReprDoc() { return rdoc; }
getReprDoc()192     Inkscape::XML::Document const *getReprDoc() const { return rdoc; }
193 
194 
195     std::vector<Glib::ustring> getLanguages() const;
196 
197     // Styling
getStyleCascade()198     CRCascade    *getStyleCascade() { return style_cascade; }
199 
200     // File information --------------------
201 
202     /** A filename (not a URI yet), or NULL */
203     void setDocumentUri(char const *document_uri);
getDocumentURI()204     char const *getDocumentURI() const { return document_uri; }
205 
206     /** To be used for resolving relative hrefs. */
207     void setDocumentBase( char const* document_base );
getDocumentBase()208     char const *getDocumentBase() const { return document_base; };
209 
210     /** basename(uri) or other human-readable label for the document. */
getDocumentName()211     char const* getDocumentName() const { return document_name; }
212 
213 
214     // Document geometry ------------------------
215     Inkscape::Util::Unit const* getDisplayUnit() const;
216 
217     void setDocumentScale( const double scaleX, const double scaleY );
218     void setDocumentScale( const double scale );
219     Geom::Scale getDocumentScale() const;
220 
221     void setWidthAndHeight(const Inkscape::Util::Quantity &width, const Inkscape::Util::Quantity &height, bool changeSize=true);
222     Geom::Point getDimensions() const;
223 
224     void setWidth(const Inkscape::Util::Quantity &width, bool changeSize=true);
225     void setHeight(const Inkscape::Util::Quantity &height, bool changeSize=true);
226     Inkscape::Util::Quantity getWidth() const;
227     Inkscape::Util::Quantity getHeight() const;
228 
229     void setViewBox();
230     void setViewBox(const Geom::Rect &viewBox);
231     Geom::Rect getViewBox() const;
232 
233     Geom::OptRect preferredBounds() const;
234     void fitToRect(Geom::Rect const &rect, bool with_margins = false);
235     void setupViewport(SPItemCtx *ctx);
236 
237 
238     // Find items -----------------------------
239     void bindObjectToId(char const *id, SPObject *object);
240     void enforceObjectIds();
241     SPObject *getObjectById(Glib::ustring const &id) const;
242     SPObject *getObjectById(char const *id) const;
243 
244     void bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object);
245     SPObject *getObjectByRepr(Inkscape::XML::Node *repr) const;
246 
247     std::vector<SPObject *> getObjectsByClass(Glib::ustring const &klass) const;
248     std::vector<SPObject *> getObjectsByElement(Glib::ustring const &element) const;
249     std::vector<SPObject *> getObjectsBySelector(Glib::ustring const &selector) const;
250 
251 
252     // Find items by geometry --------------------
253     void build_flat_item_list(unsigned int dkey, SPGroup *group, gboolean into_groups) const;
254 
255     std::vector<SPItem*> getItemsInBox         (unsigned int dkey, Geom::Rect const &box, bool take_hidden = false, bool take_insensitive = false, bool take_groups = true, bool enter_groups = false) const;
256     std::vector<SPItem*> getItemsPartiallyInBox(unsigned int dkey, Geom::Rect const &box, bool take_hidden = false, bool take_insensitive = false, bool take_groups = true, bool enter_groups = false) const;
257     SPItem *getItemAtPoint(unsigned int key, Geom::Point const &p, bool into_groups, SPItem *upto = nullptr) const;
258     std::vector<SPItem*> getItemsAtPoints(unsigned const key, std::vector<Geom::Point> points, bool all_layers = true, size_t limit = 0) const ;
259     SPItem *getGroupAtPoint(unsigned int key,  Geom::Point const &p) const;
260 
261     /**
262      * Returns the bottommost item from the list which is at the point, or NULL if none.
263      */
264     static SPItem *getItemFromListAtPointBottom(unsigned int dkey, SPGroup *group, const std::vector<SPItem*> &list, Geom::Point const &p, bool take_insensitive = false);
265 
266 
267     // Box tool -------------------------------
268     void setCurrentPersp3D(Persp3D * const persp);
269     /*
270      * getCurrentPersp3D returns current_persp3d (if non-NULL) or the first
271      * perspective in the defs. If no perspective exists, returns NULL.
272      */
273     Persp3D * getCurrentPersp3D();
274 
setCurrentPersp3DImpl(Persp3DImpl * const persp_impl)275     void setCurrentPersp3DImpl(Persp3DImpl * const persp_impl) { current_persp3d_impl = persp_impl; }
getCurrentPersp3DImpl()276     Persp3DImpl * getCurrentPersp3DImpl() { return current_persp3d_impl; }
277 
278     void getPerspectivesInDefs(std::vector<Persp3D*> &list) const;
numPerspectivesInDefs()279     unsigned int numPerspectivesInDefs() const {
280         std::vector<Persp3D*> list;
281         getPerspectivesInDefs(list);
282         return list.size();
283     }
284 
285 
286     // Document undo/redo ----------------------
serial()287     unsigned long serial() const { return _serial; }  // Returns document's unique number.
isSeeking()288     bool isSeeking() const {return seeking;} // In a transition between two "good" states of document?
reset_key(void * dummy)289     void reset_key(void *dummy) { actionkey.clear(); }
isSensitive()290     bool isSensitive() const { return sensitive; }
291 
292 
293     // Garbage collecting ----------------------
294     void queueForOrphanCollection(SPObject *object);
295     void collectOrphans();
296 
297 
298     // Actions ---------------------------------
getActionGroup()299     Glib::RefPtr<Gio::SimpleActionGroup> getActionGroup() { return action_group; }
300 
301     /************* Data ***************/
302 private:
303 
304     // Document ------------------------------
305     Inkscape::ProfileManager* profileManager;   // Color profile.
306     Avoid::Router *router; // Instance of the connector router
307 
308     // Document status -----------------------
309 
310     bool keepalive; ///< false if temporary document (e.g. to generate a PNG for display in a dialog).
311     bool virgin ;   ///< Has the document never been touched?
312     bool modified_since_save = false;
313     bool modified_since_autosave = false;
314     sigc::connection modified_connection;
315     sigc::connection rerouting_connection;
316 
317     // Document structure --------------------
318     Inkscape::XML::Document *rdoc; ///< Our Inkscape::XML::Document
319     Inkscape::XML::Node *rroot; ///< Root element of Inkscape::XML::Document
320 
321     SPRoot *root;             ///< Our SPRoot
322 
323     // A list of svg documents being used or shown within this document
324     boost::ptr_list<SPDocument> _child_documents;
325     // Conversely this is a parent document because this is a child.
326     SPDocument *_parent_document;
327     // When copying documents, this can refer to it's original
328     std::unique_ptr<SPDocument const> _original_document;
329 
330     // Styling
331     CRCascade *style_cascade;
332 
333     // File information ----------------------
334     char *document_uri;   ///< A filename (not a URI yet), or NULL
335     char *document_base;  ///< To be used for resolving relative hrefs.
336     char *document_name;  ///< basename(uri) or other human-readable label for the document.
337 
338     // Find items ----------------------------
339     std::map<std::string, SPObject *> iddef;
340     std::map<Inkscape::XML::Node *, SPObject *> reprdef;
341 
342     // Find items by geometry --------------------
343     mutable std::deque<SPItem*> _node_cache; // Used to speed up search.
344     mutable bool _node_cache_valid;
345 
346     // Box tool ----------------------------
347     Persp3D *current_persp3d; /**< Currently 'active' perspective (to which, e.g., newly created boxes are attached) */
348     Persp3DImpl *current_persp3d_impl;
349 
350     // Document undo/redo ----------------------
351     friend Inkscape::DocumentUndo;
352 
353     /* Undo/Redo state */
354     bool sensitive; /* If we save actions to undo stack */
355     Inkscape::XML::Event * partial; /* partial undo log when interrupted */
356     int history_size;
357     std::vector<Inkscape::Event *> undo; /* Undo stack of reprs */
358     std::vector<Inkscape::Event *> redo; /* Redo stack of reprs */
359 
360     /* Undo listener */
361     Inkscape::CompositeUndoStackObserver undoStackObservers;
362 
363     // XXX only for testing!
364     Inkscape::ConsoleOutputUndoObserver console_output_undo_observer;
365 
366     bool seeking; // Related to undo/redo/unique id
367     unsigned long _serial; // Unique document number (used by undo/redo).
368     Glib::ustring actionkey; // Last action key, used to combine actions in undo.
369 
370     // Garbage collecting ----------------------
371 
372     std::vector<SPObject *> _collection_queue; ///< Orphans
373 
374     // Actions ---------------------------------
375     Glib::RefPtr<Gio::SimpleActionGroup> action_group;
376 
377     /*********** Signals **************/
378 
379     typedef sigc::signal<void, SPObject *> IDChangedSignal;
380     typedef sigc::signal<void> ResourcesChangedSignal;
381     typedef sigc::signal<void, unsigned> ModifiedSignal;
382     typedef sigc::signal<void, char const *> URISetSignal;
383     typedef sigc::signal<void, double, double> ResizedSignal;
384     typedef sigc::signal<void> ReconstructionStart;
385     typedef sigc::signal<void> ReconstructionFinish;
386     typedef sigc::signal<void> CommitSignal;
387 
388     typedef std::map<GQuark, SPDocument::IDChangedSignal> IDChangedSignalMap;
389     typedef std::map<GQuark, SPDocument::ResourcesChangedSignal> ResourcesChangedSignalMap;
390 
391     /** Dictionary of signals for id changes */
392     IDChangedSignalMap id_changed_signals;
393 
394     SPDocument::ModifiedSignal modified_signal;
395     SPDocument::URISetSignal uri_set_signal;
396     SPDocument::ResizedSignal resized_signal;
397     SPDocument::ReconstructionStart _reconstruction_start_signal;
398     SPDocument::ReconstructionFinish  _reconstruction_finish_signal;
399     SPDocument::CommitSignal commit_signal; // Used by friend Inkscape::DocumentUndo
400 
401     bool oldSignalsConnected;
402 
403     sigc::connection _selection_changed_connection;
404     sigc::connection _desktop_activated_connection;
405     sigc::connection selChangeConnection;
406     sigc::connection desktopActivatedConnection;
407 
408     sigc::signal<void> destroySignal;
409 
410     mutable Geom::Affine _doc2dt;
411 
412 public:
413     /// Document to desktop coordinate transformation.
414     const Geom::Affine &doc2dt() const;
415     /// Desktop to document coordinate transformation.
dt2doc()416     const Geom::Affine &dt2doc() const
417     {
418         // Note: doc2dt().inverse() happens to be identical to doc2dt()
419         return doc2dt();
420     }
421     /// True if the desktop Y-axis points down, false if it points up.
is_yaxisdown()422     bool is_yaxisdown() const { return yaxisdir() > 0; }
423     /// "1" if the desktop Y-axis points down, "-1" if it points up.
yaxisdir()424     double yaxisdir() const { return _doc2dt[3]; }
425 
426     void addUndoObserver(Inkscape::UndoStackObserver& observer);
427     void removeUndoObserver(Inkscape::UndoStackObserver& observer);
428 
429     sigc::connection connectDestroy(sigc::signal<void>::slot_type slot);
430     sigc::connection connectModified(ModifiedSignal::slot_type slot);
431     sigc::connection connectURISet(URISetSignal::slot_type slot);
432     sigc::connection connectResized(ResizedSignal::slot_type slot);
433     sigc::connection connectCommit(CommitSignal::slot_type slot);
434     sigc::connection connectIdChanged(const char *id, IDChangedSignal::slot_type slot);
435     sigc::connection connectResourcesChanged(char const *key, SPDocument::ResourcesChangedSignal::slot_type slot);
436     sigc::connection connectReconstructionStart(ReconstructionStart::slot_type slot);
437     sigc::connection connectReconstructionFinish(ReconstructionFinish::slot_type slot);
438 
439     /* Resources */
440     std::map<std::string, std::vector<SPObject *> > resources;
441     ResourcesChangedSignalMap resources_changed_signals; // Used by Extension::Internal::Filter
442 
443     void _emitModified();  // Used by SPItem
444     void emitReconstructionStart();
445     void emitReconstructionFinish();
446     void emitResizedSignal(double width, double height);
447 };
448 
449 namespace std {
450 template <>
451 struct default_delete<SPDocument> {
452     void operator()(SPDocument *ptr) const { Inkscape::GC::release(ptr); }
453 };
454 }; // namespace std
455 
456 /*
457  * Ideas: How to overcome style invalidation nightmare
458  *
459  * 1. There is reference request dictionary, that contains
460  * objects (styles) needing certain id. Object::build checks
461  * final id against it, and invokes necessary methods
462  *
463  * 2. Removing referenced object is simply prohibited -
464  * needs analyse, how we can deal with situations, where
465  * we simply want to ungroup etc. - probably we need
466  * Repr::reparent method :( [Or was it ;)]
467  *
468  */
469 
470 #endif // SEEN_SP_DOCUMENT_H
471 
472 /*
473   Local Variables:
474   mode:c++
475   c-file-style:"stroustrup"
476   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
477   indent-tabs-mode:nil
478   fill-column:99
479   End:
480 */
481 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
482