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