1 #ifndef SP_OBJECT_H_SEEN
2 #define SP_OBJECT_H_SEEN
4 /** \file
5 * Abstract base class for all nodes
6 *
7 * Authors:
8 * Lauris Kaplinski <lauris@kaplinski.com>
9 * Jon A. Cruz <jon@joncruz.org>
10 * Abhishek Sharma
11 *
12 * Copyright (C) 1999-2002 authors
13 * Copyright (C) 2001-2002 Ximian, Inc.
14 *
15 * Released under GNU GPL, read the file 'COPYING' for more information
16 */
18 /* SPObject flags */
20 class SPObject;
21 class SPObjectClass;
23 #define SP_TYPE_OBJECT (SPObject::sp_object_get_type ())
24 #define SP_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_OBJECT, SPObject))
25 #define SP_OBJECT_CLASS(clazz) (G_TYPE_CHECK_CLASS_CAST((clazz), SP_TYPE_OBJECT, SPObjectClass))
26 #define SP_IS_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_OBJECT))
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_USER_MODIFIED_FLAG_C (1 << 7)
38 /* Conveneience */
39 #define SP_OBJECT_FLAGS_ALL 0xff
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))
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))
49 /* Generic */
50 #define SP_OBJECT_IS_CLONED(o) (((SPObject *) (o))->cloned)
52 /* Write flags */
53 #define SP_OBJECT_WRITE_BUILD (1 << 0)
54 #define SP_OBJECT_WRITE_EXT (1 << 1)
55 #define SP_OBJECT_WRITE_ALL (1 << 2)
57 /* Convenience stuff */
58 #define SP_OBJECT_REPR(o) (((SPObject *) (o))->getRepr())
59 #define SP_OBJECT_DOCUMENT(o) (((SPObject *) (o))->document)
60 #define SP_OBJECT_PARENT(o) (((SPObject *) (o))->parent)
61 #define SP_OBJECT_STYLE(o) (((SPObject *) (o))->style)
63 #include <glib-object.h>
64 #include <sigc++/connection.h>
65 #include <sigc++/functors/slot.h>
66 #include <sigc++/signal.h>
68 #include "forward.h"
69 #include "version.h"
70 #include "util/forward-pointer-iterator.h"
71 #include "desktop-style.h"
73 namespace Inkscape {
74 namespace XML {
75 class Node;
76 class Document;
77 }
78 }
81 typedef enum {
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 } SPExceptionType;
100 class SPException;
102 /// An attempt to implement exceptions, unused?
103 struct SPException {
104 SPExceptionType code;
105 };
107 #define SP_EXCEPTION_INIT(ex) {(ex)->code = SP_NO_EXCEPTION;}
108 #define SP_EXCEPTION_IS_OK(ex) (!(ex) || ((ex)->code == SP_NO_EXCEPTION))
110 class SPCtx;
112 /// Unused
113 struct SPCtx {
114 unsigned int flags;
115 };
117 enum {
118 SP_XML_SPACE_DEFAULT,
119 SP_XML_SPACE_PRESERVE
120 };
122 class SPIXmlSpace;
124 /// Internal class consisting of two bits.
125 struct SPIXmlSpace {
126 guint set : 1;
127 guint value : 1;
128 };
130 class SPObject;
132 /*
133 * Refcounting
134 *
135 * Owner is here for debug reasons, you can set it to NULL safely
136 * Ref should return object, NULL is error, unref return always NULL
137 */
139 SPObject *sp_object_ref(SPObject *object, SPObject *owner=NULL);
140 SPObject *sp_object_unref(SPObject *object, SPObject *owner=NULL);
142 SPObject *sp_object_href(SPObject *object, gpointer owner);
143 SPObject *sp_object_hunref(SPObject *object, gpointer owner);
145 /// A refcounting tree node object.
146 class SPObject : public GObject {
147 public:
148 enum CollectionPolicy {
149 COLLECT_WITH_PARENT,
150 ALWAYS_COLLECT
151 };
153 unsigned int cloned : 1;
154 unsigned int uflags : 8;
155 unsigned int mflags : 8;
156 SPIXmlSpace xml_space;
157 unsigned int hrefcount; /* number of xlink:href references */
158 unsigned int _total_hrefcount; /* our hrefcount + total descendants */
159 SPDocument *document; /* Document we are part of */
160 SPObject *parent; /* Our parent (only one allowed) */
161 SPObject *children; /* Our children */
162 SPObject *_last_child; /* Remembered last child */
163 SPObject *next; /* Next object in linked list */
165 private:
166 gchar *id; /* Our very own unique id */
167 Inkscape::XML::Node *repr; /* Our xml representation */
168 public:
170 /**
171 * Returns the objects current ID string.
172 */
173 gchar const* getId() const;
175 /**
176 * Returns the XML representation of tree
177 */
178 //Inkscape::XML::Node const* getRepr() const;
179 //protected:
180 Inkscape::XML::Node * getRepr();
182 /**
183 * Returns the XML representation of tree
184 */
185 Inkscape::XML::Node const* getRepr() const;
187 public:
189 /** @brief cleans up an SPObject, releasing its references and
190 * requesting that references to it be released
191 */
192 void releaseReferences();
194 /** @brief connects to the release request signal
195 *
196 * @param slot the slot to connect
197 *
198 * @returns the sigc::connection formed
199 */
200 sigc::connection connectRelease(sigc::slot<void, SPObject *> slot) {
201 return _release_signal.connect(slot);
202 }
204 /**
205 * Represents the style properties, whether from presentation attributes, the <tt>style</tt>
206 * attribute, or inherited.
207 *
208 * sp_object_private_set doesn't handle SP_ATTR_STYLE or any presentation attributes at the
209 * time of writing, so this is probably NULL for all SPObject's that aren't an SPItem.
210 *
211 * However, this gives rise to the bugs mentioned in sp_object_get_style_property.
212 * Note that some non-SPItem SPObject's, such as SPStop, do need styling information,
213 * and need to inherit properties even through other non-SPItem parents like \<defs\>.
214 */
215 SPStyle *style;
217 /// Switch containing next() method.
218 struct ParentIteratorStrategy {
219 static SPObject const *next(SPObject const *object) {
220 return object->parent;
221 }
222 };
223 /// Switch containing next() method.
224 struct SiblingIteratorStrategy {
225 static SPObject const *next(SPObject const *object) {
226 return object->next;
227 }
228 };
230 typedef Inkscape::Util::ForwardPointerIterator<SPObject, ParentIteratorStrategy> ParentIterator;
231 typedef Inkscape::Util::ForwardPointerIterator<SPObject const, ParentIteratorStrategy> ConstParentIterator;
232 typedef Inkscape::Util::ForwardPointerIterator<SPObject, SiblingIteratorStrategy> SiblingIterator;
233 typedef Inkscape::Util::ForwardPointerIterator<SPObject const, SiblingIteratorStrategy> ConstSiblingIterator;
235 bool isSiblingOf(SPObject const *object) const {
236 g_return_val_if_fail(object != NULL, false);
237 return this->parent && this->parent == object->parent;
238 }
239 bool isAncestorOf(SPObject const *object) const;
241 SPObject const *nearestCommonAncestor(SPObject const *object) const;
242 /* A non-const version can be similarly constructed if you want one.
243 * (Don't just cast away the constness, which would be ill-formed.) */
245 SPObject *getNext() {return next;}
246 SPObject const *getNext() const {return next;}
248 /**
249 * Returns previous object in sibling list or NULL.
250 */
251 SPObject *getPrev();
253 bool hasChildren() const { return ( children != NULL ); }
255 SPObject *firstChild() { return children; }
256 SPObject const *firstChild() const { return children; }
258 SPObject *lastChild() { return _last_child; }
259 SPObject const *lastChild() const { return _last_child; }
261 enum Action { ActionGeneral, ActionBBox, ActionUpdate, ActionShow };
263 /**
264 * Retrieves the children as a GSList object, optionally ref'ing the children
265 * in the process, if add_ref is specified.
266 */
267 GSList *childList(bool add_ref, Action action = ActionGeneral);
269 SPObject *appendChildRepr(Inkscape::XML::Node *repr);
271 /** @brief Gets the author-visible label for this object. */
272 gchar const *label() const;
273 /** @brief Returns a default label for this object. */
274 gchar const *defaultLabel() const;
275 /** @brief Sets the author-visible label for this object.
276 *
277 * Sets the author-visible label for the object.
278 *
279 * @param label the new label
280 */
281 void setLabel(gchar const *label);
283 /** Retrieves the title of this object */
284 gchar *title() const;
285 /** Sets the title of this object */
286 bool setTitle(gchar const *title, bool verbatim=false);
288 /** Retrieves the description of this object */
289 gchar *desc() const;
290 /** Sets the description of this object */
291 bool setDesc(gchar const *desc, bool verbatim=false);
293 /** @brief Set the policy under which this object will be
294 * orphan-collected.
295 *
296 * Orphan-collection is the process of deleting all objects which no longer have
297 * hyper-references pointing to them. The policy determines when this happens. Many objects
298 * should not be deleted simply because they are no longer referred to; other objects (like
299 * "intermediate" gradients) are more or less throw-away and should always be collected when no
300 * longer in use.
301 *
302 * Along these lines, there are currently two orphan-collection policies:
303 *
304 * COLLECT_WITH_PARENT - don't worry about the object's hrefcount;
305 * if its parent is collected, this object
306 * will be too
307 *
308 * COLLECT_ALWAYS - always collect the object as soon as its
309 * hrefcount reaches zero
310 *
311 * @returns the current collection policy in effect for this object
312 */
313 CollectionPolicy collectionPolicy() const { return _collection_policy; }
315 /** @brief Sets the orphan-collection policy in effect for this object.
316 *
317 * @see SPObject::collectionPolicy
318 *
319 * @param policy the new policy to adopt
320 */
321 void setCollectionPolicy(CollectionPolicy policy) {
322 _collection_policy = policy;
323 }
325 /** @brief Requests a later automatic call to collectOrphan().
326 *
327 * This method requests that collectOrphan() be called during the document update cycle,
328 * deleting the object if it is no longer used.
329 *
330 * If the current collection policy is COLLECT_WITH_PARENT, this function has no effect.
331 *
332 * @see SPObject::collectOrphan
333 */
334 void requestOrphanCollection();
336 /** @brief Unconditionally delete the object if it is not referenced.
337 *
338 * Unconditionally delete the object if there are no outstanding hyper-references to it.
339 * Observers are not notified of the object's deletion (at the SPObject level; XML tree
340 * notifications still fire).
341 *
342 * @see SPObject::deleteObject
343 */
344 void collectOrphan() {
345 if ( _total_hrefcount == 0 ) {
346 deleteObject(false);
347 }
348 }
350 /** @brief Check if object is referenced by any other object.
351 */
352 bool isReferenced() { return ( _total_hrefcount > 0 ); }
354 /** @brief Deletes an object.
355 *
356 * Detaches the object's repr, and optionally sends notification that the object has been
357 * deleted.
358 *
359 * @param propagate notify observers that the object has been deleted?
360 *
361 * @param propagate_descendants notify observers of children that they have been deleted?
362 */
363 void deleteObject(bool propagate, bool propagate_descendants);
365 /** @brief Deletes on object.
366 *
367 * @param propagate Notify observers of this object and its children that they have been
368 * deleted?
369 */
370 void deleteObject(bool propagate=true) {
371 deleteObject(propagate, propagate);
372 }
374 /** @brief Connects a slot to be called when an object is deleted.
375 *
376 * This connects a slot to an object's internal delete signal, which is invoked when the object
377 * is deleted
378 *
379 * The signal is mainly useful for e.g. knowing when to break hrefs or dissociate clones.
380 *
381 * @param slot the slot to connect
382 *
383 * @see SPObject::deleteObject
384 */
385 sigc::connection connectDelete(sigc::slot<void, SPObject *> slot) {
386 return _delete_signal.connect(slot);
387 }
389 sigc::connection connectPositionChanged(sigc::slot<void, SPObject *> slot) {
390 return _position_changed_signal.connect(slot);
391 }
393 /** @brief Returns the object which supercedes this one (if any).
394 *
395 * This is mainly useful for ensuring we can correctly perform a series of moves or deletes,
396 * even if the objects in question have been replaced in the middle of the sequence.
397 */
398 SPObject *successor() { return _successor; }
400 /** @brief Indicates that another object supercedes this one. */
401 void setSuccessor(SPObject *successor) {
402 g_assert(successor != NULL);
403 g_assert(_successor == NULL);
404 g_assert(successor->_successor == NULL);
405 sp_object_ref(successor, NULL);
406 _successor = successor;
407 }
409 /* modifications; all three sets of methods should probably ultimately be protected, as they
410 * are not really part of its public interface. However, other parts of the code to
411 * occasionally use them at present. */
413 /* the no-argument version of updateRepr() is intended to be a bit more public, however -- it
414 * essentially just flushes any changes back to the backing store (the repr layer); maybe it
415 * should be called something else and made public at that point. */
417 /** @brief Updates the object's repr based on the object's state.
418 *
419 * This method updates the the repr attached to the object to reflect the object's current
420 * state; see the three-argument version for details.
421 *
422 * @param flags object write flags that apply to this update
423 *
424 * @return the updated repr
425 */
426 Inkscape::XML::Node *updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT);
428 /** @brief Updates the given repr based on the object's state.
429 *
430 * This method updates the given repr to reflect the object's current state. There are
431 * several flags that affect this:
432 *
433 * SP_OBJECT_WRITE_BUILD - create new reprs
434 *
435 * SP_OBJECT_WRITE_EXT - write elements and attributes
436 * which are not part of pure SVG
437 * (i.e. the Inkscape and Sodipodi
438 * namespaces)
439 *
440 * SP_OBJECT_WRITE_ALL - create all nodes and attributes,
441 * even those which might be redundant
442 *
443 * @param repr the repr to update
444 * @param flags object write flags that apply to this update
445 *
446 * @return the updated repr
447 */
448 Inkscape::XML::Node *updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags);
450 /** @brief Queues an deferred update of this object's display.
451 *
452 * This method sets flags to indicate updates to be performed later, during the idle loop.
453 *
454 * There are several flags permitted here:
455 *
456 * SP_OBJECT_MODIFIED_FLAG - the object has been modified
457 *
458 * SP_OBJECT_CHILD_MODIFIED_FLAG - a child of the object has been
459 * modified
460 *
461 * SP_OBJECT_STYLE_MODIFIED_FLAG - the object's style has been
462 * modified
463 *
464 * There are also some subclass-specific modified flags which are hardly ever used.
465 *
466 * One of either MODIFIED or CHILD_MODIFIED is required.
467 *
468 * @param flags flags indicating what to update
469 */
470 void requestDisplayUpdate(unsigned int flags);
472 /** @brief Updates the object's display immediately
473 *
474 * This method is called during the idle loop by SPDocument in order to update the object's
475 * display.
476 *
477 * One additional flag is legal here:
478 *
479 * SP_OBJECT_PARENT_MODIFIED_FLAG - the parent has been
480 * modified
481 *
482 * @param ctx an SPCtx which accumulates various state
483 * during the recursive update -- beware! some
484 * subclasses try to cast this to an SPItemCtx *
485 *
486 * @param flags flags indicating what to update (in addition
487 * to any already set flags)
488 */
489 void updateDisplay(SPCtx *ctx, unsigned int flags);
491 /** @brief Requests that a modification notification signal
492 * be emitted later (e.g. during the idle loop)
493 *
494 * @param flags flags indicating what has been modified
495 */
496 void requestModified(unsigned int flags);
498 /** @brief Emits a modification notification signal
499 *
500 * @param flags indicating what has been modified
501 */
502 void emitModified(unsigned int flags);
504 /** @brief Connects to the modification notification signal
505 *
506 * @param slot the slot to connect
507 *
508 * @returns the connection formed thereby
509 */
510 sigc::connection connectModified(
511 sigc::slot<void, SPObject *, unsigned int> slot
512 ) {
513 return _modified_signal.connect(slot);
514 }
516 /** Sends the delete signal to all children of this object recursively */
517 void _sendDeleteSignalRecursive();
519 void _updateTotalHRefCount(int increment);
521 void _requireSVGVersion(unsigned major, unsigned minor) {
522 _requireSVGVersion(Inkscape::Version(major, minor));
523 }
524 void _requireSVGVersion(Inkscape::Version version);
526 sigc::signal<void, SPObject *> _release_signal;
527 sigc::signal<void, SPObject *> _delete_signal;
528 sigc::signal<void, SPObject *> _position_changed_signal;
529 sigc::signal<void, SPObject *, unsigned int> _modified_signal;
530 SPObject *_successor;
531 CollectionPolicy _collection_policy;
532 gchar *_label;
533 mutable gchar *_default_label;
535 // WARNING:
536 // Methods below should not be used outside of the SP tree,
537 // as they operate directly on the XML representation.
538 // In future, they will be made protected.
539 void attach(SPObject *object, SPObject *prev);
540 void reorder(SPObject *prev);
541 void detach(SPObject *object);
542 SPObject *get_child_by_repr(Inkscape::XML::Node *repr);
543 void invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned);
544 long long int getIntAttribute(char const *key, long long int def);
545 unsigned getPosition();
546 gchar const * getAttribute(gchar const *name,SPException *ex=0) const;
547 void appendChild(Inkscape::XML::Node *child);
548 void addChild(Inkscape::XML::Node *child,Inkscape::XML::Node *prev=0);
549 void setKeyValue(unsigned int key, gchar const *value);
550 void setAttribute(gchar const *key, gchar const *value, SPException *ex=0);
551 void readAttr(gchar const *key);
552 gchar const *getTagName(SPException *ex) const;
553 void removeAttribute(gchar const *key, SPException *ex=0);
554 gchar const *getStyleProperty(gchar const *key, gchar const *def) const;
555 void setCSS(SPCSSAttr *css, gchar const *attr);
556 void changeCSS(SPCSSAttr *css, gchar const *attr);
557 bool storeAsDouble( gchar const *key, double *val ) const;
559 private:
560 // Private member functions used in the definitions of setTitle(),
561 // setDesc(), title() and desc().
562 bool setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim);
563 gchar * getTitleOrDesc(gchar const *svg_tagname) const;
564 SPObject * findFirstChild(gchar const *tagname) const;
565 GString * textualContent() const;
567 static void sp_object_init(SPObject *object);
568 static void sp_object_finalize(GObject *object);
570 static void sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref);
571 static void sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child);
572 static void sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *old_ref, Inkscape::XML::Node *new_ref);
574 static void sp_object_release(SPObject *object);
575 static void sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
577 static void sp_object_private_set(SPObject *object, unsigned int key, gchar const *value);
578 static Inkscape::XML::Node *sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
579 static gchar *sp_object_get_unique_id(SPObject *object, gchar const *defid);
581 /* Real handlers of repr signals */
583 public:
584 static GType sp_object_get_type();
585 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);
587 static void sp_object_repr_content_changed(Inkscape::XML::Node *repr, gchar const *oldcontent, gchar const *newcontent, gpointer data);
589 static void sp_object_repr_child_added(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data);
590 static void sp_object_repr_child_removed(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, void *data);
592 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);
595 friend class SPObjectClass;
596 friend class SPObjectImpl;
597 };
599 /// The SPObject vtable.
600 class SPObjectClass {
601 public:
602 GObjectClass parent_class;
604 void (* build) (SPObject *object, SPDocument *doc, Inkscape::XML::Node *repr);
605 void (* release) (SPObject *object);
607 /* Virtual handlers of repr signals */
608 void (* child_added) (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref);
609 void (* remove_child) (SPObject *object, Inkscape::XML::Node *child);
611 void (* order_changed) (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *new_repr);
613 void (* set) (SPObject *object, unsigned int key, gchar const *value);
615 void (* read_content) (SPObject *object);
617 /* Update handler */
618 void (* update) (SPObject *object, SPCtx *ctx, unsigned int flags);
619 /* Modification handler */
620 void (* modified) (SPObject *object, unsigned int flags);
622 Inkscape::XML::Node * (* write) (SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags);
624 private:
625 static GObjectClass *static_parent_class;
626 static void sp_object_class_init(SPObjectClass *klass);
628 friend class SPObject;
629 };
632 int sp_object_compare_position(SPObject const *first, SPObject const *second);
635 #endif // SP_OBJECT_H_SEEN
638 /*
639 Local Variables:
640 mode:c++
641 c-file-style:"stroustrup"
642 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
643 indent-tabs-mode:nil
644 fill-column:99
645 End:
646 */
647 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :