1 // SPDX-License-Identifier: GPL-2.0-or-later 2 #ifndef SP_OBJECT_H_SEEN 3 #define SP_OBJECT_H_SEEN 4 5 6 /* 7 * Authors: 8 * Lauris Kaplinski <lauris@kaplinski.com> 9 * Jon A. Cruz <jon@joncruz.org> 10 * Abhishek Sharma 11 * Adrian Boguszewski 12 * 13 * Copyright (C) 1999-2016 authors 14 * Copyright (C) 2001-2002 Ximian, Inc. 15 * 16 * Released under GNU GPL v2+, read the file 'COPYING' for more information. 17 */ 18 19 #include <glibmm/ustring.h> 20 #include "util/const_char_ptr.h" 21 /* SPObject flags */ 22 23 class SPObject; 24 25 #define SP_OBJECT(obj) (dynamic_cast<SPObject*>((SPObject*)obj)) 26 #define SP_IS_OBJECT(obj) (dynamic_cast<const SPObject*>((SPObject*)obj) != NULL) 27 28 /* Async modification flags */ 29 #define SP_OBJECT_MODIFIED_FLAG (1 << 0) 30 #define SP_OBJECT_CHILD_MODIFIED_FLAG (1 << 1) 31 #define SP_OBJECT_PARENT_MODIFIED_FLAG (1 << 2) 32 #define SP_OBJECT_STYLE_MODIFIED_FLAG (1 << 3) 33 #define SP_OBJECT_VIEWPORT_MODIFIED_FLAG (1 << 4) 34 #define SP_OBJECT_USER_MODIFIED_FLAG_A (1 << 5) 35 #define SP_OBJECT_USER_MODIFIED_FLAG_B (1 << 6) 36 #define SP_OBJECT_STYLESHEET_MODIFIED_FLAG (1 << 7) 37 38 /* Convenience */ 39 #define SP_OBJECT_FLAGS_ALL 0xff 40 41 /* Flags that mark object as modified */ 42 /* Object, Child, Style, Viewport, User */ 43 #define SP_OBJECT_MODIFIED_STATE (SP_OBJECT_FLAGS_ALL & ~(SP_OBJECT_PARENT_MODIFIED_FLAG)) 44 45 /* Flags that will propagate downstreams */ 46 /* Parent, Style, Viewport, User */ 47 #define SP_OBJECT_MODIFIED_CASCADE (SP_OBJECT_FLAGS_ALL & ~(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)) 48 49 /* Write flags */ 50 #define SP_OBJECT_WRITE_BUILD (1 << 0) 51 #define SP_OBJECT_WRITE_EXT (1 << 1) 52 #define SP_OBJECT_WRITE_ALL (1 << 2) 53 #define SP_OBJECT_WRITE_NO_CHILDREN (1 << 3) 54 55 #include <cassert> 56 #include <cstddef> 57 #include <sigc++/connection.h> 58 #include <sigc++/functors/slot.h> 59 #include <sigc++/signal.h> 60 #include <vector> 61 #include <boost/intrusive/list.hpp> 62 #include "version.h" 63 #include "util/forward-pointer-iterator.h" 64 65 enum class SPAttr; 66 67 class SPCSSAttr; 68 class SPStyle; 69 70 namespace Inkscape { 71 namespace XML { 72 class Node; 73 struct Document; 74 } 75 } 76 77 namespace Glib { 78 class ustring; 79 } 80 81 enum SPExceptionType { 82 SP_NO_EXCEPTION, 83 SP_INDEX_SIZE_ERR, 84 SP_DOMSTRING_SIZE_ERR, 85 SP_HIERARCHY_REQUEST_ERR, 86 SP_WRONG_DOCUMENT_ERR, 87 SP_INVALID_CHARACTER_ERR, 88 SP_NO_DATA_ALLOWED_ERR, 89 SP_NO_MODIFICATION_ALLOWED_ERR, 90 SP_NOT_FOUND_ERR, 91 SP_NOT_SUPPORTED_ERR, 92 SP_INUSE_ATTRIBUTE_ERR, 93 SP_INVALID_STATE_ERR, 94 SP_SYNTAX_ERR, 95 SP_INVALID_MODIFICATION_ERR, 96 SP_NAMESPACE_ERR, 97 SP_INVALID_ACCESS_ERR 98 }; 99 100 /// An attempt to implement exceptions, unused? 101 struct SPException { 102 SPExceptionType code; 103 }; 104 105 #define SP_EXCEPTION_INIT(ex) {(ex)->code = SP_NO_EXCEPTION;} 106 #define SP_EXCEPTION_IS_OK(ex) (!(ex) || ((ex)->code == SP_NO_EXCEPTION)) 107 108 /// Unused 109 struct SPCtx { 110 unsigned int flags; 111 }; 112 113 enum { 114 SP_XML_SPACE_DEFAULT, 115 SP_XML_SPACE_PRESERVE 116 }; 117 118 class SPDocument; 119 120 /// Internal class consisting of two bits. 121 class SPIXmlSpace { 122 public: SPIXmlSpace()123 SPIXmlSpace(): set(0), value(SP_XML_SPACE_DEFAULT) {}; 124 unsigned int set : 1; 125 unsigned int value : 1; 126 }; 127 128 /* 129 * Refcounting 130 * 131 * Owner is here for debug reasons, you can set it to NULL safely 132 * Ref should return object, NULL is error, unref return always NULL 133 */ 134 135 /** 136 * Increase reference count of object, with possible debugging. 137 * 138 * @param owner If non-NULL, make debug log entry. 139 * @return object, NULL is error. 140 * \pre object points to real object 141 * @todo need to move this to be a member of SPObject. 142 */ 143 SPObject *sp_object_ref(SPObject *object, SPObject *owner=nullptr); 144 145 /** 146 * Decrease reference count of object, with possible debugging and 147 * finalization. 148 * 149 * @param owner If non-NULL, make debug log entry. 150 * @return always NULL 151 * \pre object points to real object 152 * @todo need to move this to be a member of SPObject. 153 */ 154 SPObject *sp_object_unref(SPObject *object, SPObject *owner=nullptr); 155 156 /** 157 * SPObject is an abstract base class of all of the document nodes at the 158 * SVG document level. Each SPObject subclass implements a certain SVG 159 * element node type, or is an abstract base class for different node 160 * types. The SPObject layer is bound to the SPRepr layer, closely 161 * following the SPRepr mutations via callbacks. During creation, 162 * SPObject parses and interprets all textual attributes and CSS style 163 * strings of the SPRepr, and later updates the internal state whenever 164 * it receives a signal about a change. The opposite is not true - there 165 * are methods manipulating SPObjects directly and such changes do not 166 * propagate to the SPRepr layer. This is important for implementation of 167 * the undo stack, animations and other features. 168 * 169 * SPObjects are bound to the higher-level container SPDocument, which 170 * provides document level functionality such as the undo stack, 171 * dictionary and so on. Source: doc/architecture.txt 172 */ 173 class SPObject { 174 public: 175 enum CollectionPolicy { 176 COLLECT_WITH_PARENT, 177 ALWAYS_COLLECT 178 }; 179 180 SPObject(); 181 virtual ~SPObject(); 182 183 unsigned int cloned : 1; 184 SPObject *clone_original; 185 unsigned int uflags : 8; 186 unsigned int mflags : 8; 187 SPIXmlSpace xml_space; 188 Glib::ustring lang; 189 unsigned int hrefcount; /* number of xlink:href references */ 190 unsigned int _total_hrefcount; /* our hrefcount + total descendants */ 191 SPDocument *document; /* Document we are part of */ 192 SPObject *parent; /* Our parent (only one allowed) */ 193 194 private: 195 SPObject(const SPObject&); 196 SPObject& operator=(const SPObject&); 197 198 char *id; /* Our very own unique id */ 199 Inkscape::XML::Node *repr; /* Our xml representation */ 200 201 public: 202 int refCount; 203 std::list<SPObject*> hrefList; 204 205 /** 206 * Returns the objects current ID string. 207 */ 208 char const* getId() const; 209 210 /** 211 * Returns the XML representation of tree 212 */ 213 //protected: 214 Inkscape::XML::Node * getRepr(); 215 216 /** 217 * Returns the XML representation of tree 218 */ 219 Inkscape::XML::Node const* getRepr() const; 220 221 public: 222 223 /** 224 * Cleans up an SPObject, releasing its references and 225 * requesting that references to it be released 226 */ 227 void releaseReferences(); 228 229 /** 230 * Connects to the release request signal 231 * 232 * @param slot the slot to connect 233 * 234 * @return the sigc::connection formed 235 */ connectRelease(sigc::slot<void,SPObject * > slot)236 sigc::connection connectRelease(sigc::slot<void, SPObject *> slot) { 237 return _release_signal.connect(slot); 238 } 239 240 /** 241 * Represents the style properties, whether from presentation attributes, the <tt>style</tt> 242 * attribute, or inherited. 243 * 244 * private_set() doesn't handle SPAttr::STYLE or any presentation attributes at the 245 * time of writing, so this is probably NULL for all SPObject's that aren't an SPItem. 246 * 247 * However, this gives rise to the bugs mentioned in sp_object_get_style_property. 248 * Note that some non-SPItem SPObject's, such as SPStop, do need styling information, 249 * and need to inherit properties even through other non-SPItem parents like \<defs\>. 250 */ 251 SPStyle *style; 252 253 /** 254 * Represents the style that should be used to resolve 'context-fill' and 'context-stroke' 255 */ 256 SPStyle *context_style; 257 258 /// Switch containing next() method. 259 struct ParentIteratorStrategy { nextParentIteratorStrategy260 static SPObject const *next(SPObject const *object) { 261 return object->parent; 262 } 263 }; 264 265 typedef Inkscape::Util::ForwardPointerIterator<SPObject, ParentIteratorStrategy> ParentIterator; 266 typedef Inkscape::Util::ForwardPointerIterator<SPObject const, ParentIteratorStrategy> ConstParentIterator; 267 isSiblingOf(SPObject const * object)268 bool isSiblingOf(SPObject const *object) const { 269 if (object == nullptr) return false; 270 return this->parent && this->parent == object->parent; 271 } 272 273 /** 274 * True if object is non-NULL and this is some in/direct parent of object. 275 */ 276 bool isAncestorOf(SPObject const *object) const; 277 278 /** 279 * Returns youngest object being parent to this and object. 280 */ 281 SPObject const *nearestCommonAncestor(SPObject const *object) const; 282 283 /* Returns next object in sibling list or NULL. */ 284 SPObject *getNext(); 285 286 /** 287 * Returns previous object in sibling list or NULL. 288 */ 289 SPObject *getPrev(); 290 hasChildren()291 bool hasChildren() const { return ( children.size() > 0 ); } 292 firstChild()293 SPObject *firstChild() { return children.empty() ? nullptr : &children.front(); } firstChild()294 SPObject const *firstChild() const { return children.empty() ? nullptr : &children.front(); } 295 lastChild()296 SPObject *lastChild() { return children.empty() ? nullptr : &children.back(); } lastChild()297 SPObject const *lastChild() const { return children.empty() ? nullptr : &children.back(); } 298 299 SPObject *nthChild(unsigned index); 300 SPObject const *nthChild(unsigned index) const; 301 302 enum Action { ActionGeneral, ActionBBox, ActionUpdate, ActionShow }; 303 304 /** 305 * Retrieves the children as a std vector object, optionally ref'ing the children 306 * in the process, if add_ref is specified. 307 */ 308 std::vector<SPObject*> childList(bool add_ref, Action action = ActionGeneral); 309 310 /** 311 * Append repr as child of this object. 312 * \pre this is not a cloned object 313 */ 314 SPObject *appendChildRepr(Inkscape::XML::Node *repr); 315 316 /** 317 * Gets the author-visible label property for the object or a default if 318 * no label is defined. 319 */ 320 char const *label() const; 321 322 /** 323 * Returns a default label property for this object. 324 */ 325 char const *defaultLabel() const; 326 327 /** 328 * Sets the author-visible label for this object. 329 * 330 * @param label the new label. 331 */ 332 void setLabel(char const *label); 333 334 /** 335 * Returns the title of this object, or NULL if there is none. 336 * The caller must free the returned string using g_free() - see comment 337 * for getTitleOrDesc() below. 338 */ 339 char *title() const; 340 341 /** 342 * Sets the title of this object. 343 * A NULL first argument is interpreted as meaning that the existing title 344 * (if any) should be deleted. 345 * The second argument is optional - @see setTitleOrDesc() below for details. 346 */ 347 bool setTitle(char const *title, bool verbatim = false); 348 349 /** 350 * Returns the description of this object, or NULL if there is none. 351 * The caller must free the returned string using g_free() - see comment 352 * for getTitleOrDesc() below. 353 */ 354 char *desc() const; 355 356 /** 357 * Sets the description of this object. 358 * A NULL first argument is interpreted as meaning that the existing 359 * description (if any) should be deleted. 360 * The second argument is optional - @see setTitleOrDesc() below for details. 361 */ 362 bool setDesc(char const *desc, bool verbatim=false); 363 364 /** 365 * Set the policy under which this object will be orphan-collected. 366 * 367 * Orphan-collection is the process of deleting all objects which no longer have 368 * hyper-references pointing to them. The policy determines when this happens. Many objects 369 * should not be deleted simply because they are no longer referred to; other objects (like 370 * "intermediate" gradients) are more or less throw-away and should always be collected when no 371 * longer in use. 372 * 373 * Along these lines, there are currently two orphan-collection policies: 374 * 375 * COLLECT_WITH_PARENT - don't worry about the object's hrefcount; 376 * if its parent is collected, this object 377 * will be too 378 * 379 * COLLECT_ALWAYS - always collect the object as soon as its 380 * hrefcount reaches zero 381 * 382 * @return the current collection policy in effect for this object 383 */ collectionPolicy()384 CollectionPolicy collectionPolicy() const { return _collection_policy; } 385 386 /** 387 * Sets the orphan-collection policy in effect for this object. 388 * 389 * @param policy the new policy to adopt 390 * 391 * @see SPObject::collectionPolicy 392 */ setCollectionPolicy(CollectionPolicy policy)393 void setCollectionPolicy(CollectionPolicy policy) { 394 _collection_policy = policy; 395 } 396 397 /** 398 * Requests a later automatic call to collectOrphan(). 399 * 400 * This method requests that collectOrphan() be called during the document update cycle, 401 * deleting the object if it is no longer used. 402 * 403 * If the current collection policy is COLLECT_WITH_PARENT, this function has no effect. 404 * 405 * @see SPObject::collectOrphan 406 */ 407 void requestOrphanCollection(); 408 409 /** 410 * Unconditionally delete the object if it is not referenced. 411 * 412 * Unconditionally delete the object if there are no outstanding hyper-references to it. 413 * Observers are not notified of the object's deletion (at the SPObject level; XML tree 414 * notifications still fire). 415 * 416 * @see SPObject::deleteObject 417 */ collectOrphan()418 void collectOrphan() { 419 if ( _total_hrefcount == 0 ) { 420 deleteObject(false); 421 } 422 } 423 424 /** 425 * Increase weak refcount. 426 * 427 * Hrefcount is used for weak references, for example, to 428 * determine whether any graphical element references a certain gradient 429 * node. 430 * It keeps a list of "owners". 431 * @param owner Used to track who uses this object. 432 */ 433 void hrefObject(SPObject* owner = nullptr); 434 435 /** 436 * Decrease weak refcount. 437 * 438 * Hrefcount is used for weak references, for example, to determine whether 439 * any graphical element references a certain gradient node. 440 * @param owner Used to track who uses this object. 441 * \pre hrefcount>0 442 */ 443 void unhrefObject(SPObject* owner = nullptr); 444 445 /** 446 * Check if object is referenced by any other object. 447 */ isReferenced()448 bool isReferenced() { return ( _total_hrefcount > 0 ); } 449 450 /** 451 * Deletes an object, unparenting it from its parent. 452 * 453 * Detaches the object's repr, and optionally sends notification that the object has been 454 * deleted. 455 * 456 * @param propagate If it is set to true, it emits a delete signal. 457 * 458 * @param propagate_descendants If it is true, it recursively sends the delete signal to children. 459 */ 460 void deleteObject(bool propagate, bool propagate_descendants); 461 462 /** 463 * Deletes on object. 464 * 465 * @param propagate Notify observers of this object and its children that they have been 466 * deleted? 467 */ 468 void deleteObject(bool propagate = true) 469 { 470 deleteObject(propagate, propagate); 471 } 472 473 /** 474 * Removes all children except for the given object, it's children and it's ancesstors. 475 */ 476 void cropToObject(SPObject *except); 477 478 /** 479 * Connects a slot to be called when an object is deleted. 480 * 481 * This connects a slot to an object's internal delete signal, which is invoked when the object 482 * is deleted 483 * 484 * The signal is mainly useful for e.g. knowing when to break hrefs or dissociate clones. 485 * 486 * @param slot the slot to connect 487 * 488 * @see SPObject::deleteObject 489 */ connectDelete(sigc::slot<void,SPObject * > slot)490 sigc::connection connectDelete(sigc::slot<void, SPObject *> slot) { 491 return _delete_signal.connect(slot); 492 } 493 connectPositionChanged(sigc::slot<void,SPObject * > slot)494 sigc::connection connectPositionChanged(sigc::slot<void, SPObject *> slot) { 495 return _position_changed_signal.connect(slot); 496 } 497 498 /** 499 * Returns the object which supercedes this one (if any). 500 * 501 * This is mainly useful for ensuring we can correctly perform a series of moves or deletes, 502 * even if the objects in question have been replaced in the middle of the sequence. 503 */ successor()504 SPObject *successor() { return _successor; } 505 506 /** 507 * Indicates that another object supercedes this one. 508 */ setSuccessor(SPObject * successor)509 void setSuccessor(SPObject *successor) { 510 assert(successor != NULL); 511 assert(_successor == NULL); 512 assert(successor->_successor == NULL); 513 sp_object_ref(successor, nullptr); 514 _successor = successor; 515 } 516 517 /* modifications; all three sets of methods should probably ultimately be protected, as they 518 * are not really part of its public interface. However, other parts of the code to 519 * occasionally use them at present. */ 520 521 /* the no-argument version of updateRepr() is intended to be a bit more public, however -- it 522 * essentially just flushes any changes back to the backing store (the repr layer); maybe it 523 * should be called something else and made public at that point. */ 524 525 /** 526 * Updates the object's repr based on the object's state. 527 * 528 * This method updates the repr attached to the object to reflect the object's current 529 * state; see the three-argument version for details. 530 * 531 * @param flags object write flags that apply to this update 532 * 533 * @return the updated repr 534 */ 535 Inkscape::XML::Node *updateRepr(unsigned int flags = SP_OBJECT_WRITE_EXT); 536 537 /** 538 * Updates the given repr based on the object's state. 539 * 540 * Used both to create reprs in the original document, and to create reprs 541 * in another document (e.g. a temporary document used when saving as "Plain SVG". 542 * 543 * This method updates the given repr to reflect the object's current state. There are 544 * several flags that affect this: 545 * 546 * SP_OBJECT_WRITE_BUILD - create new reprs 547 * 548 * SP_OBJECT_WRITE_EXT - write elements and attributes 549 * which are not part of pure SVG 550 * (i.e. the Inkscape and Sodipodi 551 * namespaces) 552 * 553 * SP_OBJECT_WRITE_ALL - create all nodes and attributes, 554 * even those which might be redundant 555 * 556 * @param repr the repr to update 557 * @param flags object write flags that apply to this update 558 * 559 * @return the updated repr 560 */ 561 Inkscape::XML::Node *updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags); 562 563 /** 564 * Queues an deferred update of this object's display. 565 * 566 * This method sets flags to indicate updates to be performed later, during the idle loop. 567 * 568 * There are several flags permitted here: 569 * 570 * SP_OBJECT_MODIFIED_FLAG - the object has been modified 571 * 572 * SP_OBJECT_CHILD_MODIFIED_FLAG - a child of the object has been 573 * modified 574 * 575 * SP_OBJECT_STYLE_MODIFIED_FLAG - the object's style has been 576 * modified 577 * 578 * There are also some subclass-specific modified flags which are hardly ever used. 579 * 580 * One of either MODIFIED or CHILD_MODIFIED is required. 581 * 582 * @param flags flags indicating what to update 583 */ 584 void requestDisplayUpdate(unsigned int flags); 585 586 /** 587 * Updates the object's display immediately 588 * 589 * This method is called during the idle loop by SPDocument in order to update the object's 590 * display. 591 * 592 * One additional flag is legal here: 593 * 594 * SP_OBJECT_PARENT_MODIFIED_FLAG - the parent has been 595 * modified 596 * 597 * @param ctx an SPCtx which accumulates various state 598 * during the recursive update -- beware! some 599 * subclasses try to cast this to an SPItemCtx * 600 * 601 * @param flags flags indicating what to update (in addition 602 * to any already set flags) 603 */ 604 void updateDisplay(SPCtx *ctx, unsigned int flags); 605 606 /** 607 * Requests that a modification notification signal 608 * be emitted later (e.g. during the idle loop) 609 * 610 * Request modified always bubbles *up* the tree, as opposed to 611 * request display update, which trickles down and relies on the 612 * flags set during this pass... 613 * 614 * @param flags flags indicating what has been modified 615 */ 616 void requestModified(unsigned int flags); 617 618 /** 619 * Emits the MODIFIED signal with the object's flags. 620 * The object's mflags are the original set aside during the update pass for 621 * later delivery here. Once emitModified() is called, those flags don't 622 * need to be stored any longer. 623 * 624 * @param flags indicating what has been modified. 625 */ 626 void emitModified(unsigned int flags); 627 628 /** 629 * Connects to the modification notification signal 630 * 631 * @param slot the slot to connect 632 * 633 * @return the connection formed thereby 634 */ connectModified(sigc::slot<void,SPObject *,unsigned int> slot)635 sigc::connection connectModified( 636 sigc::slot<void, SPObject *, unsigned int> slot 637 ) { 638 return _modified_signal.connect(slot); 639 } 640 641 /** Sends the delete signal to all children of this object recursively */ 642 void _sendDeleteSignalRecursive(); 643 644 /** 645 * Adds increment to _total_hrefcount of object and its parents. 646 */ 647 void _updateTotalHRefCount(int increment); 648 _requireSVGVersion(unsigned major,unsigned minor)649 void _requireSVGVersion(unsigned major, unsigned minor) { 650 _requireSVGVersion(Inkscape::Version(major, minor)); 651 } 652 653 /** 654 * Lifts SVG version of all root objects to version. 655 */ 656 void _requireSVGVersion(Inkscape::Version version); 657 658 sigc::signal<void, SPObject *> _release_signal; 659 sigc::signal<void, SPObject *> _delete_signal; 660 sigc::signal<void, SPObject *> _position_changed_signal; 661 sigc::signal<void, SPObject *, unsigned int> _modified_signal; 662 SPObject *_successor; 663 CollectionPolicy _collection_policy; 664 char *_label; 665 mutable char *_default_label; 666 667 // WARNING: 668 // Methods below should not be used outside of the SP tree, 669 // as they operate directly on the XML representation. 670 // In future, they will be made protected. 671 672 /** 673 * Put object into object tree, under parent, and behind prev; 674 * also update object's XML space. 675 */ 676 void attach(SPObject *object, SPObject *prev); 677 678 /** 679 * In list of object's children, move object behind prev. 680 */ 681 void reorder(SPObject* obj, SPObject *prev); 682 683 /** 684 * Remove object from parent's children, release and unref it. 685 */ 686 void detach(SPObject *object); 687 688 /** 689 * Return object's child whose node pointer equals repr. 690 */ 691 SPObject *get_child_by_repr(Inkscape::XML::Node *repr); 692 693 void invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned); 694 695 int getIntAttribute(char const *key, int def); 696 697 unsigned getPosition(); 698 699 char const * getAttribute(char const *name,SPException *ex=nullptr) const; 700 701 void appendChild(Inkscape::XML::Node *child); 702 703 void addChild(Inkscape::XML::Node *child,Inkscape::XML::Node *prev=nullptr); 704 705 /** 706 * Call virtual set() function of object. 707 */ 708 void setKeyValue(SPAttr key, char const *value); 709 710 711 void setAttribute(Inkscape::Util::const_char_ptr key, 712 Inkscape::Util::const_char_ptr value, 713 SPException *ex=nullptr); 714 715 void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, 716 Inkscape::Util::const_char_ptr value, 717 SPException *ex=nullptr) { 718 this->setAttribute(key.data(), 719 (value.data() == nullptr || value.data()[0]=='\0') ? nullptr : value.data(), ex); 720 } 721 722 /** 723 * Read value of key attribute from XML node into object. 724 */ 725 void readAttr(char const *key); 726 void readAttr(SPAttr keyid); 727 728 char const *getTagName(SPException *ex) const; 729 730 void removeAttribute(char const *key, SPException *ex=nullptr); 731 732 void setCSS(SPCSSAttr *css, char const *attr); 733 734 void changeCSS(SPCSSAttr *css, char const *attr); 735 736 bool storeAsDouble( char const *key, double *val ) const; 737 738 private: 739 // Private member functions used in the definitions of setTitle(), 740 // setDesc(), title() and desc(). 741 742 /** 743 * Sets or deletes the title or description of this object. 744 * A NULL 'value' argument causes the title or description to be deleted. 745 * 746 * 'verbatim' parameter: 747 * If verbatim==true, then the title or description is set to exactly the 748 * specified value. If verbatim==false then two exceptions are made: 749 * (1) If the specified value is just whitespace, then the title/description 750 * is deleted. 751 * (2) If the specified value is the same as the current value except for 752 * mark-up, then the current value is left unchanged. 753 * This is usually the desired behaviour, so 'verbatim' defaults to false for 754 * setTitle() and setDesc(). 755 * 756 * The return value is true if a change was made to the title/description, 757 * and usually false otherwise. 758 */ 759 bool setTitleOrDesc(char const *value, char const *svg_tagname, bool verbatim); 760 761 /** 762 * Returns the title or description of this object, or NULL if there is none. 763 * 764 * The SVG spec allows 'title' and 'desc' elements to contain text marked up 765 * using elements from other namespaces. Therefore, this function cannot 766 * in general just return a pointer to an existing string - it must instead 767 * construct a string containing the title or description without the mark-up. 768 * Consequently, the return value is a newly allocated string (or NULL), and 769 * must be freed (using g_free()) by the caller. 770 */ 771 char * getTitleOrDesc(char const *svg_tagname) const; 772 773 /** 774 * Find the first child of this object with a given tag name, 775 * and return it. Returns NULL if there is no matching child. 776 */ 777 SPObject * findFirstChild(char const *tagname) const; 778 779 /** 780 * Return the full textual content of an element (typically all the 781 * content except the tags). 782 * Must not be used on anything except elements. 783 */ 784 Glib::ustring textualContent() const; 785 786 /* Real handlers of repr signals */ 787 788 public: 789 /** 790 * Callback for attr_changed node event. 791 */ 792 static void repr_attr_changed(Inkscape::XML::Node *repr, char const *key, char const *oldval, char const *newval, bool is_interactive, void* data); 793 794 /** 795 * Callback for content_changed node event. 796 */ 797 static void repr_content_changed(Inkscape::XML::Node *repr, char const *oldcontent, char const *newcontent, void* data); 798 799 /** 800 * Callback for child_added node event. 801 */ 802 static void repr_child_added(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, void* data); 803 804 /** 805 * Callback for remove_child node event. 806 */ 807 static void repr_child_removed(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, void *data); 808 809 /** 810 * Callback for order_changed node event. 811 * 812 * \todo fixme: 813 */ 814 static void repr_order_changed(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, void* data); 815 816 817 friend class SPObjectImpl; 818 819 protected: 820 virtual void build(SPDocument* doc, Inkscape::XML::Node* repr); 821 virtual void release(); 822 823 virtual void child_added(Inkscape::XML::Node* child, Inkscape::XML::Node* ref); 824 virtual void remove_child(Inkscape::XML::Node* child); 825 826 virtual void order_changed(Inkscape::XML::Node* child, Inkscape::XML::Node* old_repr, Inkscape::XML::Node* new_repr); 827 828 virtual void set(SPAttr key, const char* value); 829 830 virtual void update(SPCtx* ctx, unsigned int flags); 831 virtual void modified(unsigned int flags); 832 833 virtual Inkscape::XML::Node* write(Inkscape::XML::Document* doc, Inkscape::XML::Node* repr, unsigned int flags); 834 835 typedef boost::intrusive::list_member_hook<> ListHook; 836 ListHook _child_hook; 837 public: 838 typedef boost::intrusive::list< 839 SPObject, 840 boost::intrusive::member_hook< 841 SPObject, 842 ListHook, 843 &SPObject::_child_hook 844 >> ChildrenList; 845 ChildrenList children; 846 virtual void read_content(); 847 848 void recursivePrintTree(unsigned level = 0); // For debugging 849 static unsigned indent_level; 850 void objectTrace( std::string const &, bool in=true, unsigned flags=0 ); 851 }; 852 853 std::ostream &operator<<(std::ostream &out, const SPObject &o); 854 855 /** 856 * Compares height of objects in tree. 857 * 858 * Works for different-parent objects, so long as they have a common ancestor. 859 * \return \verbatim 860 * 0 positions are equivalent 861 * 1 first object's position is greater than the second 862 * -1 first object's position is less than the second \endverbatim 863 */ 864 int sp_object_compare_position(SPObject const *first, SPObject const *second); 865 bool sp_object_compare_position_bool(SPObject const *first, SPObject const *second); 866 gchar * sp_object_get_unique_id(SPObject *object, gchar const *defid); 867 868 #endif // SP_OBJECT_H_SEEN 869 870 871 /* 872 Local Variables: 873 mode:c++ 874 c-file-style:"stroustrup" 875 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) 876 indent-tabs-mode:nil 877 fill-column:99 878 End: 879 */ 880 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : 881