Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / sp-object.cpp
1 /** \file
2  * SPObject implementation.
3  *
4  * Authors:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *   bulia byak <buliabyak@users.sf.net>
7  *   Stephen Silver <sasilver@users.sourceforge.net>
8  *   Jon A. Cruz <jon@joncruz.org>
9  *   Abhishek Sharma
10  *
11  * Copyright (C) 1999-2008 authors
12  * Copyright (C) 2001-2002 Ximian, Inc.
13  *
14  * Released under GNU GPL, read the file 'COPYING' for more information
15  */
17 /** \class SPObject
18  *
19  * SPObject is an abstract base class of all of the document nodes at the
20  * SVG document level. Each SPObject subclass implements a certain SVG
21  * element node type, or is an abstract base class for different node
22  * types.  The SPObject layer is bound to the SPRepr layer, closely
23  * following the SPRepr mutations via callbacks.  During creation,
24  * SPObject parses and interprets all textual attributes and CSS style
25  * strings of the SPRepr, and later updates the internal state whenever
26  * it receives a signal about a change. The opposite is not true - there
27  * are methods manipulating SPObjects directly and such changes do not
28  * propagate to the SPRepr layer. This is important for implementation of
29  * the undo stack, animations and other features.
30  *
31  * SPObjects are bound to the higher-level container SPDocument, which
32  * provides document level functionality such as the undo stack,
33  * dictionary and so on. Source: doc/architecture.txt
34  */
36 #include <cstring>
37 #include <string>
39 #include "helper/sp-marshal.h"
40 #include "xml/node-event-vector.h"
41 #include "attributes.h"
42 #include "color-profile-fns.h"
43 #include "document.h"
44 #include "style.h"
45 #include "sp-object-repr.h"
46 #include "sp-root.h"
47 #include "sp-style-elem.h"
48 #include "sp-script.h"
49 #include "streq.h"
50 #include "strneq.h"
51 #include "xml/repr.h"
52 #include "xml/node-fns.h"
53 #include "debug/event-tracker.h"
54 #include "debug/simple-event.h"
55 #include "debug/demangle.h"
56 #include "util/share.h"
57 #include "util/format.h"
58 #include "util/longest-common-suffix.h"
60 using std::memcpy;
61 using std::strchr;
62 using std::strcmp;
63 using std::strlen;
64 using std::strstr;
66 #define noSP_OBJECT_DEBUG_CASCADE
68 #define noSP_OBJECT_DEBUG
70 #ifdef SP_OBJECT_DEBUG
71 # define debug(f, a...) { g_print("%s(%d) %s:", \
72                                   __FILE__,__LINE__,__FUNCTION__); \
73                           g_print(f, ## a); \
74                           g_print("\n"); \
75                         }
76 #else
77 # define debug(f, a...) /**/
78 #endif
80 guint update_in_progress = 0; // guard against update-during-update
82 Inkscape::XML::NodeEventVector object_event_vector = {
83     SPObject::sp_object_repr_child_added,
84     SPObject::sp_object_repr_child_removed,
85     SPObject::sp_object_repr_attr_changed,
86     SPObject::sp_object_repr_content_changed,
87     SPObject::sp_object_repr_order_changed
88 };
90 // A friend class used to set internal members on SPObject so as to not expose settors in SPObject's public API
91 class SPObjectImpl
92 {
93 public:
95 /**
96  * Null's the id member of an SPObject without attempting to free prior contents.
97  */
98     static void setIdNull( SPObject* obj ) {
99         if (obj) {
100             obj->id = 0;
101         }
102     }
104 /**
105  * Sets the id member of an object, freeing any prior content.
106  */
107     static void setId( SPObject* obj, gchar const* id ) {
108         if (obj && (id != obj->id) ) {
109             if (obj->id) {
110                 g_free(obj->id);
111                 obj->id = 0;
112             }
113             if (id) {
114                 obj->id = g_strdup(id);
115             }
116         }
117     }
118 };
121 GObjectClass * SPObjectClass::static_parent_class = 0;
123 /**
124  * Registers the SPObject class with Gdk and returns its type number.
125  */
126 GType SPObject::sp_object_get_type()
128     static GType type = 0;
129     if (!type) {
130         GTypeInfo info = {
131             sizeof(SPObjectClass),
132             NULL, NULL,
133             (GClassInitFunc) SPObjectClass::sp_object_class_init,
134             NULL, NULL,
135             sizeof(SPObject),
136             16,
137             (GInstanceInitFunc) sp_object_init,
138             NULL
139         };
140         type = g_type_register_static(G_TYPE_OBJECT, "SPObject", &info, (GTypeFlags)0);
141     }
142     return type;
145 /**
146  * Initializes the SPObject vtable.
147  */
148 void SPObjectClass::sp_object_class_init(SPObjectClass *klass)
150     GObjectClass *object_class;
152     object_class = (GObjectClass *) klass;
154     static_parent_class = (GObjectClass *) g_type_class_ref(G_TYPE_OBJECT);
156     object_class->finalize = SPObject::sp_object_finalize;
158     klass->child_added = SPObject::sp_object_child_added;
159     klass->remove_child = SPObject::sp_object_remove_child;
160     klass->order_changed = SPObject::sp_object_order_changed;
162     klass->release = SPObject::sp_object_release;
164     klass->build = SPObject::sp_object_build;
166     klass->set = SPObject::sp_object_private_set;
167     klass->write = SPObject::sp_object_private_write;
170 /**
171  * Callback to initialize the SPObject object.
172  */
173 void SPObject::sp_object_init(SPObject *object)
175     debug("id=%x, typename=%s",object, g_type_name_from_instance((GTypeInstance*)object));
177     object->hrefcount = 0;
178     object->_total_hrefcount = 0;
179     object->document = NULL;
180     object->children = object->_last_child = NULL;
181     object->parent = object->next = NULL;
183     //used XML Tree here.
184     Inkscape::XML::Node *repr = object->getRepr();
185     repr = NULL;
186     SPObjectImpl::setIdNull(object);
188     object->_collection_policy = SPObject::COLLECT_WITH_PARENT;
190     new (&object->_release_signal) sigc::signal<void, SPObject *>();
191     new (&object->_modified_signal) sigc::signal<void, SPObject *, unsigned int>();
192     new (&object->_delete_signal) sigc::signal<void, SPObject *>();
193     new (&object->_position_changed_signal) sigc::signal<void, SPObject *>();
194     object->_successor = NULL;
196     // FIXME: now we create style for all objects, but per SVG, only the following can have style attribute:
197     // vg, g, defs, desc, title, symbol, use, image, switch, path, rect, circle, ellipse, line, polyline,
198     // polygon, text, tspan, tref, textPath, altGlyph, glyphRef, marker, linearGradient, radialGradient,
199     // stop, pattern, clipPath, mask, filter, feImage, a, font, glyph, missing-glyph, foreignObject
200     object->style = sp_style_new_from_object(object);
202     object->_label = NULL;
203     object->_default_label = NULL;
206 /**
207  * Callback to destroy all members and connections of object and itself.
208  */
209 void SPObject::sp_object_finalize(GObject *object)
211     SPObject *spobject = (SPObject *)object;
213     g_free(spobject->_label);
214     g_free(spobject->_default_label);
215     spobject->_label = NULL;
216     spobject->_default_label = NULL;
218     if (spobject->_successor) {
219         sp_object_unref(spobject->_successor, NULL);
220         spobject->_successor = NULL;
221     }
223     if (((GObjectClass *) (SPObjectClass::static_parent_class))->finalize) {
224         (* ((GObjectClass *) (SPObjectClass::static_parent_class))->finalize)(object);
225     }
227     spobject->_release_signal.~signal();
228     spobject->_modified_signal.~signal();
229     spobject->_delete_signal.~signal();
230     spobject->_position_changed_signal.~signal();
233 namespace {
235 namespace Debug = Inkscape::Debug;
236 namespace Util = Inkscape::Util;
238 typedef Debug::SimpleEvent<Debug::Event::REFCOUNT> BaseRefCountEvent;
240 class RefCountEvent : public BaseRefCountEvent {
241 public:
242     RefCountEvent(SPObject *object, int bias, Util::ptr_shared<char> name)
243     : BaseRefCountEvent(name)
244     {
245         _addProperty("object", Util::format("%p", object));
246         _addProperty("class", Debug::demangle(g_type_name(G_TYPE_FROM_INSTANCE(object))));
247         _addProperty("new-refcount", Util::format("%d", G_OBJECT(object)->ref_count + bias));
248     }
249 };
251 class RefEvent : public RefCountEvent {
252 public:
253     RefEvent(SPObject *object)
254     : RefCountEvent(object, 1, Util::share_static_string("sp-object-ref"))
255     {}
256 };
258 class UnrefEvent : public RefCountEvent {
259 public:
260     UnrefEvent(SPObject *object)
261     : RefCountEvent(object, -1, Util::share_static_string("sp-object-unref"))
262     {}
263 };
267 gchar const* SPObject::getId() const {
268     return id;
271 Inkscape::XML::Node * SPObject::getRepr() {
272     return repr;
275 Inkscape::XML::Node const* SPObject::getRepr() const{
276     return repr;
280 /**
281  * Increase reference count of object, with possible debugging.
282  *
283  * \param owner If non-NULL, make debug log entry.
284  * \return object, NULL is error.
285  * \pre object points to real object
286  */
287 SPObject *sp_object_ref(SPObject *object, SPObject *owner)
289     g_return_val_if_fail(object != NULL, NULL);
290     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
291     g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
293     Inkscape::Debug::EventTracker<RefEvent> tracker(object);
294     g_object_ref(G_OBJECT(object));
295     return object;
298 /**
299  * Decrease reference count of object, with possible debugging and
300  * finalization.
301  *
302  * \param owner If non-NULL, make debug log entry.
303  * \return always NULL
304  * \pre object points to real object
305  */
306 SPObject *sp_object_unref(SPObject *object, SPObject *owner)
308     g_return_val_if_fail(object != NULL, NULL);
309     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
310     g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
312     Inkscape::Debug::EventTracker<UnrefEvent> tracker(object);
313     g_object_unref(G_OBJECT(object));
314     return NULL;
317 /**
318  * Increase weak refcount.
319  *
320  * Hrefcount is used for weak references, for example, to
321  * determine whether any graphical element references a certain gradient
322  * node.
323  * \param owner Ignored.
324  * \return object, NULL is error
325  * \pre object points to real object
326  */
327 SPObject *sp_object_href(SPObject *object, gpointer /*owner*/)
329     g_return_val_if_fail(object != NULL, NULL);
330     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
332     object->hrefcount++;
333     object->_updateTotalHRefCount(1);
335     return object;
338 /**
339  * Decrease weak refcount.
340  *
341  * Hrefcount is used for weak references, for example, to determine whether
342  * any graphical element references a certain gradient node.
343  * \param owner Ignored.
344  * \return always NULL
345  * \pre object points to real object and hrefcount>0
346  */
347 SPObject *sp_object_hunref(SPObject *object, gpointer /*owner*/)
349     g_return_val_if_fail(object != NULL, NULL);
350     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
351     g_return_val_if_fail(object->hrefcount > 0, NULL);
353     object->hrefcount--;
354     object->_updateTotalHRefCount(-1);
356     return NULL;
359 /**
360  * Adds increment to _total_hrefcount of object and its parents.
361  */
362 void SPObject::_updateTotalHRefCount(int increment) {
363     SPObject *topmost_collectable = NULL;
364     for ( SPObject *iter = this ; iter ; iter = iter->parent ) {
365         iter->_total_hrefcount += increment;
366         if ( iter->_total_hrefcount < iter->hrefcount ) {
367             g_critical("HRefs overcounted");
368         }
369         if ( iter->_total_hrefcount == 0 &&
370              iter->_collection_policy != COLLECT_WITH_PARENT )
371         {
372             topmost_collectable = iter;
373         }
374     }
375     if (topmost_collectable) {
376         topmost_collectable->requestOrphanCollection();
377     }
380 /**
381  * True if object is non-NULL and this is some in/direct parent of object.
382  */
383 bool SPObject::isAncestorOf(SPObject const *object) const {
384     g_return_val_if_fail(object != NULL, false);
385     object = object->parent;
386     while (object) {
387         if ( object == this ) {
388             return true;
389         }
390         object = object->parent;
391     }
392     return false;
395 namespace {
397 bool same_objects(SPObject const &a, SPObject const &b) {
398     return &a == &b;
403 /**
404  * Returns youngest object being parent to this and object.
405  */
406 SPObject const *SPObject::nearestCommonAncestor(SPObject const *object) const {
407     g_return_val_if_fail(object != NULL, NULL);
409     using Inkscape::Algorithms::longest_common_suffix;
410     return longest_common_suffix<SPObject::ConstParentIterator>(this, object, NULL, &same_objects);
413 SPObject const *AncestorSon(SPObject const *obj, SPObject const *ancestor) {
414     SPObject const *result = 0;
415     if ( obj && ancestor ) {
416         if (obj->parent == ancestor) {
417             result = obj;
418         } else {
419             result = AncestorSon(obj->parent, ancestor);
420         }
421     }
422     return result;
425 /**
426  * Compares height of objects in tree.
427  *
428  * Works for different-parent objects, so long as they have a common ancestor.
429  * \return \verbatim
430  *    0    positions are equivalent
431  *    1    first object's position is greater than the second
432  *   -1    first object's position is less than the second   \endverbatim
433  */
434 int sp_object_compare_position(SPObject const *first, SPObject const *second)
436     int result = 0;
437     if (first != second) {
438         SPObject const *ancestor = first->nearestCommonAncestor(second);
439         // Need a common ancestor to be able to compare
440         if ( ancestor ) {
441             // we have an object and its ancestor (should not happen when sorting selection)
442             if (ancestor == first) {
443                 result = 1;
444             } else if (ancestor == second) {
445                 result = -1;
446             } else {
447                 SPObject const *to_first = AncestorSon(first, ancestor);
448                 SPObject const *to_second = AncestorSon(second, ancestor);
450                 g_assert(to_second->parent == to_first->parent);
452                 result = sp_repr_compare_position(to_first->getRepr(), to_second->getRepr());
453             }
454         }
455     }
456     return result;
460 /**
461  * Append repr as child of this object.
462  * \pre this is not a cloned object
463  */
464 SPObject *SPObject::appendChildRepr(Inkscape::XML::Node *repr) {
465     if ( !cloned ) {
466         getRepr()->appendChild(repr);
467         return document->getObjectByRepr(repr);
468     } else {
469         g_critical("Attempt to append repr as child of cloned object");
470         return NULL;
471     }
474 void SPObject::setCSS(SPCSSAttr *css, gchar const *attr)
476     g_assert(this->getRepr() != NULL);
477     sp_repr_css_set(this->getRepr(), css, attr);
480 void SPObject::changeCSS(SPCSSAttr *css, gchar const *attr)
482     g_assert(this->getRepr() != NULL);
483     sp_repr_css_change(this->getRepr(), css, attr);
486 GSList *SPObject::childList(bool add_ref, Action) {
487     GSList *l = NULL;
488     for ( SPObject *child = firstChild() ; child; child = child->getNext() ) {
489         if (add_ref) {
490             g_object_ref (G_OBJECT (child));
491         }
493         l = g_slist_prepend (l, child);
494     }
495     return l;
499 /** Gets the label property for the object or a default if no label
500  *  is defined.
501  */
502 gchar const *SPObject::label() const {
503     return _label;
506 /** Returns a default label property for the object. */
507 gchar const *SPObject::defaultLabel() const {
508     if (_label) {
509         return _label;
510     } else {
511         if (!_default_label) {
512             if (getId()) {
513                 _default_label = g_strdup_printf("#%s", getId());
514             } else {
515                 _default_label = g_strdup_printf("<%s>", getRepr()->name());
516             }
517         }
518         return _default_label;
519     }
522 /** Sets the label property for the object */
523 void SPObject::setLabel(gchar const *label) {
524     getRepr()->setAttribute("inkscape:label", label, false);
528 /** Queues the object for orphan collection */
529 void SPObject::requestOrphanCollection() {
530     g_return_if_fail(document != NULL);
532     // do not remove style or script elements (Bug #276244)
533     if (SP_IS_STYLE_ELEM(this)) {
534         // leave it
535     } else if (SP_IS_SCRIPT(this)) {
536         // leave it
537     } else if (SP_IS_PAINT_SERVER(this) && static_cast<SPPaintServer*>(this)->isSwatch() ) {
538         // leave it
539     } else if (IS_COLORPROFILE(this)) {
540         // leave it
541     } else {
542         document->queueForOrphanCollection(this);
544         /** \todo
545          * This is a temporary hack added to make fill&stroke rebuild its
546          * gradient list when the defs are vacuumed.  gradient-vector.cpp
547          * listens to the modified signal on defs, and now we give it that
548          * signal.  Mental says that this should be made automatic by
549          * merging SPObjectGroup with SPObject; SPObjectGroup would issue
550          * this signal automatically. Or maybe just derive SPDefs from
551          * SPObjectGroup?
552          */
554         this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
555     }
558 void SPObject::_sendDeleteSignalRecursive() {
559     for (SPObject *child = firstChild(); child; child = child->getNext()) {
560         child->_delete_signal.emit(child);
561         child->_sendDeleteSignalRecursive();
562     }
565 /**
566  * Deletes the object reference, unparenting it from its parent.
567  *
568  * If the \a propagate parameter is set to true, it emits a delete
569  * signal.  If the \a propagate_descendants parameter is true, it
570  * recursively sends the delete signal to children.
571  */
572 void SPObject::deleteObject(bool propagate, bool propagate_descendants)
574     sp_object_ref(this, NULL);
575     if (propagate) {
576         _delete_signal.emit(this);
577     }
578     if (propagate_descendants) {
579         this->_sendDeleteSignalRecursive();
580     }
582     Inkscape::XML::Node *repr = getRepr();
583     if (repr && sp_repr_parent(repr)) {
584         sp_repr_unparent(repr);
585     }
587     if (_successor) {
588         _successor->deleteObject(propagate, propagate_descendants);
589     }
590     sp_object_unref(this, NULL);
593 /**
594  * Put object into object tree, under parent, and behind prev;
595  * also update object's XML space.
596  */
597 void SPObject::attach(SPObject *object, SPObject *prev)
599     //g_return_if_fail(parent != NULL);
600     //g_return_if_fail(SP_IS_OBJECT(parent));
601     g_return_if_fail(object != NULL);
602     g_return_if_fail(SP_IS_OBJECT(object));
603     g_return_if_fail(!prev || SP_IS_OBJECT(prev));
604     g_return_if_fail(!prev || prev->parent == this);
605     g_return_if_fail(!object->parent);
607     sp_object_ref(object, this);
608     object->parent = this;
609     this->_updateTotalHRefCount(object->_total_hrefcount);
611     SPObject *next;
612     if (prev) {
613         next = prev->next;
614         prev->next = object;
615     } else {
616         next = this->children;
617         this->children = object;
618     }
619     object->next = next;
620     if (!next) {
621         this->_last_child = object;
622     }
623     if (!object->xml_space.set)
624         object->xml_space.value = this->xml_space.value;
627 /**
628  * In list of object's siblings, move object behind prev.
629  */
630 void SPObject::reorder(SPObject *prev) {
631     //g_return_if_fail(object != NULL);
632     //g_return_if_fail(SP_IS_OBJECT(object));
633     g_return_if_fail(this->parent != NULL);
634     g_return_if_fail(this != prev);
635     g_return_if_fail(!prev || SP_IS_OBJECT(prev));
636     g_return_if_fail(!prev || prev->parent == this->parent);
638     SPObject *const parent=this->parent;
640     SPObject *old_prev=NULL;
641     for ( SPObject *child = parent->children ; child && child != this ;
642           child = child->next )
643     {
644         old_prev = child;
645     }
647     SPObject *next=this->next;
648     if (old_prev) {
649         old_prev->next = next;
650     } else {
651         parent->children = next;
652     }
653     if (!next) {
654         parent->_last_child = old_prev;
655     }
656     if (prev) {
657         next = prev->next;
658         prev->next = this;
659     } else {
660         next = parent->children;
661         parent->children = this;
662     }
663     this->next = next;
664     if (!next) {
665         parent->_last_child = this;
666     }
669 /**
670  * Remove object from parent's children, release and unref it.
671  */
672 void SPObject::detach(SPObject *object) {
673     //g_return_if_fail(parent != NULL);
674     //g_return_if_fail(SP_IS_OBJECT(parent));
675     g_return_if_fail(object != NULL);
676     g_return_if_fail(SP_IS_OBJECT(object));
677     g_return_if_fail(object->parent == this);
679     object->releaseReferences();
681     SPObject *prev=NULL;
682     for ( SPObject *child = this->children ; child && child != object ;
683           child = child->next )
684     {
685         prev = child;
686     }
688     SPObject *next=object->next;
689     if (prev) {
690         prev->next = next;
691     } else {
692         this->children = next;
693     }
694     if (!next) {
695         this->_last_child = prev;
696     }
698     object->next = NULL;
699     object->parent = NULL;
701     this->_updateTotalHRefCount(-object->_total_hrefcount);
702     sp_object_unref(object, this);
705 /**
706  * Return object's child whose node pointer equals repr.
707  */
708 SPObject *SPObject::get_child_by_repr(Inkscape::XML::Node *repr)
710     g_return_val_if_fail(repr != NULL, NULL);
711     SPObject *result = 0;
713     if ( _last_child && (_last_child->getRepr() == repr) ) {
714         result = _last_child;   // optimization for common scenario
715     } else {
716         for ( SPObject *child = children ; child ; child = child->next ) {
717             if ( child->getRepr() == repr ) {
718                 result = child;
719                 break;
720             }
721         }
722     }
723     return result;
726 /**
727  * Callback for child_added event.
728  * Invoked whenever the given mutation event happens in the XML tree.
729  */
730 void SPObject::sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
732     GType type = sp_repr_type_lookup(child);
733     if (!type) {
734         return;
735     }
736     SPObject *ochild = SP_OBJECT(g_object_new(type, 0));
737     SPObject *prev = ref ? object->get_child_by_repr(ref) : NULL;
738     object->attach(ochild, prev);
739     sp_object_unref(ochild, NULL);
741     ochild->invoke_build(object->document, child, object->cloned);
744 /**
745  * Removes, releases and unrefs all children of object.
746  *
747  * This is the opposite of build. It has to be invoked as soon as the
748  * object is removed from the tree, even if it is still alive according
749  * to reference count. The frontend unregisters the object from the
750  * document and releases the SPRepr bindings; implementations should free
751  * state data and release all child objects.  Invoking release on
752  * SPRoot destroys the whole document tree.
753  * \see sp_object_build()
754  */
755 void SPObject::sp_object_release(SPObject *object)
757     debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
758     while (object->children) {
759         object->detach(object->children);
760     }
763 /**
764  * Remove object's child whose node equals repr, release and
765  * unref it.
766  *
767  * Invoked whenever the given mutation event happens in the XML
768  * tree, BEFORE removal from the XML tree happens, so grouping
769  * objects can safely release the child data.
770  */
771 void SPObject::sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child)
773     debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
774     SPObject *ochild = object->get_child_by_repr(child);
775     g_return_if_fail (ochild != NULL || !strcmp("comment", child->name())); // comments have no objects
776     if (ochild) {
777         object->detach(ochild);
778     }
781 /**
782  * Move object corresponding to child after sibling object corresponding
783  * to new_ref.
784  * Invoked whenever the given mutation event happens in the XML tree.
785  * \param old_ref Ignored
786  */
787 void SPObject::sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node */*old_ref*/,
788                                     Inkscape::XML::Node *new_ref)
790     SPObject *ochild = object->get_child_by_repr(child);
791     g_return_if_fail(ochild != NULL);
792     SPObject *prev = new_ref ? object->get_child_by_repr(new_ref) : NULL;
793     ochild->reorder(prev);
794     ochild->_position_changed_signal.emit(ochild);
797 /**
798  * Virtual build callback.
799  *
800  * This has to be invoked immediately after creation of an SPObject. The
801  * frontend method ensures that the new object is properly attached to
802  * the document and repr; implementation then will parse all of the attributes,
803  * generate the children objects and so on.  Invoking build on the SPRoot
804  * object results in creation of the whole document tree (this is, what
805  * SPDocument does after the creation of the XML tree).
806  * \see sp_object_release()
807  */
808 void SPObject::sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
810     /* Nothing specific here */
811     debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
813     object->readAttr("xml:space");
814     object->readAttr("inkscape:label");
815     object->readAttr("inkscape:collect");
817     for (Inkscape::XML::Node *rchild = repr->firstChild() ; rchild != NULL; rchild = rchild->next()) {
818         GType type = sp_repr_type_lookup(rchild);
819         if (!type) {
820             continue;
821         }
822         SPObject *child = SP_OBJECT(g_object_new(type, 0));
823         object->attach(child, object->lastChild());
824         sp_object_unref(child, NULL);
825         child->invoke_build(document, rchild, object->cloned);
826     }
829 void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
831     debug("id=%x, typename=%s", this, g_type_name_from_instance((GTypeInstance*)this));
833     //g_assert(object != NULL);
834     //g_assert(SP_IS_OBJECT(object));
835     g_assert(document != NULL);
836     g_assert(repr != NULL);
838     g_assert(this->document == NULL);
839     g_assert(this->repr == NULL);
840     g_assert(this->getId() == NULL);
842     /* Bookkeeping */
844     this->document = document;
845     this->repr = repr;
846     if (!cloned) {
847         Inkscape::GC::anchor(repr);
848     }
849     this->cloned = cloned;
851     if ( !cloned ) {
852         this->document->bindObjectToRepr(this->repr, this);
854         if (Inkscape::XML::id_permitted(this->repr)) {
855             /* If we are not cloned, and not seeking, force unique id */
856             gchar const *id = this->repr->attribute("id");
857             if (!document->isSeeking()) {
858                 {
859                     gchar *realid = sp_object_get_unique_id(this, id);
860                     g_assert(realid != NULL);
862                     this->document->bindObjectToId(realid, this);
863                     SPObjectImpl::setId(this, realid);
864                     g_free(realid);
865                 }
867                 /* Redefine ID, if required */
868                 if ((id == NULL) || (strcmp(id, this->getId()) != 0)) {
869                     this->repr->setAttribute("id", this->getId());
870                 }
871             } else if (id) {
872                 // bind if id, but no conflict -- otherwise, we can expect
873                 // a subsequent setting of the id attribute
874                 if (!this->document->getObjectById(id)) {
875                     this->document->bindObjectToId(id, this);
876                     SPObjectImpl::setId(this, id);
877                 }
878             }
879         }
880     } else {
881         g_assert(this->getId() == NULL);
882     }
884     /* Invoke derived methods, if any */
885     if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->build) {
886         (*((SPObjectClass *) G_OBJECT_GET_CLASS(this))->build)(this, document, repr);
887     }
889     /* Signalling (should be connected AFTER processing derived methods */
890     sp_repr_add_listener(repr, &object_event_vector, this);
893 long long int SPObject::getIntAttribute(char const *key, long long int def)
895     return sp_repr_get_int_attribute(getRepr(),key,def);
898 unsigned SPObject::getPosition(){
899     g_assert(this->repr);
901     return repr->position();
904 void SPObject::appendChild(Inkscape::XML::Node *child) {
905     g_assert(this->repr);
907     repr->appendChild(child);
910 void SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node * prev)
912     g_assert(this->repr);
914     repr->addChild(child,prev);
917 void SPObject::releaseReferences() {
918     g_assert(this->document);
919     g_assert(this->repr);
921     sp_repr_remove_listener_by_data(this->repr, this);
923     this->_release_signal.emit(this);
924     SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
925     if (klass->release) {
926         klass->release(this);
927     }
929     /* all hrefs should be released by the "release" handlers */
930     g_assert(this->hrefcount == 0);
932     if (!cloned) {
933         if (this->id) {
934             this->document->bindObjectToId(this->id, NULL);
935         }
936         g_free(this->id);
937         this->id = NULL;
939         g_free(this->_default_label);
940         this->_default_label = NULL;
942         this->document->bindObjectToRepr(this->repr, NULL);
944         Inkscape::GC::release(this->repr);
945     } else {
946         g_assert(!this->id);
947     }
949     if (this->style) {
950         this->style = sp_style_unref(this->style);
951     }
953     this->document = NULL;
954     this->repr = NULL;
958 SPObject *SPObject::getPrev()
960     SPObject *prev = 0;
961     for ( SPObject *obj = parent->firstChild(); obj && !prev; obj = obj->getNext() ) {
962         if (obj->getNext() == this) {
963             prev = obj;
964         }
965     }
966     return prev;
969 /**
970  * Callback for child_added node event.
971  */
972 void SPObject::sp_object_repr_child_added(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data)
974     SPObject *object = SP_OBJECT(data);
976     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->child_added) {
977         (*((SPObjectClass *)G_OBJECT_GET_CLASS(object))->child_added)(object, child, ref);
978     }
981 /**
982  * Callback for remove_child node event.
983  */
984 void SPObject::sp_object_repr_child_removed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node */*ref*/, gpointer data)
986     SPObject *object = SP_OBJECT(data);
988     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->remove_child) {
989         (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->remove_child)(object, child);
990     }
993 /**
994  * Callback for order_changed node event.
995  *
996  * \todo fixme:
997  */
998 void SPObject::sp_object_repr_order_changed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data)
1000     SPObject *object = SP_OBJECT(data);
1002     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->order_changed) {
1003         (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->order_changed)(object, child, old, newer);
1004     }
1007 /**
1008  * Callback for set event.
1009  */
1010 void SPObject::sp_object_private_set(SPObject *object, unsigned int key, gchar const *value)
1012     g_assert(key != SP_ATTR_INVALID);
1014     switch (key) {
1015         case SP_ATTR_ID:
1017             //XML Tree being used here.
1018             if ( !object->cloned && object->getRepr()->type() == Inkscape::XML::ELEMENT_NODE ) {
1019                 SPDocument *document=object->document;
1020                 SPObject *conflict=NULL;
1022                 gchar const *new_id = value;
1024                 if (new_id) {
1025                     conflict = document->getObjectById((char const *)new_id);
1026                 }
1028                 if ( conflict && conflict != object ) {
1029                     if (!document->isSeeking()) {
1030                         sp_object_ref(conflict, NULL);
1031                         // give the conflicting object a new ID
1032                         gchar *new_conflict_id = sp_object_get_unique_id(conflict, NULL);
1033                         conflict->getRepr()->setAttribute("id", new_conflict_id);
1034                         g_free(new_conflict_id);
1035                         sp_object_unref(conflict, NULL);
1036                     } else {
1037                         new_id = NULL;
1038                     }
1039                 }
1041                 if (object->getId()) {
1042                     document->bindObjectToId(object->getId(), NULL);
1043                     SPObjectImpl::setId(object, 0);
1044                 }
1046                 if (new_id) {
1047                     SPObjectImpl::setId(object, new_id);
1048                     document->bindObjectToId(object->getId(), object);
1049                 }
1051                 g_free(object->_default_label);
1052                 object->_default_label = NULL;
1053             }
1054             break;
1055         case SP_ATTR_INKSCAPE_LABEL:
1056             g_free(object->_label);
1057             if (value) {
1058                 object->_label = g_strdup(value);
1059             } else {
1060                 object->_label = NULL;
1061             }
1062             g_free(object->_default_label);
1063             object->_default_label = NULL;
1064             break;
1065         case SP_ATTR_INKSCAPE_COLLECT:
1066             if ( value && !strcmp(value, "always") ) {
1067                 object->setCollectionPolicy(SPObject::ALWAYS_COLLECT);
1068             } else {
1069                 object->setCollectionPolicy(SPObject::COLLECT_WITH_PARENT);
1070             }
1071             break;
1072         case SP_ATTR_XML_SPACE:
1073             if (value && !strcmp(value, "preserve")) {
1074                 object->xml_space.value = SP_XML_SPACE_PRESERVE;
1075                 object->xml_space.set = TRUE;
1076             } else if (value && !strcmp(value, "default")) {
1077                 object->xml_space.value = SP_XML_SPACE_DEFAULT;
1078                 object->xml_space.set = TRUE;
1079             } else if (object->parent) {
1080                 SPObject *parent;
1081                 parent = object->parent;
1082                 object->xml_space.value = parent->xml_space.value;
1083             }
1084             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1085             break;
1086         case SP_ATTR_STYLE:
1087             sp_style_read_from_object(object->style, object);
1088             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1089             break;
1090         default:
1091             break;
1092     }
1095 /**
1096  * Call virtual set() function of object.
1097  */
1098 void SPObject::setKeyValue(unsigned int key, gchar const *value)
1100     //g_assert(object != NULL);
1101     //g_assert(SP_IS_OBJECT(object));
1103     if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->set) {
1104         ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->set(this, key, value);
1105     }
1108 /**
1109  * Read value of key attribute from XML node into object.
1110  */
1111 void SPObject::readAttr(gchar const *key)
1113     //g_assert(object != NULL);
1114     //g_assert(SP_IS_OBJECT(object));
1115     g_assert(key != NULL);
1117     //XML Tree being used here.
1118     g_assert(this->getRepr() != NULL);
1120     unsigned int keyid = sp_attribute_lookup(key);
1121     if (keyid != SP_ATTR_INVALID) {
1122         /* Retrieve the 'key' attribute from the object's XML representation */
1123         gchar const *value = getRepr()->attribute(key);
1125         setKeyValue(keyid, value);
1126     }
1129 /**
1130  * Callback for attr_changed node event.
1131  */
1132 void SPObject::sp_object_repr_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *key, gchar const */*oldval*/, gchar const */*newval*/, bool is_interactive, gpointer data)
1134     SPObject *object = SP_OBJECT(data);
1136     object->readAttr(key);
1138     // manual changes to extension attributes require the normal
1139     // attributes, which depend on them, to be updated immediately
1140     if (is_interactive) {
1141         object->updateRepr(0);
1142     }
1145 /**
1146  * Callback for content_changed node event.
1147  */
1148 void SPObject::sp_object_repr_content_changed(Inkscape::XML::Node */*repr*/, gchar const */*oldcontent*/, gchar const */*newcontent*/, gpointer data)
1150     SPObject *object = SP_OBJECT(data);
1152     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content) {
1153         (*((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)(object);
1154     }
1157 /**
1158  * Return string representation of space value.
1159  */
1160 static gchar const*
1161 sp_xml_get_space_string(unsigned int space)
1163     switch (space) {
1164         case SP_XML_SPACE_DEFAULT:
1165             return "default";
1166         case SP_XML_SPACE_PRESERVE:
1167             return "preserve";
1168         default:
1169             return NULL;
1170     }
1173 /**
1174  * Callback for write event.
1175  */
1176 Inkscape::XML::Node * SPObject::sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
1178     if (!repr && (flags & SP_OBJECT_WRITE_BUILD)) {
1179         repr = object->getRepr()->duplicate(doc);
1180         if (!( flags & SP_OBJECT_WRITE_EXT )) {
1181             repr->setAttribute("inkscape:collect", NULL);
1182         }
1183     } else {
1184         repr->setAttribute("id", object->getId());
1186         if (object->xml_space.set) {
1187             char const *xml_space;
1188             xml_space = sp_xml_get_space_string(object->xml_space.value);
1189             repr->setAttribute("xml:space", xml_space);
1190         }
1192         if ( flags & SP_OBJECT_WRITE_EXT &&
1193              object->collectionPolicy() == SPObject::ALWAYS_COLLECT )
1194         {
1195             repr->setAttribute("inkscape:collect", "always");
1196         } else {
1197             repr->setAttribute("inkscape:collect", NULL);
1198         }
1200         SPStyle const *const obj_style = object->style;
1201         if (obj_style) {
1202             gchar *s = sp_style_write_string(obj_style, SP_STYLE_FLAG_IFSET);
1203             repr->setAttribute("style", ( *s ? s : NULL ));
1204             g_free(s);
1205         } else {
1206             /** \todo I'm not sure what to do in this case.  Bug #1165868
1207              * suggests that it can arise, but the submitter doesn't know
1208              * how to do so reliably.  The main two options are either
1209              * leave repr's style attribute unchanged, or explicitly clear it.
1210              * Must also consider what to do with property attributes for
1211              * the element; see below.
1212              */
1213             char const *style_str = repr->attribute("style");
1214             if (!style_str) {
1215                 style_str = "NULL";
1216             }
1217             g_warning("Item's style is NULL; repr style attribute is %s", style_str);
1218         }
1220         /** \note We treat object->style as authoritative.  Its effects have
1221          * been written to the style attribute above; any properties that are
1222          * unset we take to be deliberately unset (e.g. so that clones can
1223          * override the property).
1224          *
1225          * Note that the below has an undesirable consequence of changing the
1226          * appearance on renderers that lack CSS support (e.g. SVG tiny);
1227          * possibly we should write property attributes instead of a style
1228          * attribute.
1229          */
1230         sp_style_unset_property_attrs (object);
1231     }
1233     return repr;
1236 /**
1237  * Update this object's XML node with flags value.
1238  */
1239 Inkscape::XML::Node * SPObject::updateRepr(unsigned int flags) {
1240     if ( !cloned ) {
1241         Inkscape::XML::Node *repr = getRepr();
1242         if (repr) {
1243             return updateRepr(repr->document(), repr, flags);
1244         } else {
1245             g_critical("Attempt to update non-existent repr");
1246             return NULL;
1247         }
1248     } else {
1249         /* cloned objects have no repr */
1250         return NULL;
1251     }
1254 /** Used both to create reprs in the original document, and to create
1255  *  reprs in another document (e.g. a temporary document used when
1256  *  saving as "Plain SVG"
1257  */
1258 Inkscape::XML::Node * SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags) {
1259     g_assert(doc != NULL);
1261     if (cloned) {
1262         /* cloned objects have no repr */
1263         return NULL;
1264     }
1265     if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write) {
1266         if (!(flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1267             repr = getRepr();
1268         }
1269         return ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write(this, doc, repr, flags);
1270     } else {
1271         g_warning("Class %s does not implement ::write", G_OBJECT_TYPE_NAME(this));
1272         if (!repr) {
1273             if (flags & SP_OBJECT_WRITE_BUILD) {
1274                 repr = getRepr()->duplicate(doc);
1275             }
1276             /// \todo FIXME: else probably error (Lauris) */
1277         } else {
1278             repr->mergeFrom(getRepr(), "id");
1279         }
1280         return repr;
1281     }
1284 /* Modification */
1286 /**
1287  * Add \a flags to \a object's as dirtiness flags, and
1288  * recursively add CHILD_MODIFIED flag to
1289  * parent and ancestors (as far up as necessary).
1290  */
1291 void SPObject::requestDisplayUpdate(unsigned int flags)
1293     g_return_if_fail( this->document != NULL );
1295     if (update_in_progress) {
1296         g_print("WARNING: Requested update while update in progress, counter = %d\n", update_in_progress);
1297     }
1299     /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1300      * SP_OBJECT_CHILD_MODIFIED_FLAG */
1301     g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1302     g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1303     g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1305     bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1307     this->uflags |= flags;
1309     /* If requestModified has already been called on this object or one of its children, then we
1310      * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1311      */
1312     if (already_propagated) {
1313         if (parent) {
1314             parent->requestDisplayUpdate(SP_OBJECT_CHILD_MODIFIED_FLAG);
1315         } else {
1316             document->requestModified();
1317         }
1318     }
1321 /**
1322  * Update views
1323  */
1324 void SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
1326     g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1328     update_in_progress ++;
1330 #ifdef SP_OBJECT_DEBUG_CASCADE
1331     g_print("Update %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), getId(), flags, this->uflags, this->mflags);
1332 #endif
1334     /* Get this flags */
1335     flags |= this->uflags;
1336     /* Copy flags to modified cascade for later processing */
1337     this->mflags |= this->uflags;
1338     /* We have to clear flags here to allow rescheduling update */
1339     this->uflags = 0;
1341     // Merge style if we have good reasons to think that parent style is changed */
1342     /** \todo
1343      * I am not sure whether we should check only propagated
1344      * flag. We are currently assuming that style parsing is
1345      * done immediately. I think this is correct (Lauris).
1346      */
1347     if ((flags & SP_OBJECT_STYLE_MODIFIED_FLAG) && (flags & SP_OBJECT_PARENT_MODIFIED_FLAG)) {
1348         if (this->style && this->parent) {
1349             sp_style_merge_from_parent(this->style, this->parent->style);
1350         }
1351     }
1353     try
1354     {
1355         if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update) {
1356             ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);
1357         }
1358     }
1359     catch(...)
1360     {
1361         /** \todo
1362         * in case of catching an exception we need to inform the user somehow that the document is corrupted
1363         * maybe by implementing an document flag documentOk
1364         * or by a modal error dialog
1365         */
1366         g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
1367     }
1369     update_in_progress --;
1372 /**
1373  * Request modified always bubbles *up* the tree, as opposed to
1374  * request display update, which trickles down and relies on the
1375  * flags set during this pass...
1376  */
1377 void SPObject::requestModified(unsigned int flags)
1379     g_return_if_fail( this->document != NULL );
1381     /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1382      * SP_OBJECT_CHILD_MODIFIED_FLAG */
1383     g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1384     g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1385     g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1387     bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1389     this->mflags |= flags;
1391     /* If requestModified has already been called on this object or one of its children, then we
1392      * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1393      */
1394     if (already_propagated) {
1395         if (parent) {
1396             parent->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
1397         } else {
1398             document->requestModified();
1399         }
1400     }
1403 /**
1404  *  Emits the MODIFIED signal with the object's flags.
1405  *  The object's mflags are the original set aside during the update pass for
1406  *  later delivery here.  Once emitModified() is called, those flags don't
1407  *  need to be stored any longer.
1408  */
1409 void SPObject::emitModified(unsigned int flags)
1411     /* only the MODIFIED_CASCADE flag is legal here */
1412     g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1414 #ifdef SP_OBJECT_DEBUG_CASCADE
1415     g_print("Modified %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), getId(), flags, this->uflags, this->mflags);
1416 #endif
1418     flags |= this->mflags;
1419     /* We have to clear mflags beforehand, as signal handlers may
1420      * make changes and therefore queue new modification notifications
1421      * themselves. */
1422     this->mflags = 0;
1424     g_object_ref(G_OBJECT(this));
1425     SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
1426     if (klass->modified) {
1427         klass->modified(this, flags);
1428     }
1429     _modified_signal.emit(this, flags);
1430     g_object_unref(G_OBJECT(this));
1433 gchar const *SPObject::getTagName(SPException *ex) const
1435     g_assert(repr != NULL);
1436     /* If exception is not clear, return */
1437     if (!SP_EXCEPTION_IS_OK(ex)) {
1438         return NULL;
1439     }
1441     /// \todo fixme: Exception if object is NULL? */
1442     //XML Tree being used here.
1443     return getRepr()->name();
1446 gchar const *SPObject::getAttribute(gchar const *key, SPException *ex) const
1448     g_assert(this->repr != NULL);
1449     /* If exception is not clear, return */
1450     if (!SP_EXCEPTION_IS_OK(ex)) {
1451         return NULL;
1452     }
1454     /// \todo fixme: Exception if object is NULL? */
1455     //XML Tree being used here.
1456     return (gchar const *) getRepr()->attribute(key);
1459 void SPObject::setAttribute(gchar const *key, gchar const *value, SPException *ex)
1461     g_assert(this->repr != NULL);
1462     /* If exception is not clear, return */
1463     g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1465     /// \todo fixme: Exception if object is NULL? */
1466     //XML Tree being used here.
1467     getRepr()->setAttribute(key, value, false);
1470 void SPObject::removeAttribute(gchar const *key, SPException *ex)
1472     /* If exception is not clear, return */
1473     g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1475     /// \todo fixme: Exception if object is NULL? */
1476     //XML Tree being used here.
1477     getRepr()->setAttribute(key, NULL, false);
1480 bool SPObject::storeAsDouble( gchar const *key, double *val ) const
1482     g_assert(this->getRepr()!= NULL);
1483     return sp_repr_get_double(((Inkscape::XML::Node *)(this->getRepr())),key,val);
1486 /* Helper */
1488 gchar * SPObject::sp_object_get_unique_id(SPObject *object, gchar const *id)
1490     static unsigned long count = 0;
1492     g_assert(SP_IS_OBJECT(object));
1494     count++;
1496     //XML Tree being used here.
1497     gchar const *name = object->getRepr()->name();
1498     g_assert(name != NULL);
1500     gchar const *local = strchr(name, ':');
1501     if (local) {
1502         name = local + 1;
1503     }
1505     if (id != NULL) {
1506         if (object->document->getObjectById(id) == NULL) {
1507             return g_strdup(id);
1508         }
1509     }
1511     size_t const name_len = strlen(name);
1512     size_t const buflen = name_len + (sizeof(count) * 10 / 4) + 1;
1513     gchar *const buf = (gchar *) g_malloc(buflen);
1514     memcpy(buf, name, name_len);
1515     gchar *const count_buf = buf + name_len;
1516     size_t const count_buflen = buflen - name_len;
1517     do {
1518         ++count;
1519         g_snprintf(count_buf, count_buflen, "%lu", count);
1520     } while ( object->document->getObjectById(buf) != NULL );
1521     return buf;
1524 /* Style */
1526 /**
1527  * Returns an object style property.
1528  *
1529  * \todo
1530  * fixme: Use proper CSS parsing.  The current version is buggy
1531  * in a number of situations where key is a substring of the
1532  * style string other than as a property name (including
1533  * where key is a substring of a property name), and is also
1534  * buggy in its handling of inheritance for properties that
1535  * aren't inherited by default.  It also doesn't allow for
1536  * the case where the property is specified but with an invalid
1537  * value (in which case I believe the CSS2 error-handling
1538  * behaviour applies, viz. behave as if the property hadn't
1539  * been specified).  Also, the current code doesn't use CRSelEng
1540  * stuff to take a value from stylesheets.  Also, we aren't
1541  * setting any hooks to force an update for changes in any of
1542  * the inputs (i.e., in any of the elements that this function
1543  * queries).
1544  *
1545  * \par
1546  * Given that the default value for a property depends on what
1547  * property it is (e.g., whether to inherit or not), and given
1548  * the above comment about ignoring invalid values, and that the
1549  * repr parent isn't necessarily the right element to inherit
1550  * from (e.g., maybe we need to inherit from the referencing
1551  * <use> element instead), we should probably make the caller
1552  * responsible for ascending the repr tree as necessary.
1553  */
1554 gchar const * SPObject::getStyleProperty(gchar const *key, gchar const *def) const
1556     //g_return_val_if_fail(object != NULL, NULL);
1557     //g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
1558     g_return_val_if_fail(key != NULL, NULL);
1560     //XML Tree being used here.
1561     gchar const *style = getRepr()->attribute("style");
1562     if (style) {
1563         size_t const len = strlen(key);
1564         char const *p;
1565         while ( (p = strstr(style, key))
1566                 != NULL )
1567         {
1568             p += len;
1569             while ((*p <= ' ') && *p) {
1570                 p++;
1571             }
1572             if (*p++ != ':') {
1573                 break;
1574             }
1575             while ((*p <= ' ') && *p) {
1576                 p++;
1577             }
1578             size_t const inherit_len = sizeof("inherit") - 1;
1579             if (*p
1580                 && !(strneq(p, "inherit", inherit_len)
1581                      && (p[inherit_len] == '\0'
1582                          || p[inherit_len] == ';'
1583                          || g_ascii_isspace(p[inherit_len])))) {
1584                 return p;
1585             }
1586         }
1587     }
1589     //XML Tree being used here.
1590     gchar const *val = getRepr()->attribute(key);
1591     if (val && !streq(val, "inherit")) {
1592         return val;
1593     }
1594     if (this->parent) {
1595         return (this->parent)->getStyleProperty(key, def);
1596     }
1598     return def;
1601 /**
1602  * Lifts SVG version of all root objects to version.
1603  */
1604 void SPObject::_requireSVGVersion(Inkscape::Version version) {
1605     for ( SPObject::ParentIterator iter=this ; iter ; ++iter ) {
1606         SPObject *object=iter;
1607         if (SP_IS_ROOT(object)) {
1608             SPRoot *root=SP_ROOT(object);
1609             if ( root->version.svg < version ) {
1610                 root->version.svg = version;
1611             }
1612         }
1613     }
1616 /* Titles and descriptions */
1618 /* Note:
1619    Titles and descriptions are stored in 'title' and 'desc' child elements
1620    (see section 5.4 of the SVG 1.0 and 1.1 specifications).  The spec allows
1621    an element to have more than one 'title' child element, but strongly
1622    recommends against this and requires using the first one if a choice must
1623    be made.  The same applies to 'desc' elements.  Therefore, these functions
1624    ignore all but the first 'title' child element and first 'desc' child
1625    element, except when deleting a title or description.
1626 */
1628 /**
1629  * Returns the title of this object, or NULL if there is none.
1630  * The caller must free the returned string using g_free() - see comment
1631  * for getTitleOrDesc() below.
1632  */
1633 gchar * SPObject::title() const
1635     return getTitleOrDesc("svg:title");
1638 /**
1639  * Sets the title of this object
1640  * A NULL first argument is interpreted as meaning that the existing title
1641  * (if any) should be deleted.
1642  * The second argument is optional - see setTitleOrDesc() below for details.
1643  */
1644 bool SPObject::setTitle(gchar const *title, bool verbatim)
1646     return setTitleOrDesc(title, "svg:title", verbatim);
1649 /**
1650  * Returns the description of this object, or NULL if there is none.
1651  * The caller must free the returned string using g_free() - see comment
1652  * for getTitleOrDesc() below.
1653  */
1654 gchar * SPObject::desc() const
1656     return getTitleOrDesc("svg:desc");
1659 /**
1660  * Sets the description of this object.
1661  * A NULL first argument is interpreted as meaning that the existing
1662  * description (if any) should be deleted.
1663  * The second argument is optional - see setTitleOrDesc() below for details.
1664  */
1665 bool SPObject::setDesc(gchar const *desc, bool verbatim)
1667     return setTitleOrDesc(desc, "svg:desc", verbatim);
1670 /**
1671  * Returns the title or description of this object, or NULL if there is none.
1672  *
1673  * The SVG spec allows 'title' and 'desc' elements to contain text marked up
1674  * using elements from other namespaces.  Therefore, this function cannot
1675  * in general just return a pointer to an existing string - it must instead
1676  * construct a string containing the title or description without the mark-up.
1677  * Consequently, the return value is a newly allocated string (or NULL), and
1678  * must be freed (using g_free()) by the caller.
1679  */
1680 gchar * SPObject::getTitleOrDesc(gchar const *svg_tagname) const
1682     gchar *result = 0;
1683     SPObject *elem = findFirstChild(svg_tagname);
1684     if ( elem ) {
1685         result = g_string_free(elem->textualContent(), FALSE);
1686     }
1687     return result;
1690 /**
1691  * Sets or deletes the title or description of this object.
1692  * A NULL 'value' argument causes the title or description to be deleted.
1693  *
1694  * 'verbatim' parameter:
1695  * If verbatim==true, then the title or description is set to exactly the
1696  * specified value.  If verbatim==false then two exceptions are made:
1697  *   (1) If the specified value is just whitespace, then the title/description
1698  *       is deleted.
1699  *   (2) If the specified value is the same as the current value except for
1700  *       mark-up, then the current value is left unchanged.
1701  * This is usually the desired behaviour, so 'verbatim' defaults to false for
1702  * setTitle() and setDesc().
1703  *
1704  * The return value is true if a change was made to the title/description,
1705  * and usually false otherwise.
1706  */
1707 bool SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim)
1709     if (!verbatim) {
1710         // If the new title/description is just whitespace,
1711         // treat it as though it were NULL.
1712         if (value) {
1713             bool just_whitespace = true;
1714             for (const gchar *cp = value; *cp; ++cp) {
1715                 if (!std::strchr("\r\n \t", *cp)) {
1716                     just_whitespace = false;
1717                     break;
1718                 }
1719             }
1720             if (just_whitespace) {
1721                 value = NULL;
1722             }
1723         }
1724         // Don't stomp on mark-up if there is no real change.
1725         if (value) {
1726             gchar *current_value = getTitleOrDesc(svg_tagname);
1727             if (current_value) {
1728                 bool different = std::strcmp(current_value, value);
1729                 g_free(current_value);
1730                 if (!different) {
1731                     return false;
1732                 }
1733             }
1734         }
1735     }
1737     SPObject *elem = findFirstChild(svg_tagname);
1739     if (value == NULL) {
1740         if (elem == NULL) {
1741             return false;
1742         }
1743         // delete the title/description(s)
1744         while (elem) {
1745             elem->deleteObject();
1746             elem = findFirstChild(svg_tagname);
1747         }
1748         return true;
1749     }
1751     Inkscape::XML::Document *xml_doc = document->getReprDoc();
1753     if (elem == NULL) {
1754         // create a new 'title' or 'desc' element, putting it at the
1755         // beginning (in accordance with the spec's recommendations)
1756         Inkscape::XML::Node *xml_elem = xml_doc->createElement(svg_tagname);
1757         repr->addChild(xml_elem, NULL);
1758         elem = document->getObjectByRepr(xml_elem);
1759         Inkscape::GC::release(xml_elem);
1760     }
1761     else {
1762         // remove the current content of the 'text' or 'desc' element
1763         SPObject *child;
1764         while (NULL != (child = elem->firstChild())) child->deleteObject();
1765     }
1767     // add the new content
1768     elem->appendChildRepr(xml_doc->createTextNode(value));
1769     return true;
1772 /**
1773  * Find the first child of this object with a given tag name,
1774  * and return it.  Returns NULL if there is no matching child.
1775  */
1776 SPObject * SPObject::findFirstChild(gchar const *tagname) const
1778     for (SPObject *child = children; child; child = child->next)
1779     {
1780         if (child->repr->type() == Inkscape::XML::ELEMENT_NODE &&
1781             !strcmp(child->repr->name(), tagname)) {
1782             return child;
1783         }
1784     }
1785     return NULL;
1788 /**
1789  * Return the full textual content of an element (typically all the
1790  * content except the tags).
1791  * Must not be used on anything except elements.
1792  */
1793 GString * SPObject::textualContent() const
1795     GString* text = g_string_new("");
1797     for (const SPObject *child = firstChild(); child; child = child->next)
1798     {
1799         Inkscape::XML::NodeType child_type = child->repr->type();
1801         if (child_type == Inkscape::XML::ELEMENT_NODE) {
1802             GString * new_text = child->textualContent();
1803             g_string_append(text, new_text->str);
1804             g_string_free(new_text, TRUE);
1805         }
1806         else if (child_type == Inkscape::XML::TEXT_NODE) {
1807             g_string_append(text, child->repr->content());
1808         }
1809     }
1810     return text;
1813 /*
1814   Local Variables:
1815   mode:c++
1816   c-file-style:"stroustrup"
1817   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1818   indent-tabs-mode:nil
1819   fill-column:99
1820   End:
1821 */
1822 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :