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 *
10 * Copyright (C) 1999-2002 authors
11 * Copyright (C) 2001-2002 Ximian, Inc.
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 /* SPObject flags */
18 /* Async modification flags */
19 #define SP_OBJECT_MODIFIED_FLAG (1 << 0)
20 #define SP_OBJECT_CHILD_MODIFIED_FLAG (1 << 1)
21 #define SP_OBJECT_PARENT_MODIFIED_FLAG (1 << 2)
22 #define SP_OBJECT_STYLE_MODIFIED_FLAG (1 << 3)
23 #define SP_OBJECT_VIEWPORT_MODIFIED_FLAG (1 << 4)
24 #define SP_OBJECT_USER_MODIFIED_FLAG_A (1 << 5)
25 #define SP_OBJECT_USER_MODIFIED_FLAG_B (1 << 6)
26 #define SP_OBJECT_USER_MODIFIED_FLAG_C (1 << 7)
28 /* Conveneience */
29 #define SP_OBJECT_FLAGS_ALL 0xff
31 /* Flags that mark object as modified */
32 /* Object, Child, Style, Viewport, User */
33 #define SP_OBJECT_MODIFIED_STATE (SP_OBJECT_FLAGS_ALL & ~(SP_OBJECT_PARENT_MODIFIED_FLAG))
35 /* Flags that will propagate downstreams */
36 /* Parent, Style, Viewport, User */
37 #define SP_OBJECT_MODIFIED_CASCADE (SP_OBJECT_FLAGS_ALL & ~(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))
39 /* Generic */
40 #define SP_OBJECT_IS_CLONED(o) (((SPObject *) (o))->cloned)
42 /* Write flags */
43 #define SP_OBJECT_WRITE_BUILD (1 << 0)
44 #define SP_OBJECT_WRITE_EXT (1 << 1)
45 #define SP_OBJECT_WRITE_ALL (1 << 2)
47 /* Convenience stuff */
48 #define SP_OBJECT_ID(o) (((SPObject *) (o))->id)
49 #define SP_OBJECT_REPR(o) (((SPObject *) (o))->repr)
50 #define SP_OBJECT_DOCUMENT(o) (((SPObject *) (o))->document)
51 #define SP_OBJECT_PARENT(o) (((SPObject *) (o))->parent)
52 #define SP_OBJECT_NEXT(o) (((SPObject *) (o))->next)
53 #define SP_OBJECT_PREV(o) (sp_object_prev((SPObject *) (o)))
54 #define SP_OBJECT_HREFCOUNT(o) (((SPObject *) (o))->hrefcount)
55 #define SP_OBJECT_STYLE(o) (((SPObject *) (o))->style)
58 #include <glib-object.h>
59 #include <sigc++/connection.h>
60 #include <sigc++/functors/slot.h>
61 #include <sigc++/signal.h>
63 #include "forward.h"
64 #include "version.h"
65 #include "util/forward-pointer-iterator.h"
67 namespace Inkscape {
68 namespace XML {
69 class Node;
70 class Document;
71 }
72 }
75 typedef enum {
76 SP_NO_EXCEPTION,
77 SP_INDEX_SIZE_ERR,
78 SP_DOMSTRING_SIZE_ERR,
79 SP_HIERARCHY_REQUEST_ERR,
80 SP_WRONG_DOCUMENT_ERR,
81 SP_INVALID_CHARACTER_ERR,
82 SP_NO_DATA_ALLOWED_ERR,
83 SP_NO_MODIFICATION_ALLOWED_ERR,
84 SP_NOT_FOUND_ERR,
85 SP_NOT_SUPPORTED_ERR,
86 SP_INUSE_ATTRIBUTE_ERR,
87 SP_INVALID_STATE_ERR,
88 SP_SYNTAX_ERR,
89 SP_INVALID_MODIFICATION_ERR,
90 SP_NAMESPACE_ERR,
91 SP_INVALID_ACCESS_ERR
92 } SPExceptionType;
94 class SPException;
96 /// An attempt to implement exceptions, unused?
97 struct SPException {
98 SPExceptionType code;
99 };
101 #define SP_EXCEPTION_INIT(ex) {(ex)->code = SP_NO_EXCEPTION;}
102 #define SP_EXCEPTION_IS_OK(ex) (!(ex) || ((ex)->code == SP_NO_EXCEPTION))
104 class SPCtx;
106 /// Unused
107 struct SPCtx {
108 unsigned int flags;
109 };
111 enum {
112 SP_XML_SPACE_DEFAULT,
113 SP_XML_SPACE_PRESERVE
114 };
116 class SPIXmlSpace;
118 /// Internal class consisting of two bits.
119 struct SPIXmlSpace {
120 guint set : 1;
121 guint value : 1;
122 };
124 class SPObject;
126 /*
127 * Refcounting
128 *
129 * Owner is here for debug reasons, you can set it to NULL safely
130 * Ref should return object, NULL is error, unref return always NULL
131 */
133 SPObject *sp_object_ref(SPObject *object, SPObject *owner=NULL);
134 SPObject *sp_object_unref(SPObject *object, SPObject *owner=NULL);
136 SPObject *sp_object_href(SPObject *object, gpointer owner);
137 SPObject *sp_object_hunref(SPObject *object, gpointer owner);
139 /// A refcounting tree node object.
140 struct SPObject : public GObject {
141 enum CollectionPolicy {
142 COLLECT_WITH_PARENT,
143 ALWAYS_COLLECT
144 };
146 unsigned int cloned : 1;
147 unsigned int uflags : 8;
148 unsigned int mflags : 8;
149 SPIXmlSpace xml_space;
150 unsigned int hrefcount; /* number of xlink:href references */
151 unsigned int _total_hrefcount; /* our hrefcount + total descendants */
152 SPDocument *document; /* Document we are part of */
153 SPObject *parent; /* Our parent (only one allowed) */
154 SPObject *children; /* Our children */
155 SPObject *_last_child; /* Remembered last child */
156 SPObject *next; /* Next object in linked list */
157 Inkscape::XML::Node *repr; /* Our xml representation */
159 private:
160 gchar *id; /* Our very own unique id */
161 public:
163 /**
164 * Returns the objects current ID string.
165 */
166 gchar const* getId() const;
168 /** @brief cleans up an SPObject, releasing its references and
169 * requesting that references to it be released
170 */
171 void releaseReferences();
173 /** @brief connects to the release request signal
174 *
175 * @param slot the slot to connect
176 *
177 * @returns the sigc::connection formed
178 */
179 sigc::connection connectRelease(sigc::slot<void, SPObject *> slot) {
180 return _release_signal.connect(slot);
181 }
183 /**
184 * Represents the style properties, whether from presentation attributes, the <tt>style</tt>
185 * attribute, or inherited.
186 *
187 * sp_object_private_set doesn't handle SP_ATTR_STYLE or any presentation attributes at the
188 * time of writing, so this is probably NULL for all SPObject's that aren't an SPItem.
189 *
190 * However, this gives rise to the bugs mentioned in sp_object_get_style_property.
191 * Note that some non-SPItem SPObject's, such as SPStop, do need styling information,
192 * and need to inherit properties even through other non-SPItem parents like \<defs\>.
193 */
194 SPStyle *style;
196 /// Switch containing next() method.
197 struct ParentIteratorStrategy {
198 static SPObject const *next(SPObject const *object) {
199 return object->parent;
200 }
201 };
202 /// Switch containing next() method.
203 struct SiblingIteratorStrategy {
204 static SPObject const *next(SPObject const *object) {
205 return object->next;
206 }
207 };
209 typedef Inkscape::Util::ForwardPointerIterator<SPObject, ParentIteratorStrategy> ParentIterator;
210 typedef Inkscape::Util::ForwardPointerIterator<SPObject const, ParentIteratorStrategy> ConstParentIterator;
211 typedef Inkscape::Util::ForwardPointerIterator<SPObject, SiblingIteratorStrategy> SiblingIterator;
212 typedef Inkscape::Util::ForwardPointerIterator<SPObject const, SiblingIteratorStrategy> ConstSiblingIterator;
214 bool isSiblingOf(SPObject const *object) const {
215 g_return_val_if_fail(object != NULL, false);
216 return this->parent && this->parent == object->parent;
217 }
218 bool isAncestorOf(SPObject const *object) const;
220 SPObject const *nearestCommonAncestor(SPObject const *object) const;
221 /* A non-const version can be similarly constructed if you want one.
222 * (Don't just cast away the constness, which would be ill-formed.) */
224 SPObject *getNext();
225 SPObject *getPrev();
227 bool hasChildren() const { return ( children != NULL ); }
229 SPObject *firstChild() { return children; }
230 SPObject const *firstChild() const { return children; }
231 SPObject *lastChild() { return _last_child; }
232 SPObject const *lastChild() const { return _last_child; }
234 enum Action { ActionGeneral, ActionBBox, ActionUpdate, ActionShow };
235 /** @brief Retrieves children as a GSList */
236 GSList *childList(bool add_ref, Action action = ActionGeneral);
238 SPObject *appendChildRepr(Inkscape::XML::Node *repr);
240 /** @brief Gets the author-visible label for this object. */
241 gchar const *label() const;
242 /** @brief Returns a default label for this object. */
243 gchar const *defaultLabel() const;
244 /** @brief Sets the author-visible label for this object.
245 *
246 * Sets the author-visible label for the object.
247 *
248 * @param label the new label
249 */
250 void setLabel(gchar const *label);
252 /** Retrieves the title of this object */
253 gchar *title() const;
254 /** Sets the title of this object */
255 bool setTitle(gchar const *title, bool verbatim=false);
257 /** Retrieves the description of this object */
258 gchar *desc() const;
259 /** Sets the description of this object */
260 bool setDesc(gchar const *desc, bool verbatim=false);
262 /** @brief Set the policy under which this object will be
263 * orphan-collected.
264 *
265 * Orphan-collection is the process of deleting all objects which no longer have
266 * hyper-references pointing to them. The policy determines when this happens. Many objects
267 * should not be deleted simply because they are no longer referred to; other objects (like
268 * "intermediate" gradients) are more or less throw-away and should always be collected when no
269 * longer in use.
270 *
271 * Along these lines, there are currently two orphan-collection policies:
272 *
273 * COLLECT_WITH_PARENT - don't worry about the object's hrefcount;
274 * if its parent is collected, this object
275 * will be too
276 *
277 * COLLECT_ALWAYS - always collect the object as soon as its
278 * hrefcount reaches zero
279 *
280 * @returns the current collection policy in effect for this object
281 */
282 CollectionPolicy collectionPolicy() const { return _collection_policy; }
284 /** @brief Sets the orphan-collection policy in effect for this object.
285 *
286 * @see SPObject::collectionPolicy
287 *
288 * @param policy the new policy to adopt
289 */
290 void setCollectionPolicy(CollectionPolicy policy) {
291 _collection_policy = policy;
292 }
294 /** @brief Requests a later automatic call to collectOrphan().
295 *
296 * This method requests that collectOrphan() be called during the document update cycle,
297 * deleting the object if it is no longer used.
298 *
299 * If the current collection policy is COLLECT_WITH_PARENT, this function has no effect.
300 *
301 * @see SPObject::collectOrphan
302 */
303 void requestOrphanCollection();
305 /** @brief Unconditionally delete the object if it is not referenced.
306 *
307 * Unconditionally delete the object if there are no outstanding hyper-references to it.
308 * Observers are not notified of the object's deletion (at the SPObject level; XML tree
309 * notifications still fire).
310 *
311 * @see SPObject::deleteObject
312 */
313 void collectOrphan() {
314 if ( _total_hrefcount == 0 ) {
315 deleteObject(false);
316 }
317 }
319 /** @brief Check if object is referenced by any other object.
320 */
321 bool isReferenced() { return ( _total_hrefcount > 0 ); }
323 /** @brief Deletes an object.
324 *
325 * Detaches the object's repr, and optionally sends notification that the object has been
326 * deleted.
327 *
328 * @param propagate notify observers that the object has been deleted?
329 *
330 * @param propagate_descendants notify observers of children that they have been deleted?
331 */
332 void deleteObject(bool propagate, bool propagate_descendants);
334 /** @brief Deletes on object.
335 *
336 * @param propagate Notify observers of this object and its children that they have been
337 * deleted?
338 */
339 void deleteObject(bool propagate=true) {
340 deleteObject(propagate, propagate);
341 }
343 /** @brief Connects a slot to be called when an object is deleted.
344 *
345 * This connects a slot to an object's internal delete signal, which is invoked when the object
346 * is deleted
347 *
348 * The signal is mainly useful for e.g. knowing when to break hrefs or dissociate clones.
349 *
350 * @param slot the slot to connect
351 *
352 * @see SPObject::deleteObject
353 */
354 sigc::connection connectDelete(sigc::slot<void, SPObject *> slot) {
355 return _delete_signal.connect(slot);
356 }
358 sigc::connection connectPositionChanged(sigc::slot<void, SPObject *> slot) {
359 return _position_changed_signal.connect(slot);
360 }
362 /** @brief Returns the object which supercedes this one (if any).
363 *
364 * This is mainly useful for ensuring we can correctly perform a series of moves or deletes,
365 * even if the objects in question have been replaced in the middle of the sequence.
366 */
367 SPObject *successor() { return _successor; }
369 /** @brief Indicates that another object supercedes this one. */
370 void setSuccessor(SPObject *successor) {
371 g_assert(successor != NULL);
372 g_assert(_successor == NULL);
373 g_assert(successor->_successor == NULL);
374 sp_object_ref(successor, NULL);
375 _successor = successor;
376 }
378 /* modifications; all three sets of methods should probably ultimately be protected, as they
379 * are not really part of its public interface. However, other parts of the code to
380 * occasionally use them at present. */
382 /* the no-argument version of updateRepr() is intended to be a bit more public, however -- it
383 * essentially just flushes any changes back to the backing store (the repr layer); maybe it
384 * should be called something else and made public at that point. */
386 /** @brief Updates the object's repr based on the object's state.
387 *
388 * This method updates the the repr attached to the object to reflect the object's current
389 * state; see the three-argument version for details.
390 *
391 * @param flags object write flags that apply to this update
392 *
393 * @return the updated repr
394 */
395 Inkscape::XML::Node *updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT);
397 /** @brief Updates the given repr based on the object's state.
398 *
399 * This method updates the given repr to reflect the object's current state. There are
400 * several flags that affect this:
401 *
402 * SP_OBJECT_WRITE_BUILD - create new reprs
403 *
404 * SP_OBJECT_WRITE_EXT - write elements and attributes
405 * which are not part of pure SVG
406 * (i.e. the Inkscape and Sodipodi
407 * namespaces)
408 *
409 * SP_OBJECT_WRITE_ALL - create all nodes and attributes,
410 * even those which might be redundant
411 *
412 * @param repr the repr to update
413 * @param flags object write flags that apply to this update
414 *
415 * @return the updated repr
416 */
417 Inkscape::XML::Node *updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags);
419 /** @brief Queues an deferred update of this object's display.
420 *
421 * This method sets flags to indicate updates to be performed later, during the idle loop.
422 *
423 * There are several flags permitted here:
424 *
425 * SP_OBJECT_MODIFIED_FLAG - the object has been modified
426 *
427 * SP_OBJECT_CHILD_MODIFIED_FLAG - a child of the object has been
428 * modified
429 *
430 * SP_OBJECT_STYLE_MODIFIED_FLAG - the object's style has been
431 * modified
432 *
433 * There are also some subclass-specific modified flags which are hardly ever used.
434 *
435 * One of either MODIFIED or CHILD_MODIFIED is required.
436 *
437 * @param flags flags indicating what to update
438 */
439 void requestDisplayUpdate(unsigned int flags);
441 /** @brief Updates the object's display immediately
442 *
443 * This method is called during the idle loop by SPDocument in order to update the object's
444 * display.
445 *
446 * One additional flag is legal here:
447 *
448 * SP_OBJECT_PARENT_MODIFIED_FLAG - the parent has been
449 * modified
450 *
451 * @param ctx an SPCtx which accumulates various state
452 * during the recursive update -- beware! some
453 * subclasses try to cast this to an SPItemCtx *
454 *
455 * @param flags flags indicating what to update (in addition
456 * to any already set flags)
457 */
458 void updateDisplay(SPCtx *ctx, unsigned int flags);
460 /** @brief Requests that a modification notification signal
461 * be emitted later (e.g. during the idle loop)
462 *
463 * @param flags flags indicating what has been modified
464 */
465 void requestModified(unsigned int flags);
467 /** @brief Emits a modification notification signal
468 *
469 * @param flags indicating what has been modified
470 */
471 void emitModified(unsigned int flags);
473 /** @brief Connects to the modification notification signal
474 *
475 * @param slot the slot to connect
476 *
477 * @returns the connection formed thereby
478 */
479 sigc::connection connectModified(
480 sigc::slot<void, SPObject *, unsigned int> slot
481 ) {
482 return _modified_signal.connect(slot);
483 }
485 void _sendDeleteSignalRecursive();
486 void _updateTotalHRefCount(int increment);
488 void _requireSVGVersion(unsigned major, unsigned minor) {
489 _requireSVGVersion(Inkscape::Version(major, minor));
490 }
491 void _requireSVGVersion(Inkscape::Version version);
493 sigc::signal<void, SPObject *> _release_signal;
494 sigc::signal<void, SPObject *> _delete_signal;
495 sigc::signal<void, SPObject *> _position_changed_signal;
496 sigc::signal<void, SPObject *, unsigned int> _modified_signal;
497 SPObject *_successor;
498 CollectionPolicy _collection_policy;
499 gchar *_label;
500 mutable gchar *_default_label;
502 private:
503 // Private member functions used in the definitions of setTitle(),
504 // setDesc(), title() and desc().
505 bool setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim);
506 gchar * getTitleOrDesc(gchar const *svg_tagname) const;
507 SPObject * findFirstChild(gchar const *tagname) const;
508 GString * textualContent() const;
510 friend class SPObjectImpl;
511 };
513 /// The SPObject vtable.
514 struct SPObjectClass {
515 GObjectClass parent_class;
517 void (* build) (SPObject *object, SPDocument *doc, Inkscape::XML::Node *repr);
518 void (* release) (SPObject *object);
520 /* Virtual handlers of repr signals */
521 void (* child_added) (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref);
522 void (* remove_child) (SPObject *object, Inkscape::XML::Node *child);
524 void (* order_changed) (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *new_repr);
526 void (* set) (SPObject *object, unsigned int key, gchar const *value);
528 void (* read_content) (SPObject *object);
530 /* Update handler */
531 void (* update) (SPObject *object, SPCtx *ctx, unsigned int flags);
532 /* Modification handler */
533 void (* modified) (SPObject *object, unsigned int flags);
535 Inkscape::XML::Node * (* write) (SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags);
536 };
539 /*
540 * Attaching/detaching
541 */
543 void sp_object_attach(SPObject *parent, SPObject *object, SPObject *prev);
544 void sp_object_reorder(SPObject *object, SPObject *prev);
545 void sp_object_detach(SPObject *parent, SPObject *object);
547 inline SPObject *sp_object_first_child(SPObject *parent) {
548 return parent->firstChild();
549 }
550 SPObject *sp_object_get_child_by_repr(SPObject *object, Inkscape::XML::Node *repr);
552 void sp_object_invoke_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned);
554 void sp_object_set(SPObject *object, unsigned int key, gchar const *value);
556 void sp_object_read_attr(SPObject *object, gchar const *key);
558 /* Public */
560 gchar const *sp_object_tagName_get(SPObject const *object, SPException *ex);
561 gchar const *sp_object_getAttribute(SPObject const *object, gchar const *key, SPException *ex);
562 void sp_object_setAttribute(SPObject *object, gchar const *key, gchar const *value, SPException *ex);
563 void sp_object_removeAttribute(SPObject *object, gchar const *key, SPException *ex);
565 /* Style */
567 gchar const *sp_object_get_style_property(SPObject const *object,
568 gchar const *key, gchar const *def);
570 int sp_object_compare_position(SPObject const *first, SPObject const *second);
572 SPObject *sp_object_prev(SPObject *child);
575 #endif // SP_OBJECT_H_SEEN
578 /*
579 Local Variables:
580 mode:c++
581 c-file-style:"stroustrup"
582 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
583 indent-tabs-mode:nil
584 fill-column:99
585 End:
586 */
587 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :