diff options
Diffstat (limited to 'src/object/sp-object.h')
-rw-r--r-- | src/object/sp-object.h | 916 |
1 files changed, 916 insertions, 0 deletions
diff --git a/src/object/sp-object.h b/src/object/sp-object.h new file mode 100644 index 0000000..e936dd9 --- /dev/null +++ b/src/object/sp-object.h @@ -0,0 +1,916 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#ifndef SP_OBJECT_H_SEEN +#define SP_OBJECT_H_SEEN + +/* + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * Jon A. Cruz <jon@joncruz.org> + * Abhishek Sharma + * Adrian Boguszewski + * + * Copyright (C) 1999-2016 authors + * Copyright (C) 2001-2002 Ximian, Inc. + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include <set> +#include <glibmm/ustring.h> +#include "util/const_char_ptr.h" +#include "xml/node-observer.h" +/* SPObject flags */ + +class SPObject; + +/* Async modification flags */ +#define SP_OBJECT_MODIFIED_FLAG (1 << 0) +#define SP_OBJECT_CHILD_MODIFIED_FLAG (1 << 1) +#define SP_OBJECT_PARENT_MODIFIED_FLAG (1 << 2) +#define SP_OBJECT_STYLE_MODIFIED_FLAG (1 << 3) +#define SP_OBJECT_VIEWPORT_MODIFIED_FLAG (1 << 4) +#define SP_OBJECT_USER_MODIFIED_FLAG_A (1 << 5) +#define SP_OBJECT_USER_MODIFIED_FLAG_B (1 << 6) +#define SP_OBJECT_STYLESHEET_MODIFIED_FLAG (1 << 7) + +/* Convenience */ +#define SP_OBJECT_FLAGS_ALL 0xff + +/* Flags that mark object as modified */ +/* Object, Child, Style, Viewport, User */ +#define SP_OBJECT_MODIFIED_STATE (SP_OBJECT_FLAGS_ALL & ~(SP_OBJECT_PARENT_MODIFIED_FLAG)) + +/* Flags that will propagate downstreams */ +/* Parent, Style, Viewport, User */ +#define SP_OBJECT_MODIFIED_CASCADE (SP_OBJECT_FLAGS_ALL & ~(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)) +inline unsigned cascade_flags(unsigned flags) +{ + // Unset object-modified and child-modified, set parent-modified if object-modified. + static_assert(SP_OBJECT_PARENT_MODIFIED_FLAG == SP_OBJECT_MODIFIED_FLAG << 2); + return (flags & SP_OBJECT_MODIFIED_CASCADE) | (flags & SP_OBJECT_MODIFIED_FLAG) << 2; +} + +/* Write flags */ +#define SP_OBJECT_WRITE_BUILD (1 << 0) +#define SP_OBJECT_WRITE_EXT (1 << 1) +#define SP_OBJECT_WRITE_ALL (1 << 2) +#define SP_OBJECT_WRITE_NO_CHILDREN (1 << 3) + +#include <vector> +#include <cassert> +#include <cstddef> +#include <boost/intrusive/list.hpp> +#include <2geom/point.h> // Used for dpi only +#include <sigc++/connection.h> +#include <sigc++/functors/slot.h> +#include <sigc++/signal.h> +#include "util/forward-pointer-iterator.h" +#include "tags.h" +#include "version.h" + +enum class SPAttr; + +class SPCSSAttr; +class SPStyle; + +namespace Inkscape::XML { class Node; struct Document; } + +/// Unused +struct SPCtx +{ + unsigned int flags; +}; + +enum +{ + SP_XML_SPACE_DEFAULT, + SP_XML_SPACE_PRESERVE +}; + +class SPDocument; + +/// Internal class consisting of two bits. +class SPIXmlSpace { +public: + SPIXmlSpace(): set(0), value(SP_XML_SPACE_DEFAULT) {}; + unsigned int set : 1; + unsigned int value : 1; +}; + +/* + * Refcounting + * + * Owner is here for debug reasons, you can set it to NULL safely + * Ref should return object, NULL is error, unref return always NULL + */ + +/** + * Increase reference count of object, with possible debugging. + * + * @param owner If non-NULL, make debug log entry. + * @return object, NULL is error. + * \pre object points to real object + * @todo need to move this to be a member of SPObject. + */ +SPObject *sp_object_ref(SPObject *object, SPObject *owner=nullptr); + +/** + * Decrease reference count of object, with possible debugging and + * finalization. + * + * @param owner If non-NULL, make debug log entry. + * @return always NULL + * \pre object points to real object + * @todo need to move this to be a member of SPObject. + */ +SPObject *sp_object_unref(SPObject *object, SPObject *owner=nullptr); + +/** + * SPObject is an abstract base class of all of the document nodes at the + * SVG document level. Each SPObject subclass implements a certain SVG + * element node type, or is an abstract base class for different node + * types. The SPObject layer is bound to the SPRepr layer, closely + * following the SPRepr mutations via callbacks. During creation, + * SPObject parses and interprets all textual attributes and CSS style + * strings of the SPRepr, and later updates the internal state whenever + * it receives a signal about a change. The opposite is not true - there + * are methods manipulating SPObjects directly and such changes do not + * propagate to the SPRepr layer. This is important for implementation of + * the undo stack, animations and other features. + * + * SPObjects are bound to the higher-level container SPDocument, which + * provides document level functionality such as the undo stack, + * dictionary and so on. Source: doc/architecture.txt + */ +class SPObject : private Inkscape::XML::NodeObserver +{ +public: + enum CollectionPolicy + { + COLLECT_WITH_PARENT, + ALWAYS_COLLECT + }; + + SPObject(); + SPObject(SPObject const &) = delete; + SPObject &operator=(SPObject const &) = delete; + ~SPObject() override; + virtual int tag() const { return tag_of<decltype(*this)>; } + + unsigned int cloned : 1; + SPObject *clone_original{nullptr}; + unsigned int uflags : 8; + unsigned int mflags : 8; + SPIXmlSpace xml_space; + Glib::ustring lang; + unsigned int hrefcount{0}; /* number of xlink:href references */ + unsigned int _total_hrefcount{0}; /* our hrefcount + total descendants */ + SPDocument *document{nullptr}; /* Document we are part of */ + SPObject *parent{nullptr}; /* Our parent (only one allowed) */ + +private: + char *id{nullptr}; /* Our very own unique id */ + Inkscape::XML::Node *repr{nullptr}; /* Our xml representation */ + +public: + int refCount{1}; + std::list<SPObject *> hrefList; + + /** + * Returns the objects current ID string. + */ + char const* getId() const; + + void getIds(std::set<std::string> &ret) const; + + /** + * Get the id in a URL format. + */ + std::string getUrl() const; + + /** + * Returns the XML representation of tree + */ +//protected: + Inkscape::XML::Node * getRepr(); + + /** + * Returns the XML representation of tree + */ + Inkscape::XML::Node const* getRepr() const; + +public: + + /** + * Cleans up an SPObject, releasing its references and + * requesting that references to it be released + */ + void releaseReferences(); + + /** + * Connects to the release request signal + * + * @param slot the slot to connect + * + * @return the sigc::connection formed + */ + sigc::connection connectRelease(sigc::slot<void (SPObject *)> slot) { + return _release_signal.connect(slot); + } + + /** + * Represents the style properties, whether from presentation attributes, the <tt>style</tt> + * attribute, or inherited. + * + * private_set() doesn't handle SPAttr::STYLE or any presentation attributes at the + * time of writing, so this is probably NULL for all SPObject's that aren't an SPItem. + * + * However, this gives rise to the bugs mentioned in sp_object_get_style_property. + * Note that some non-SPItem SPObject's, such as SPStop, do need styling information, + * and need to inherit properties even through other non-SPItem parents like \<defs\>. + */ + SPStyle *style; + + /** + * Represents the style that should be used to resolve 'context-fill' and 'context-stroke' + */ + SPStyle *context_style; + + /// Switch containing next() method. + struct ParentIteratorStrategy { + static SPObject const *next(SPObject const *object) { + return object->parent; + } + }; + + typedef Inkscape::Util::ForwardPointerIterator<SPObject, ParentIteratorStrategy> ParentIterator; + typedef Inkscape::Util::ForwardPointerIterator<SPObject const, ParentIteratorStrategy> ConstParentIterator; + + bool isSiblingOf(SPObject const *object) const { + if (object == nullptr) return false; + return this->parent && this->parent == object->parent; + } + + /** + * True if object is linked to us + */ + virtual void getLinked(std::vector<SPObject *> &objects, bool ignore_clones) const; + + /** + * True if object is non-NULL and this is some in/direct parent of object. + */ + bool isAncestorOf(SPObject const *object) const; + + /** + * Returns youngest object being parent to this and object. + */ + SPObject const *nearestCommonAncestor(SPObject const *object) const; + + /** + * Returns ancestor non layer. + */ + SPObject const * getTopAncestorNonLayer() const; + + /* Returns next object in sibling list or NULL. */ + SPObject *getNext(); + + /** + * Returns previous object in sibling list or NULL. + */ + SPObject *getPrev(); + + bool hasChildren() const { return ( children.size() > 0 ); } + + SPObject *firstChild() { return children.empty() ? nullptr : &children.front(); } + SPObject const *firstChild() const { return children.empty() ? nullptr : &children.front(); } + + SPObject *lastChild() { return children.empty() ? nullptr : &children.back(); } + SPObject const *lastChild() const { return children.empty() ? nullptr : &children.back(); } + + SPObject *nthChild(unsigned index); + SPObject const *nthChild(unsigned index) const; + + enum Action { ActionGeneral, ActionBBox, ActionUpdate, ActionShow }; + + /** + * Retrieves the children as a std vector object, optionally ref'ing the children + * in the process, if add_ref is specified. + */ + std::vector<SPObject*> childList(bool add_ref, Action action = ActionGeneral); + + + /** + * Retrieves a list of ancestors of the object, as an easy to use vector + * @param root_to_tip - If set, orders the list from the svg root to the tip. + */ + std::vector<SPObject*> ancestorList(bool root_to_tip); + + /** + * Append repr as child of this object. + * \pre this is not a cloned object + */ + SPObject *appendChildRepr(Inkscape::XML::Node *repr); + + /** + * Gets the author-visible label property for the object or a default if + * no label is defined. + */ + char const *label() const; + + /** + * Returns a default label property for this object. + */ + char const *defaultLabel() const; + + /** + * Sets the author-visible label for this object. + * + * @param label the new label. + */ + void setLabel(char const *label); + + /** + * Returns the title of this object, or NULL if there is none. + * The caller must free the returned string using g_free() - see comment + * for getTitleOrDesc() below. + */ + char *title() const; + + /** + * Sets the title of this object. + * A NULL first argument is interpreted as meaning that the existing title + * (if any) should be deleted. + * The second argument is optional - @see setTitleOrDesc() below for details. + */ + bool setTitle(char const *title, bool verbatim = false); + + /** + * Returns the description of this object, or NULL if there is none. + * The caller must free the returned string using g_free() - see comment + * for getTitleOrDesc() below. + */ + char *desc() const; + + /** + * Sets the description of this object. + * A NULL first argument is interpreted as meaning that the existing + * description (if any) should be deleted. + * The second argument is optional - @see setTitleOrDesc() below for details. + */ + bool setDesc(char const *desc, bool verbatim=false); + + /** + * Get and set the exportable filename on this object. Usually sp-item or sp-page + */ + Glib::ustring getExportFilename() const; + void setExportFilename(Glib::ustring filename); + + /** + * Get and set the exported DPI for this objet, if available. + */ + Geom::Point getExportDpi() const; + void setExportDpi(Geom::Point dpi); + + /** + * Set the policy under which this object will be orphan-collected. + * + * Orphan-collection is the process of deleting all objects which no longer have + * hyper-references pointing to them. The policy determines when this happens. Many objects + * should not be deleted simply because they are no longer referred to; other objects (like + * "intermediate" gradients) are more or less throw-away and should always be collected when no + * longer in use. + * + * Along these lines, there are currently two orphan-collection policies: + * + * COLLECT_WITH_PARENT - don't worry about the object's hrefcount; + * if its parent is collected, this object + * will be too + * + * COLLECT_ALWAYS - always collect the object as soon as its + * hrefcount reaches zero + * + * @return the current collection policy in effect for this object + */ + CollectionPolicy collectionPolicy() const { return _collection_policy; } + + /** + * Sets the orphan-collection policy in effect for this object. + * + * @param policy the new policy to adopt + * + * @see SPObject::collectionPolicy + */ + void setCollectionPolicy(CollectionPolicy policy) { + _collection_policy = policy; + } + + /** + * Requests a later automatic call to collectOrphan(). + * + * This method requests that collectOrphan() be called during the document update cycle, + * deleting the object if it is no longer used. + * + * If the current collection policy is COLLECT_WITH_PARENT, this function has no effect. + * + * @see SPObject::collectOrphan + */ + void requestOrphanCollection(); + + /** + * Unconditionally delete the object if it is not referenced. + * + * Unconditionally delete the object if there are no outstanding hyper-references to it. + * Observers are not notified of the object's deletion (at the SPObject level; XML tree + * notifications still fire). + * + * @see SPObject::deleteObject + */ + void collectOrphan() { + if ( _total_hrefcount == 0 ) { + deleteObject(false); + } + } + + /** + * Increase weak refcount. + * + * Hrefcount is used for weak references, for example, to + * determine whether any graphical element references a certain gradient + * node. + * It keeps a list of "owners". + * @param owner Used to track who uses this object. + */ + void hrefObject(SPObject* owner = nullptr); + + /** + * Decrease weak refcount. + * + * Hrefcount is used for weak references, for example, to determine whether + * any graphical element references a certain gradient node. + * @param owner Used to track who uses this object. + * \pre hrefcount>0 + */ + void unhrefObject(SPObject* owner = nullptr); + + /** + * Check if object is referenced by any other object. + */ + bool isReferenced() { return ( _total_hrefcount > 0 ); } + + /** + * Deletes an object, unparenting it from its parent. + * + * Detaches the object's repr, and optionally sends notification that the object has been + * deleted. + * + * @param propagate If it is set to true, it emits a delete signal. + * + * @param propagate_descendants If it is true, it recursively sends the delete signal to children. + */ + void deleteObject(bool propagate, bool propagate_descendants); + + /** + * Deletes on object. + * + * @param propagate Notify observers of this object and its children that they have been + * deleted? + */ + void deleteObject(bool propagate = true) + { + deleteObject(propagate, propagate); + } + + /** + * Removes all children except for the given object, it's children and it's ancesstors. + */ + void cropToObject(SPObject *except); + void cropToObjects(std::vector<SPObject *> except_objects); + + /** + * Get all child objects except for any in the list. + */ + void getObjectsExcept(std::vector<SPObject *> &objects, const std::vector<SPObject *> &except); + + /** + * Grows the input list with any and all linked items. + * + * @param ignore_clones - Links between objects and their child clones are not counted + */ + void getLinkedObjects(std::vector<SPObject *> &objects, bool ignore_clones) const; + + /** + * Connects a slot to be called when an object is deleted. + * + * This connects a slot to an object's internal delete signal, which is invoked when the object + * is deleted + * + * The signal is mainly useful for e.g. knowing when to break hrefs or dissociate clones. + * + * @param slot the slot to connect + * + * @see SPObject::deleteObject + */ + sigc::connection connectDelete(sigc::slot<void (SPObject *)> slot) { + return _delete_signal.connect(slot); + } + + sigc::connection connectPositionChanged(sigc::slot<void (SPObject *)> slot) { + return _position_changed_signal.connect(slot); + } + + /** + * Returns the object which supercedes this one (if any). + * + * This is mainly useful for ensuring we can correctly perform a series of moves or deletes, + * even if the objects in question have been replaced in the middle of the sequence. + */ + SPObject *successor() { return _successor; } + + /** + * Indicates that another object supercedes this one. + */ + void setSuccessor(SPObject *successor) { + assert(successor != NULL); + assert(_successor == NULL); + assert(successor->_successor == NULL); + sp_object_ref(successor, nullptr); + _successor = successor; + } + + /** + * Indicates that another object supercedes temporaty this one. + */ + void setTmpSuccessor(SPObject *tmpsuccessor); + + /** + * Unset object supercedes. + */ + void unsetTmpSuccessor(); + + /** + * Fix temporary successors in duple stamp. + */ + void fixTmpSuccessors(); + + /* modifications; all three sets of methods should probably ultimately be protected, as they + * are not really part of its public interface. However, other parts of the code to + * occasionally use them at present. */ + + /* the no-argument version of updateRepr() is intended to be a bit more public, however -- it + * essentially just flushes any changes back to the backing store (the repr layer); maybe it + * should be called something else and made public at that point. */ + + /** + * Updates the object's repr based on the object's state. + * + * This method updates the repr attached to the object to reflect the object's current + * state; see the three-argument version for details. + * + * @param flags object write flags that apply to this update + * + * @return the updated repr + */ + Inkscape::XML::Node *updateRepr(unsigned int flags = SP_OBJECT_WRITE_EXT); + + /** + * Updates the given repr based on the object's state. + * + * Used both to create reprs in the original document, and to create reprs + * in another document (e.g. a temporary document used when saving as "Plain SVG". + * + * This method updates the given repr to reflect the object's current state. There are + * several flags that affect this: + * + * SP_OBJECT_WRITE_BUILD - create new reprs + * + * SP_OBJECT_WRITE_EXT - write elements and attributes + * which are not part of pure SVG + * (i.e. the Inkscape and Sodipodi + * namespaces) + * + * SP_OBJECT_WRITE_ALL - create all nodes and attributes, + * even those which might be redundant + * + * @param repr the repr to update + * @param flags object write flags that apply to this update + * + * @return the updated repr + */ + Inkscape::XML::Node *updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags); + + /** + * Queues an deferred update of this object's display. + * + * This method sets flags to indicate updates to be performed later, during the idle loop. + * + * There are several flags permitted here: + * + * SP_OBJECT_MODIFIED_FLAG - the object has been modified + * + * SP_OBJECT_CHILD_MODIFIED_FLAG - a child of the object has been + * modified + * + * SP_OBJECT_STYLE_MODIFIED_FLAG - the object's style has been + * modified + * + * There are also some subclass-specific modified flags which are hardly ever used. + * + * One of either MODIFIED or CHILD_MODIFIED is required. + * + * @param flags flags indicating what to update + */ + void requestDisplayUpdate(unsigned int flags); + + /** + * Updates the object's display immediately + * + * This method is called during the idle loop by SPDocument in order to update the object's + * display. + * + * One additional flag is legal here: + * + * SP_OBJECT_PARENT_MODIFIED_FLAG - the parent has been + * modified + * + * @param ctx an SPCtx which accumulates various state + * during the recursive update -- beware! some + * subclasses try to cast this to an SPItemCtx * + * + * @param flags flags indicating what to update (in addition + * to any already set flags) + */ + void updateDisplay(SPCtx *ctx, unsigned int flags); + + /** + * Requests that a modification notification signal + * be emitted later (e.g. during the idle loop) + * + * Request modified always bubbles *up* the tree, as opposed to + * request display update, which trickles down and relies on the + * flags set during this pass... + * + * @param flags flags indicating what has been modified + */ + void requestModified(unsigned int flags); + + /** + * Emits the MODIFIED signal with the object's flags. + * The object's mflags are the original set aside during the update pass for + * later delivery here. Once emitModified() is called, those flags don't + * need to be stored any longer. + * + * @param flags indicating what has been modified. + */ + void emitModified(unsigned int flags); + + /** + * Connects to the modification notification signal + * + * @param slot the slot to connect + * + * @return the connection formed thereby + */ + sigc::connection connectModified( + sigc::slot<void (SPObject *, unsigned int)> slot + ) { + return _modified_signal.connect(slot); + } + + /** Sends the delete signal to all children of this object recursively */ + void _sendDeleteSignalRecursive(); + + /** + * Adds increment to _total_hrefcount of object and its parents. + */ + void _updateTotalHRefCount(int increment); + + void _requireSVGVersion(unsigned major, unsigned minor) { _requireSVGVersion(Inkscape::Version(major, minor)); } + + /** + * Lifts SVG version of all root objects to version. + */ + void _requireSVGVersion(Inkscape::Version version); + + sigc::signal<void (SPObject *)> _release_signal; + sigc::signal<void (SPObject *)> _delete_signal; + sigc::signal<void (SPObject *)> _position_changed_signal; + sigc::signal<void (SPObject *, unsigned int)> _modified_signal; + SPObject *_successor{nullptr}; + SPObject *_tmpsuccessor{nullptr}; + CollectionPolicy _collection_policy{SPObject::COLLECT_WITH_PARENT}; + char *_label{nullptr}; + mutable char *_default_label{nullptr}; + + // WARNING: + // Methods below should not be used outside of the SP tree, + // as they operate directly on the XML representation. + // In future, they will be made protected. + + /** + * Put object into object tree, under parent, and behind prev; + * also update object's XML space. + */ + void attach(SPObject *object, SPObject *prev); + + /** + * In list of object's children, move object behind prev. + */ + void reorder(SPObject* obj, SPObject *prev); + + /** + * Remove object from parent's children, release and unref it. + */ + void detach(SPObject *object); + + /** + * Return object's child whose node pointer equals repr. + */ + SPObject *get_child_by_repr(Inkscape::XML::Node *repr); + + void invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned); + + int getIntAttribute(char const *key, int def); + + unsigned getPosition(); + + char const * getAttribute(char const *name) const; + + void appendChild(Inkscape::XML::Node *child); + + void addChild(Inkscape::XML::Node *child,Inkscape::XML::Node *prev=nullptr); + + /** + * Call virtual set() function of object. + */ + void setKeyValue(SPAttr key, char const *value); + + + void setAttribute(Inkscape::Util::const_char_ptr key, + Inkscape::Util::const_char_ptr value); + + void setAttributeDouble(Inkscape::Util::const_char_ptr key, double value); + + void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, + Inkscape::Util::const_char_ptr value) { + this->setAttribute(key.data(), + (value.data() == nullptr || value.data()[0]=='\0') ? nullptr : value.data()); + } + + /** + * Read value of key attribute from XML node into object. + */ + void readAttr(char const *key); + void readAttr(SPAttr keyid); + + char const *getTagName() const; + + void removeAttribute(char const *key); + + void setCSS(SPCSSAttr *css, char const *attr); + + void changeCSS(SPCSSAttr *css, char const *attr); + + bool storeAsDouble( char const *key, double *val ) const; + +private: + // Private member functions used in the definitions of setTitle(), + // setDesc(), title() and desc(). + + /** + * Sets or deletes the title or description of this object. + * A NULL 'value' argument causes the title or description to be deleted. + * + * 'verbatim' parameter: + * If verbatim==true, then the title or description is set to exactly the + * specified value. If verbatim==false then two exceptions are made: + * (1) If the specified value is just whitespace, then the title/description + * is deleted. + * (2) If the specified value is the same as the current value except for + * mark-up, then the current value is left unchanged. + * This is usually the desired behaviour, so 'verbatim' defaults to false for + * setTitle() and setDesc(). + * + * The return value is true if a change was made to the title/description, + * and usually false otherwise. + */ + bool setTitleOrDesc(char const *value, char const *svg_tagname, bool verbatim); + + /** + * Returns the title or description of this object, or NULL if there is none. + * + * The SVG spec allows 'title' and 'desc' elements to contain text marked up + * using elements from other namespaces. Therefore, this function cannot + * in general just return a pointer to an existing string - it must instead + * construct a string containing the title or description without the mark-up. + * Consequently, the return value is a newly allocated string (or NULL), and + * must be freed (using g_free()) by the caller. + */ + char * getTitleOrDesc(char const *svg_tagname) const; + + /** + * Find the first child of this object with a given tag name, + * and return it. Returns NULL if there is no matching child. + */ + SPObject * findFirstChild(char const *tagname) const; + + /** + * Return the full textual content of an element (typically all the + * content except the tags). + * Must not be used on anything except elements. + */ + Glib::ustring textualContent() const; + + /* Real handlers of repr signals */ + +private: + // XML::NodeObserver functions + void notifyAttributeChanged(Inkscape::XML::Node &node, GQuark key, Inkscape::Util::ptr_shared oldval, + Inkscape::Util::ptr_shared newval) final; + + void notifyContentChanged(Inkscape::XML::Node &node, Inkscape::Util::ptr_shared oldcontent, + Inkscape::Util::ptr_shared newcontent) final; + + void notifyChildAdded(Inkscape::XML::Node &node, Inkscape::XML::Node &child, + Inkscape::XML::Node *prev) final; + + void notifyChildRemoved(Inkscape::XML::Node &node, Inkscape::XML::Node &child, + Inkscape::XML::Node *prev) final; + + void notifyChildOrderChanged(Inkscape::XML::Node &node, Inkscape::XML::Node &child, Inkscape::XML::Node *old_prev, + Inkscape::XML::Node *new_prev) final; + + void notifyElementNameChanged(Inkscape::XML::Node &node, GQuark old_name, GQuark new_name) final; + + friend class SPObjectImpl; + +protected: + virtual void build(SPDocument *doc, Inkscape::XML::Node *repr); + virtual void release(); + + virtual void child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref); + virtual void remove_child(Inkscape::XML::Node *child); + + virtual void order_changed(Inkscape::XML::Node *child, Inkscape::XML::Node *old_repr, + Inkscape::XML::Node *new_repr); + virtual void tag_name_changed(gchar const *oldname, gchar const *newname); + + virtual void set(SPAttr key, const char *value); + + virtual void update(SPCtx *ctx, unsigned int flags); + virtual void modified(unsigned int flags); + + virtual Inkscape::XML::Node *write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags); + + typedef boost::intrusive::list_member_hook<> ListHook; + ListHook _child_hook; + +public: + using ChildrenList = boost::intrusive::list< + SPObject, + boost::intrusive::member_hook< + SPObject, + ListHook, + &SPObject::_child_hook + >>; + ChildrenList children; + virtual void read_content(); + + void recursivePrintTree(unsigned level = 0); // For debugging + void objectTrace(std::string const &, bool in = true, unsigned flags = 0); + + /** + * @brief Generate a document-wide unique id for this object. + * + * Returns an id string not in use by any object within the object's document. + * If default_id is specified, it will be returned if possible. + * Otherwise, an id will be generated based on the object's name. + */ + std::string generate_unique_id(char const *default_id = nullptr) const; +}; + +std::ostream &operator<<(std::ostream &out, const SPObject &o); + +/** + * Compares height of objects in tree. + * + * Works for different-parent objects, so long as they have a common ancestor. + * \return \verbatim + * 0 positions are equivalent + * 1 first object's position is greater than the second + * -1 first object's position is less than the second \endverbatim + */ +int sp_object_compare_position(SPObject const *first, SPObject const *second); +bool sp_object_compare_position_bool(SPObject const *first, SPObject const *second); + +#endif // SP_OBJECT_H_SEEN + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |