#ifndef SP_OBJECT_H_SEEN #define SP_OBJECT_H_SEEN /** \file * Abstract base class for all nodes * * Authors: * Lauris Kaplinski * Jon A. Cruz * Abhishek Sharma * * Copyright (C) 1999-2002 authors * Copyright (C) 2001-2002 Ximian, Inc. * * Released under GNU GPL, read the file 'COPYING' for more information */ /* SPObject flags */ class SPObject; class SPObjectClass; #define SP_TYPE_OBJECT (SPObject::sp_object_get_type ()) #define SP_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_OBJECT, SPObject)) #define SP_OBJECT_CLASS(clazz) (G_TYPE_CHECK_CLASS_CAST((clazz), SP_TYPE_OBJECT, SPObjectClass)) #define SP_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_OBJECT)) /* 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_USER_MODIFIED_FLAG_C (1 << 7) /* Conveneience */ #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)) /* Write flags */ #define SP_OBJECT_WRITE_BUILD (1 << 0) #define SP_OBJECT_WRITE_EXT (1 << 1) #define SP_OBJECT_WRITE_ALL (1 << 2) #include #include #include #include #include #include "forward.h" #include "version.h" #include "util/forward-pointer-iterator.h" #include "desktop-style.h" namespace Inkscape { namespace XML { class Node; class Document; } } typedef enum { SP_NO_EXCEPTION, SP_INDEX_SIZE_ERR, SP_DOMSTRING_SIZE_ERR, SP_HIERARCHY_REQUEST_ERR, SP_WRONG_DOCUMENT_ERR, SP_INVALID_CHARACTER_ERR, SP_NO_DATA_ALLOWED_ERR, SP_NO_MODIFICATION_ALLOWED_ERR, SP_NOT_FOUND_ERR, SP_NOT_SUPPORTED_ERR, SP_INUSE_ATTRIBUTE_ERR, SP_INVALID_STATE_ERR, SP_SYNTAX_ERR, SP_INVALID_MODIFICATION_ERR, SP_NAMESPACE_ERR, SP_INVALID_ACCESS_ERR } SPExceptionType; class SPException; /// An attempt to implement exceptions, unused? struct SPException { SPExceptionType code; }; #define SP_EXCEPTION_INIT(ex) {(ex)->code = SP_NO_EXCEPTION;} #define SP_EXCEPTION_IS_OK(ex) (!(ex) || ((ex)->code == SP_NO_EXCEPTION)) class SPCtx; /// Unused struct SPCtx { unsigned int flags; }; enum { SP_XML_SPACE_DEFAULT, SP_XML_SPACE_PRESERVE }; class SPIXmlSpace; /// Internal class consisting of two bits. struct SPIXmlSpace { guint set : 1; guint value : 1; }; class SPObject; /* * 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 */ SPObject *sp_object_ref(SPObject *object, SPObject *owner=NULL); SPObject *sp_object_unref(SPObject *object, SPObject *owner=NULL); SPObject *sp_object_href(SPObject *object, gpointer owner); SPObject *sp_object_hunref(SPObject *object, gpointer owner); /// A refcounting tree node object. class SPObject : public GObject { public: enum CollectionPolicy { COLLECT_WITH_PARENT, ALWAYS_COLLECT }; unsigned int cloned : 1; unsigned int uflags : 8; unsigned int mflags : 8; SPIXmlSpace xml_space; unsigned int hrefcount; /* number of xlink:href references */ unsigned int _total_hrefcount; /* our hrefcount + total descendants */ SPDocument *document; /* Document we are part of */ SPObject *parent; /* Our parent (only one allowed) */ SPObject *children; /* Our children */ SPObject *_last_child; /* Remembered last child */ SPObject *next; /* Next object in linked list */ private: gchar *id; /* Our very own unique id */ Inkscape::XML::Node *repr; /* Our xml representation */ public: /** * Returns the objects current ID string. */ gchar const* getId() const; /** * Returns the XML representation of tree */ //Inkscape::XML::Node const* getRepr() const; //protected: Inkscape::XML::Node * getRepr(); /** * Returns the XML representation of tree */ Inkscape::XML::Node const* getRepr() const; public: /** @brief cleans up an SPObject, releasing its references and * requesting that references to it be released */ void releaseReferences(); /** @brief connects to the release request signal * * @param slot the slot to connect * * @returns the sigc::connection formed */ sigc::connection connectRelease(sigc::slot slot) { return _release_signal.connect(slot); } /** * Represents the style properties, whether from presentation attributes, the style * attribute, or inherited. * * sp_object_private_set doesn't handle SP_ATTR_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 \. */ SPStyle *style; /// Switch containing next() method. struct ParentIteratorStrategy { static SPObject const *next(SPObject const *object) { return object->parent; } }; /// Switch containing next() method. struct SiblingIteratorStrategy { static SPObject const *next(SPObject const *object) { return object->next; } }; typedef Inkscape::Util::ForwardPointerIterator ParentIterator; typedef Inkscape::Util::ForwardPointerIterator ConstParentIterator; typedef Inkscape::Util::ForwardPointerIterator SiblingIterator; typedef Inkscape::Util::ForwardPointerIterator ConstSiblingIterator; bool isSiblingOf(SPObject const *object) const { g_return_val_if_fail(object != NULL, false); return this->parent && this->parent == object->parent; } bool isAncestorOf(SPObject const *object) const; SPObject const *nearestCommonAncestor(SPObject const *object) const; /* A non-const version can be similarly constructed if you want one. * (Don't just cast away the constness, which would be ill-formed.) */ SPObject *getNext() {return next;} SPObject const *getNext() const {return next;} /** * Returns previous object in sibling list or NULL. */ SPObject *getPrev(); bool hasChildren() const { return ( children != NULL ); } SPObject *firstChild() { return children; } SPObject const *firstChild() const { return children; } SPObject *lastChild() { return _last_child; } SPObject const *lastChild() const { return _last_child; } enum Action { ActionGeneral, ActionBBox, ActionUpdate, ActionShow }; /** * Retrieves the children as a GSList object, optionally ref'ing the children * in the process, if add_ref is specified. */ GSList *childList(bool add_ref, Action action = ActionGeneral); SPObject *appendChildRepr(Inkscape::XML::Node *repr); /** @brief Gets the author-visible label for this object. */ gchar const *label() const; /** @brief Returns a default label for this object. */ gchar const *defaultLabel() const; /** @brief Sets the author-visible label for this object. * * Sets the author-visible label for the object. * * @param label the new label */ void setLabel(gchar const *label); /** Retrieves the title of this object */ gchar *title() const; /** Sets the title of this object */ bool setTitle(gchar const *title, bool verbatim=false); /** Retrieves the description of this object */ gchar *desc() const; /** Sets the description of this object */ bool setDesc(gchar const *desc, bool verbatim=false); /** @brief 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 * * @returns the current collection policy in effect for this object */ CollectionPolicy collectionPolicy() const { return _collection_policy; } /** @brief Sets the orphan-collection policy in effect for this object. * * @see SPObject::collectionPolicy * * @param policy the new policy to adopt */ void setCollectionPolicy(CollectionPolicy policy) { _collection_policy = policy; } /** @brief 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(); /** @brief 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); } } /** @brief Check if object is referenced by any other object. */ bool isReferenced() { return ( _total_hrefcount > 0 ); } /** @brief Deletes an object. * * Detaches the object's repr, and optionally sends notification that the object has been * deleted. * * @param propagate notify observers that the object has been deleted? * * @param propagate_descendants notify observers of children that they have been deleted? */ void deleteObject(bool propagate, bool propagate_descendants); /** @brief 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); } /** @brief 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 slot) { return _delete_signal.connect(slot); } sigc::connection connectPositionChanged(sigc::slot slot) { return _position_changed_signal.connect(slot); } /** @brief 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; } /** @brief Indicates that another object supercedes this one. */ void setSuccessor(SPObject *successor) { g_assert(successor != NULL); g_assert(_successor == NULL); g_assert(successor->_successor == NULL); sp_object_ref(successor, NULL); _successor = successor; } /* 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. */ /** @brief Updates the object's repr based on the object's state. * * This method updates the 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); /** @brief Updates the given repr based on the object's state. * * 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); /** @brief 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); /** @brief 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); /** @brief Requests that a modification notification signal * be emitted later (e.g. during the idle loop) * * @param flags flags indicating what has been modified */ void requestModified(unsigned int flags); /** @brief Emits a modification notification signal * * @param flags indicating what has been modified */ void emitModified(unsigned int flags); /** @brief Connects to the modification notification signal * * @param slot the slot to connect * * @returns the connection formed thereby */ sigc::connection connectModified( sigc::slot slot ) { return _modified_signal.connect(slot); } /** Sends the delete signal to all children of this object recursively */ void _sendDeleteSignalRecursive(); void _updateTotalHRefCount(int increment); void _requireSVGVersion(unsigned major, unsigned minor) { _requireSVGVersion(Inkscape::Version(major, minor)); } void _requireSVGVersion(Inkscape::Version version); sigc::signal _release_signal; sigc::signal _delete_signal; sigc::signal _position_changed_signal; sigc::signal _modified_signal; SPObject *_successor; CollectionPolicy _collection_policy; gchar *_label; mutable gchar *_default_label; // 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. void attach(SPObject *object, SPObject *prev); void reorder(SPObject *prev); void detach(SPObject *object); SPObject *get_child_by_repr(Inkscape::XML::Node *repr); void invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned); long long int getIntAttribute(char const *key, long long int def); unsigned getPosition(); gchar const * getAttribute(gchar const *name,SPException *ex=0) const; void appendChild(Inkscape::XML::Node *child); void addChild(Inkscape::XML::Node *child,Inkscape::XML::Node *prev=0); void setKeyValue(unsigned int key, gchar const *value); void setAttribute(gchar const *key, gchar const *value, SPException *ex=0); void readAttr(gchar const *key); gchar const *getTagName(SPException *ex) const; void removeAttribute(gchar const *key, SPException *ex=0); gchar const *getStyleProperty(gchar const *key, gchar const *def) const; void setCSS(SPCSSAttr *css, gchar const *attr); void changeCSS(SPCSSAttr *css, gchar const *attr); bool storeAsDouble( gchar const *key, double *val ) const; private: // Private member functions used in the definitions of setTitle(), // setDesc(), title() and desc(). bool setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim); gchar * getTitleOrDesc(gchar const *svg_tagname) const; SPObject * findFirstChild(gchar const *tagname) const; GString * textualContent() const; static void sp_object_init(SPObject *object); static void sp_object_finalize(GObject *object); static void sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref); static void sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child); static void sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *old_ref, Inkscape::XML::Node *new_ref); static void sp_object_release(SPObject *object); static void sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); static void sp_object_private_set(SPObject *object, unsigned int key, gchar const *value); static Inkscape::XML::Node *sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags); static gchar *sp_object_get_unique_id(SPObject *object, gchar const *defid); /* Real handlers of repr signals */ public: static GType sp_object_get_type(); static void sp_object_repr_attr_changed(Inkscape::XML::Node *repr, gchar const *key, gchar const *oldval, gchar const *newval, bool is_interactive, gpointer data); static void sp_object_repr_content_changed(Inkscape::XML::Node *repr, gchar const *oldcontent, gchar const *newcontent, gpointer data); static void sp_object_repr_child_added(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data); static void sp_object_repr_child_removed(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, void *data); static void sp_object_repr_order_changed(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data); friend class SPObjectClass; friend class SPObjectImpl; }; /// The SPObject vtable. class SPObjectClass { public: GObjectClass parent_class; void (* build) (SPObject *object, SPDocument *doc, Inkscape::XML::Node *repr); void (* release) (SPObject *object); /* Virtual handlers of repr signals */ void (* child_added) (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref); void (* remove_child) (SPObject *object, Inkscape::XML::Node *child); void (* order_changed) (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *new_repr); void (* set) (SPObject *object, unsigned int key, gchar const *value); void (* read_content) (SPObject *object); /* Update handler */ void (* update) (SPObject *object, SPCtx *ctx, unsigned int flags); /* Modification handler */ void (* modified) (SPObject *object, unsigned int flags); Inkscape::XML::Node * (* write) (SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags); private: static GObjectClass *static_parent_class; static void sp_object_class_init(SPObjectClass *klass); friend class SPObject; }; int sp_object_compare_position(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 :