Code

A simple layout document as to what, why and how is cppification.
[inkscape.git] / src / sp-object.cpp
1 #define __SP_OBJECT_C__
2 /** \file
3  * SPObject implementation.
4  *
5  * Authors:
6  *   Lauris Kaplinski <lauris@kaplinski.com>
7  *   bulia byak <buliabyak@users.sf.net>
8  *   Stephen Silver <sasilver@users.sourceforge.net>
9  *
10  * Copyright (C) 1999-2008 authors
11  * Copyright (C) 2001-2002 Ximian, Inc.
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 /** \class SPObject
17  *
18  * SPObject is an abstract base class of all of the document nodes at the
19  * SVG document level. Each SPObject subclass implements a certain SVG
20  * element node type, or is an abstract base class for different node
21  * types.  The SPObject layer is bound to the SPRepr layer, closely
22  * following the SPRepr mutations via callbacks.  During creation,
23  * SPObject parses and interprets all textual attributes and CSS style
24  * strings of the SPRepr, and later updates the internal state whenever
25  * it receives a signal about a change. The opposite is not true - there
26  * are methods manipulating SPObjects directly and such changes do not
27  * propagate to the SPRepr layer. This is important for implementation of
28  * the undo stack, animations and other features.
29  *
30  * SPObjects are bound to the higher-level container SPDocument, which
31  * provides document level functionality such as the undo stack,
32  * dictionary and so on. Source: doc/architecture.txt
33  */
35 #include <cstring>
36 #include <string>
38 #include "helper/sp-marshal.h"
39 #include "xml/node-event-vector.h"
40 #include "attributes.h"
41 #include "document.h"
42 #include "style.h"
43 #include "sp-object-repr.h"
44 #include "sp-root.h"
45 #include "sp-style-elem.h"
46 #include "sp-script.h"
47 #include "streq.h"
48 #include "strneq.h"
49 #include "xml/repr.h"
50 #include "xml/node-fns.h"
51 #include "debug/event-tracker.h"
52 #include "debug/simple-event.h"
53 #include "debug/demangle.h"
54 #include "util/share.h"
55 #include "util/format.h"
56 #include "util/longest-common-suffix.h"
58 using std::memcpy;
59 using std::strchr;
60 using std::strcmp;
61 using std::strlen;
62 using std::strstr;
64 #define noSP_OBJECT_DEBUG_CASCADE
66 #define noSP_OBJECT_DEBUG
68 #ifdef SP_OBJECT_DEBUG
69 # define debug(f, a...) { g_print("%s(%d) %s:", \
70                                   __FILE__,__LINE__,__FUNCTION__); \
71                           g_print(f, ## a); \
72                           g_print("\n"); \
73                         }
74 #else
75 # define debug(f, a...) /**/
76 #endif
78 /*static void sp_object_class_init(SPObjectClass *klass);
79 static void sp_object_init(SPObject *object);
80 static void sp_object_finalize(GObject *object);
82 static void sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref);
83 static void sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child);
84 static void sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *old_ref, Inkscape::XML::Node *new_ref);
86 static void sp_object_release(SPObject *object);
87 static void sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
89 static void sp_object_private_set(SPObject *object, unsigned int key, gchar const *value);
90 static Inkscape::XML::Node *sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
92 * Real handlers of repr signals */
94 /*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);
96 static void sp_object_repr_content_changed(Inkscape::XML::Node *repr, gchar const *oldcontent, gchar const *newcontent, gpointer data);
98 static void sp_object_repr_child_added(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data);
99 static void sp_object_repr_child_removed(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, void *data);
101 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);
103 static gchar *sp_object_get_unique_id(SPObject *object, gchar const *defid);*/
105 guint update_in_progress = 0; // guard against update-during-update
107 Inkscape::XML::NodeEventVector object_event_vector = {
108         SPObject::sp_object_repr_child_added,
109     SPObject::sp_object_repr_child_removed,
110     SPObject::sp_object_repr_attr_changed,
111     SPObject::sp_object_repr_content_changed,
112     SPObject::sp_object_repr_order_changed
113 };
115 // A friend class used to set internal members on SPObject so as to not expose settors in SPObject's public API
116 class SPObjectImpl
118 public:
120 /**
121  * Null's the id member of an SPObject without attempting to free prior contents.
122  */
123     static void setIdNull( SPObject* obj ) {
124         if (obj) {
125             obj->id = 0;
126         }
127     }
129 /**
130  * Sets the id member of an object, freeing any prior content.
131  */
132     static void setId( SPObject* obj, gchar const* id ) {
133         if (obj && (id != obj->id) ) {
134             if (obj->id) {
135                 g_free(obj->id);
136                 obj->id = 0;
137             }
138             if (id) {
139                 obj->id = g_strdup(id);
140             }
141         }
142     }
143 };
146 //static GObjectClass *parent_class;
148 GObjectClass * SPObjectClass::static_parent_class = 0;
150 /**
151  * Registers the SPObject class with Gdk and returns its type number.
152  */
153 GType
154 SPObject::sp_object_get_type()
156     static GType type = 0;
157     if (!type) {
158         GTypeInfo info = {
159             sizeof(SPObjectClass),
160             NULL, NULL,
161             (GClassInitFunc) SPObjectClass::sp_object_class_init,
162             NULL, NULL,
163             sizeof(SPObject),
164             16,
165             (GInstanceInitFunc) sp_object_init,
166             NULL
167         };
168         type = g_type_register_static(G_TYPE_OBJECT, "SPObject", &info, (GTypeFlags)0);
169     }
170     return type;
173 /**
174  * Initializes the SPObject vtable.
175  */
176 void
177 SPObjectClass::sp_object_class_init(SPObjectClass *klass)
179     GObjectClass *object_class;
181     object_class = (GObjectClass *) klass;
183         static_parent_class = (GObjectClass *) g_type_class_ref(G_TYPE_OBJECT);
185     object_class->finalize = SPObject::sp_object_finalize;
187     klass->child_added = SPObject::sp_object_child_added;
188     klass->remove_child = SPObject::sp_object_remove_child;
189     klass->order_changed = SPObject::sp_object_order_changed;
191     klass->release = SPObject::sp_object_release;
193     klass->build = SPObject::sp_object_build;
195     klass->set = SPObject::sp_object_private_set;
196     klass->write = SPObject::sp_object_private_write;
199 /**
200  * Callback to initialize the SPObject object.
201  */
202 void
203 SPObject::sp_object_init(SPObject *object)
205     debug("id=%x, typename=%s",object, g_type_name_from_instance((GTypeInstance*)object));
207     object->hrefcount = 0;
208     object->_total_hrefcount = 0;
209     object->document = NULL;
210     object->children = object->_last_child = NULL;
211     object->parent = object->next = NULL;
213         //used XML Tree here.
214         Inkscape::XML::Node *repr = object->getRepr();
215         repr = NULL;
216     SPObjectImpl::setIdNull(object);
218     object->_collection_policy = SPObject::COLLECT_WITH_PARENT;
220     new (&object->_release_signal) sigc::signal<void, SPObject *>();
221     new (&object->_modified_signal) sigc::signal<void, SPObject *, unsigned int>();
222     new (&object->_delete_signal) sigc::signal<void, SPObject *>();
223     new (&object->_position_changed_signal) sigc::signal<void, SPObject *>();
224     object->_successor = NULL;
226     // FIXME: now we create style for all objects, but per SVG, only the following can have style attribute:
227     // vg, g, defs, desc, title, symbol, use, image, switch, path, rect, circle, ellipse, line, polyline,
228     // polygon, text, tspan, tref, textPath, altGlyph, glyphRef, marker, linearGradient, radialGradient,
229     // stop, pattern, clipPath, mask, filter, feImage, a, font, glyph, missing-glyph, foreignObject
230     object->style = sp_style_new_from_object(object);
232     object->_label = NULL;
233     object->_default_label = NULL;
236 /**
237  * Callback to destroy all members and connections of object and itself.
238  */
239 void
240 SPObject::sp_object_finalize(GObject *object)
242     SPObject *spobject = (SPObject *)object;
244     g_free(spobject->_label);
245     g_free(spobject->_default_label);
246     spobject->_label = NULL;
247     spobject->_default_label = NULL;
249     if (spobject->_successor) {
250         sp_object_unref(spobject->_successor, NULL);
251         spobject->_successor = NULL;
252     }
254     if (((GObjectClass *) (SPObjectClass::static_parent_class))->finalize) {
255         (* ((GObjectClass *) (SPObjectClass::static_parent_class))->finalize)(object);
256     }
258     spobject->_release_signal.~signal();
259     spobject->_modified_signal.~signal();
260     spobject->_delete_signal.~signal();
261     spobject->_position_changed_signal.~signal();
264 namespace {
266 namespace Debug = Inkscape::Debug;
267 namespace Util = Inkscape::Util;
269 typedef Debug::SimpleEvent<Debug::Event::REFCOUNT> BaseRefCountEvent;
271 class RefCountEvent : public BaseRefCountEvent {
272 public:
273     RefCountEvent(SPObject *object, int bias, Util::ptr_shared<char> name)
274     : BaseRefCountEvent(name)
275     {
276         _addProperty("object", Util::format("%p", object));
277         _addProperty("class", Debug::demangle(g_type_name(G_TYPE_FROM_INSTANCE(object))));
278         _addProperty("new-refcount", Util::format("%d", G_OBJECT(object)->ref_count + bias));
279     }
280 };
282 class RefEvent : public RefCountEvent {
283 public:
284     RefEvent(SPObject *object)
285     : RefCountEvent(object, 1, Util::share_static_string("sp-object-ref"))
286     {}
287 };
289 class UnrefEvent : public RefCountEvent {
290 public:
291     UnrefEvent(SPObject *object)
292     : RefCountEvent(object, -1, Util::share_static_string("sp-object-unref"))
293     {}
294 };
298 gchar const* SPObject::getId() const {
299     return id;
302 Inkscape::XML::Node * SPObject::getRepr() {
303         return repr;
306 Inkscape::XML::Node const* SPObject::getRepr() const{
307             return repr;
311 /**
312  * Increase reference count of object, with possible debugging.
313  *
314  * \param owner If non-NULL, make debug log entry.
315  * \return object, NULL is error.
316  * \pre object points to real object
317  */
318 SPObject *
319 sp_object_ref(SPObject *object, SPObject *owner)
321     g_return_val_if_fail(object != NULL, NULL);
322     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
323     g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
325     Inkscape::Debug::EventTracker<RefEvent> tracker(object);
326     g_object_ref(G_OBJECT(object));
327     return object;
330 /**
331  * Decrease reference count of object, with possible debugging and
332  * finalization.
333  *
334  * \param owner If non-NULL, make debug log entry.
335  * \return always NULL
336  * \pre object points to real object
337  */
338 SPObject *
339 sp_object_unref(SPObject *object, SPObject *owner)
341     g_return_val_if_fail(object != NULL, NULL);
342     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
343     g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
345     Inkscape::Debug::EventTracker<UnrefEvent> tracker(object);
346     g_object_unref(G_OBJECT(object));
347     return NULL;
350 /**
351  * Increase weak refcount.
352  *
353  * Hrefcount is used for weak references, for example, to
354  * determine whether any graphical element references a certain gradient
355  * node.
356  * \param owner Ignored.
357  * \return object, NULL is error
358  * \pre object points to real object
359  */
360 SPObject *
361 sp_object_href(SPObject *object, gpointer /*owner*/)
363     g_return_val_if_fail(object != NULL, NULL);
364     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
366     object->hrefcount++;
367     object->_updateTotalHRefCount(1);
369     return object;
372 /**
373  * Decrease weak refcount.
374  *
375  * Hrefcount is used for weak references, for example, to determine whether
376  * any graphical element references a certain gradient node.
377  * \param owner Ignored.
378  * \return always NULL
379  * \pre object points to real object and hrefcount>0
380  */
381 SPObject *
382 sp_object_hunref(SPObject *object, gpointer /*owner*/)
384     g_return_val_if_fail(object != NULL, NULL);
385     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
386     g_return_val_if_fail(object->hrefcount > 0, NULL);
388     object->hrefcount--;
389     object->_updateTotalHRefCount(-1);
391     return NULL;
394 /**
395  * Adds increment to _total_hrefcount of object and its parents.
396  */
397 void
398 SPObject::_updateTotalHRefCount(int increment) {
399     SPObject *topmost_collectable = NULL;
400     for ( SPObject *iter = this ; iter ; iter = SP_OBJECT_PARENT(iter) ) {
401         iter->_total_hrefcount += increment;
402         if ( iter->_total_hrefcount < iter->hrefcount ) {
403             g_critical("HRefs overcounted");
404         }
405         if ( iter->_total_hrefcount == 0 &&
406              iter->_collection_policy != COLLECT_WITH_PARENT )
407         {
408             topmost_collectable = iter;
409         }
410     }
411     if (topmost_collectable) {
412         topmost_collectable->requestOrphanCollection();
413     }
416 /**
417  * True if object is non-NULL and this is some in/direct parent of object.
418  */
419 bool
420 SPObject::isAncestorOf(SPObject const *object) const {
421     g_return_val_if_fail(object != NULL, false);
422     object = SP_OBJECT_PARENT(object);
423     while (object) {
424         if ( object == this ) {
425             return true;
426         }
427         object = SP_OBJECT_PARENT(object);
428     }
429     return false;
432 namespace {
434 bool same_objects(SPObject const &a, SPObject const &b) {
435     return &a == &b;
440 /**
441  * Returns youngest object being parent to this and object.
442  */
443 SPObject const *
444 SPObject::nearestCommonAncestor(SPObject const *object) const {
445     g_return_val_if_fail(object != NULL, NULL);
447     using Inkscape::Algorithms::longest_common_suffix;
448     return longest_common_suffix<SPObject::ConstParentIterator>(this, object, NULL, &same_objects);
451 SPObject const *AncestorSon(SPObject const *obj, SPObject const *ancestor) {
452     if (obj == NULL || ancestor == NULL)
453         return NULL;
454     if (SP_OBJECT_PARENT(obj) == ancestor)
455         return obj;
456     return AncestorSon(SP_OBJECT_PARENT(obj), ancestor);
459 /**
460  * Compares height of objects in tree.
461  *
462  * Works for different-parent objects, so long as they have a common ancestor.
463  * \return \verbatim
464  *    0    positions are equivalent
465  *    1    first object's position is greater than the second
466  *   -1    first object's position is less than the second   \endverbatim
467  */
468 int
469 sp_object_compare_position(SPObject const *first, SPObject const *second)
471     if (first == second) return 0;
473     SPObject const *ancestor = first->nearestCommonAncestor(second);
474     if (ancestor == NULL) return 0; // cannot compare, no common ancestor!
476     // we have an object and its ancestor (should not happen when sorting selection)
477     if (ancestor == first)
478         return 1;
479     if (ancestor == second)
480         return -1;
482     SPObject const *to_first = AncestorSon(first, ancestor);
483     SPObject const *to_second = AncestorSon(second, ancestor);
485     g_assert(SP_OBJECT_PARENT(to_second) == SP_OBJECT_PARENT(to_first));
487     return sp_repr_compare_position(SP_OBJECT_REPR(to_first), SP_OBJECT_REPR(to_second));
491 /**
492  * Append repr as child of this object.
493  * \pre this is not a cloned object
494  */
495 SPObject *
496 SPObject::appendChildRepr(Inkscape::XML::Node *repr) {
497     if (!SP_OBJECT_IS_CLONED(this)) {
498         SP_OBJECT_REPR(this)->appendChild(repr);
499         return SP_OBJECT_DOCUMENT(this)->getObjectByRepr(repr);
500     } else {
501         g_critical("Attempt to append repr as child of cloned object");
502         return NULL;
503     }
506 void SPObject::setCSS(SPCSSAttr *css, gchar const *attr)
508         g_assert(this->getRepr() != NULL);
509         sp_repr_css_set(this->getRepr(), css, attr);
512 void SPObject::changeCSS(SPCSSAttr *css, gchar const *attr)
514         g_assert(this->getRepr() != NULL);
515         sp_repr_css_change(this->getRepr(), css, attr);
518 /**
519  * Retrieves the children as a GSList object, optionally ref'ing the children
520  * in the process, if add_ref is specified.
521  */
522 GSList *SPObject::childList(bool add_ref, Action) {
523     GSList *l = NULL;
524     for (SPObject *child = this->first_child() ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
525         if (add_ref)
526             g_object_ref (G_OBJECT (child));
528         l = g_slist_prepend (l, child);
529     }
530     return l;
534 /** Gets the label property for the object or a default if no label
535  *  is defined.
536  */
537 gchar const *
538 SPObject::label() const {
539     return _label;
542 /** Returns a default label property for the object. */
543 gchar const *
544 SPObject::defaultLabel() const {
545     if (_label) {
546         return _label;
547     } else {
548         if (!_default_label) {
549             gchar const *id=SP_OBJECT_ID(this);
550             if (id) {
551                 _default_label = g_strdup_printf("#%s", id);
552             } else {
553                 _default_label = g_strdup_printf("<%s>", SP_OBJECT_REPR(this)->name());
554             }
555         }
556         return _default_label;
557     }
560 /** Sets the label property for the object */
561 void
562 SPObject::setLabel(gchar const *label) {
563     SP_OBJECT_REPR(this)->setAttribute("inkscape:label", label, false);
567 /** Queues the object for orphan collection */
568 void
569 SPObject::requestOrphanCollection() {
570     g_return_if_fail(document != NULL);
572     // do not remove style or script elements (Bug #276244)
573     if (SP_IS_STYLE_ELEM(this))
574         return;
575     if (SP_IS_SCRIPT(this))
576         return;
578     document->queueForOrphanCollection(this);
580     /** \todo
581      * This is a temporary hack added to make fill&stroke rebuild its
582      * gradient list when the defs are vacuumed.  gradient-vector.cpp
583      * listens to the modified signal on defs, and now we give it that
584      * signal.  Mental says that this should be made automatic by
585      * merging SPObjectGroup with SPObject; SPObjectGroup would issue
586      * this signal automatically. Or maybe just derive SPDefs from
587      * SPObjectGroup?
588      */
590     this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
593 /** Sends the delete signal to all children of this object recursively */
594 void
595 SPObject::_sendDeleteSignalRecursive() {
596     for (SPObject *child = this->first_child(); child; child = SP_OBJECT_NEXT(child)) {
597         child->_delete_signal.emit(child);
598         child->_sendDeleteSignalRecursive();
599     }
602 /**
603  * Deletes the object reference, unparenting it from its parent.
604  *
605  * If the \a propagate parameter is set to true, it emits a delete
606  * signal.  If the \a propagate_descendants parameter is true, it
607  * recursively sends the delete signal to children.
608  */
609 void
610 SPObject::deleteObject(bool propagate, bool propagate_descendants)
612     sp_object_ref(this, NULL);
613     if (propagate) {
614         _delete_signal.emit(this);
615     }
616     if (propagate_descendants) {
617         this->_sendDeleteSignalRecursive();
618     }
620     Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
621     if (repr && sp_repr_parent(repr)) {
622         sp_repr_unparent(repr);
623     }
625     if (_successor) {
626         _successor->deleteObject(propagate, propagate_descendants);
627     }
628     sp_object_unref(this, NULL);
631 /**
632  * Put object into object tree, under parent, and behind prev;
633  * also update object's XML space.
634  */
635 void
636 SPObject::attach(SPObject *object, SPObject *prev)
638     //g_return_if_fail(parent != NULL);
639     //g_return_if_fail(SP_IS_OBJECT(parent));
640     g_return_if_fail(object != NULL);
641     g_return_if_fail(SP_IS_OBJECT(object));
642     g_return_if_fail(!prev || SP_IS_OBJECT(prev));
643     g_return_if_fail(!prev || prev->parent == this);
644     g_return_if_fail(!object->parent);
646     sp_object_ref(object, this);
647     object->parent = this;
648     this->_updateTotalHRefCount(object->_total_hrefcount);
650     SPObject *next;
651     if (prev) {
652         next = prev->next;
653         prev->next = object;
654     } else {
655         next = this->children;
656         this->children = object;
657     }
658     object->next = next;
659     if (!next) {
660         this->_last_child = object;
661     }
662     if (!object->xml_space.set)
663         object->xml_space.value = this->xml_space.value;
666 /**
667  * In list of object's siblings, move object behind prev.
668  */
669 void
670 SPObject::reorder(SPObject *prev) {
671     //g_return_if_fail(object != NULL);
672     //g_return_if_fail(SP_IS_OBJECT(object));
673     g_return_if_fail(this->parent != NULL);
674     g_return_if_fail(this != prev);
675     g_return_if_fail(!prev || SP_IS_OBJECT(prev));
676     g_return_if_fail(!prev || prev->parent == this->parent);
678     SPObject *const parent=this->parent;
680     SPObject *old_prev=NULL;
681     for ( SPObject *child = parent->children ; child && child != this ;
682           child = child->next )
683     {
684         old_prev = child;
685     }
687     SPObject *next=this->next;
688     if (old_prev) {
689         old_prev->next = next;
690     } else {
691         parent->children = next;
692     }
693     if (!next) {
694         parent->_last_child = old_prev;
695     }
696     if (prev) {
697         next = prev->next;
698         prev->next = this;
699     } else {
700         next = parent->children;
701         parent->children = this;
702     }
703     this->next = next;
704     if (!next) {
705         parent->_last_child = this;
706     }
709 /**
710  * Remove object from parent's children, release and unref it.
711  */
712 void
713 SPObject::detach(SPObject *object) {
714     //g_return_if_fail(parent != NULL);
715     //g_return_if_fail(SP_IS_OBJECT(parent));
716     g_return_if_fail(object != NULL);
717     g_return_if_fail(SP_IS_OBJECT(object));
718     g_return_if_fail(object->parent == this);
720     object->releaseReferences();
722     SPObject *prev=NULL;
723     for ( SPObject *child = this->children ; child && child != object ;
724           child = child->next )
725     {
726         prev = child;
727     }
729     SPObject *next=object->next;
730     if (prev) {
731         prev->next = next;
732     } else {
733         this->children = next;
734     }
735     if (!next) {
736         this->_last_child = prev;
737     }
739     object->next = NULL;
740     object->parent = NULL;
742     this->_updateTotalHRefCount(-object->_total_hrefcount);
743     sp_object_unref(object, this);
746 /**
747  * Return object's child whose node pointer equals repr.
748  */
749 SPObject *
750 SPObject::get_child_by_repr(Inkscape::XML::Node *repr)
752     //g_return_val_if_fail(object != NULL, NULL);
753     //g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
754     g_return_val_if_fail(repr != NULL, NULL);
756     if (this->_last_child && SP_OBJECT_REPR(this->_last_child) == repr)
757         return this->_last_child;   // optimization for common scenario
758     for ( SPObject *child = this->children ; child ; child = child->next ) {
759         if ( SP_OBJECT_REPR(child) == repr ) {
760             return child;
761         }
762     }
764     return NULL;
767 /**
768  * Callback for child_added event.
769  * Invoked whenever the given mutation event happens in the XML tree.
770  */
771 void
772 SPObject::sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
774     GType type = sp_repr_type_lookup(child);
775     if (!type) {
776         return;
777     }
778     SPObject *ochild = SP_OBJECT(g_object_new(type, 0));
779     SPObject *prev = ref ? object->get_child_by_repr(ref) : NULL;
780     object->attach(ochild, prev);
781     sp_object_unref(ochild, NULL);
783     ochild->invoke_build(object->document, child, SP_OBJECT_IS_CLONED(object));
786 /**
787  * Removes, releases and unrefs all children of object.
788  *
789  * This is the opposite of build. It has to be invoked as soon as the
790  * object is removed from the tree, even if it is still alive according
791  * to reference count. The frontend unregisters the object from the
792  * document and releases the SPRepr bindings; implementations should free
793  * state data and release all child objects.  Invoking release on
794  * SPRoot destroys the whole document tree.
795  * \see sp_object_build()
796  */
797 void SPObject::sp_object_release(SPObject *object)
799     debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
800     while (object->children) {
801         object->detach(object->children);
802     }
805 /**
806  * Remove object's child whose node equals repr, release and
807  * unref it.
808  *
809  * Invoked whenever the given mutation event happens in the XML
810  * tree, BEFORE removal from the XML tree happens, so grouping
811  * objects can safely release the child data.
812  */
813 void
814 SPObject::sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child)
816     debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
817     SPObject *ochild = object->get_child_by_repr(child);
818     g_return_if_fail (ochild != NULL || !strcmp("comment", child->name())); // comments have no objects
819     if (ochild)
820         object->detach(ochild);
823 /**
824  * Move object corresponding to child after sibling object corresponding
825  * to new_ref.
826  * Invoked whenever the given mutation event happens in the XML tree.
827  * \param old_ref Ignored
828  */
829 void SPObject::sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node */*old_ref*/,
830                                     Inkscape::XML::Node *new_ref)
832     SPObject *ochild = object->get_child_by_repr(child);
833     g_return_if_fail(ochild != NULL);
834     SPObject *prev = new_ref ? object->get_child_by_repr(new_ref) : NULL;
835     ochild->reorder(prev);
836     ochild->_position_changed_signal.emit(ochild);
839 /**
840  * Virtual build callback.
841  *
842  * This has to be invoked immediately after creation of an SPObject. The
843  * frontend method ensures that the new object is properly attached to
844  * the document and repr; implementation then will parse all of the attributes,
845  * generate the children objects and so on.  Invoking build on the SPRoot
846  * object results in creation of the whole document tree (this is, what
847  * SPDocument does after the creation of the XML tree).
848  * \see sp_object_release()
849  */
850 void
851 SPObject::sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
853     /* Nothing specific here */
854     debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
856     object->readAttr("xml:space");
857     object->readAttr("inkscape:label");
858     object->readAttr("inkscape:collect");
860     for (Inkscape::XML::Node *rchild = repr->firstChild() ; rchild != NULL; rchild = rchild->next()) {
861         GType type = sp_repr_type_lookup(rchild);
862         if (!type) {
863             continue;
864         }
865         SPObject *child = SP_OBJECT(g_object_new(type, 0));
866         object->attach(child, object->lastChild());
867         sp_object_unref(child, NULL);
868         child->invoke_build(document, rchild, SP_OBJECT_IS_CLONED(object));
869     }
872 void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
874     debug("id=%x, typename=%s", this, g_type_name_from_instance((GTypeInstance*)this));
876     //g_assert(object != NULL);
877     //g_assert(SP_IS_OBJECT(object));
878     g_assert(document != NULL);
879     g_assert(repr != NULL);
881     g_assert(this->document == NULL);
882     g_assert(this->repr == NULL);
883     g_assert(this->getId() == NULL);
885     /* Bookkeeping */
887     this->document = document;
888     this->repr = repr;
889     if (!cloned)
890         Inkscape::GC::anchor(repr);
891     this->cloned = cloned;
893     if (!SP_OBJECT_IS_CLONED(this)) {
894         this->document->bindObjectToRepr(this->repr, this);
896         if (Inkscape::XML::id_permitted(this->repr)) {
897             /* If we are not cloned, and not seeking, force unique id */
898             gchar const *id = this->repr->attribute("id");
899             if (!document->isSeeking()) {
900                 {
901                     gchar *realid = sp_object_get_unique_id(this, id);
902                     g_assert(realid != NULL);
904                     this->document->bindObjectToId(realid, this);
905                     SPObjectImpl::setId(this, realid);
906                     g_free(realid);
907                 }
909                 /* Redefine ID, if required */
910                 if ((id == NULL) || (strcmp(id, this->getId()) != 0)) {
911                     this->repr->setAttribute("id", this->getId());
912                 }
913             } else if (id) {
914                 // bind if id, but no conflict -- otherwise, we can expect
915                 // a subsequent setting of the id attribute
916                 if (!this->document->getObjectById(id)) {
917                     this->document->bindObjectToId(id, this);
918                     SPObjectImpl::setId(this, id);
919                 }
920             }
921         }
922     } else {
923         g_assert(this->getId() == NULL);
924     }
926     /* Invoke derived methods, if any */
927     if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->build) {
928         (*((SPObjectClass *) G_OBJECT_GET_CLASS(this))->build)(this, document, repr);
929     }
931     /* Signalling (should be connected AFTER processing derived methods */
932     sp_repr_add_listener(repr, &object_event_vector, this);
935 long long int SPObject::getIntAttribute(char const *key, long long int def)
937         return sp_repr_get_int_attribute(getRepr(),key,def);
940 unsigned SPObject::getPosition(){
941         g_assert(this->repr);
943         return repr->position();
946 void SPObject::appendChild(Inkscape::XML::Node *child) {
947         g_assert(this->repr);
949         repr->appendChild(child);
952 void SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node * prev)
954         g_assert(this->repr);
956         repr->addChild(child,prev);
959 void SPObject::releaseReferences() {
960     g_assert(this->document);
961     g_assert(this->repr);
963     sp_repr_remove_listener_by_data(this->repr, this);
965     this->_release_signal.emit(this);
966     SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
967     if (klass->release) {
968         klass->release(this);
969     }
971     /* all hrefs should be released by the "release" handlers */
972     g_assert(this->hrefcount == 0);
974     if (!SP_OBJECT_IS_CLONED(this)) {
975         if (this->id) {
976             this->document->bindObjectToId(this->id, NULL);
977         }
978         g_free(this->id);
979         this->id = NULL;
981         g_free(this->_default_label);
982         this->_default_label = NULL;
984         this->document->bindObjectToRepr(this->repr, NULL);
986         Inkscape::GC::release(this->repr);
987     } else {
988         g_assert(!this->id);
989     }
991     if (this->style) {
992         this->style = sp_style_unref(this->style);
993     }
995     this->document = NULL;
996     this->repr = NULL;
1000 SPObject *SPObject::getNext()
1002     return next;
1005 SPObject *SPObject::getPrev()
1007     return this->prev();
1010 /**
1011  * Callback for child_added node event.
1012  */
1013 void
1014 SPObject::sp_object_repr_child_added(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data)
1016     SPObject *object = SP_OBJECT(data);
1018     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->child_added)
1019         (*((SPObjectClass *)G_OBJECT_GET_CLASS(object))->child_added)(object, child, ref);
1022 /**
1023  * Callback for remove_child node event.
1024  */
1025 void
1026 SPObject::sp_object_repr_child_removed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node */*ref*/, gpointer data)
1028     SPObject *object = SP_OBJECT(data);
1030     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->remove_child) {
1031         (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->remove_child)(object, child);
1032     }
1035 /**
1036  * Callback for order_changed node event.
1037  *
1038  * \todo fixme:
1039  */
1040 void
1041 SPObject::sp_object_repr_order_changed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data)
1043     SPObject *object = SP_OBJECT(data);
1045     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->order_changed) {
1046         (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->order_changed)(object, child, old, newer);
1047     }
1050 /**
1051  * Callback for set event.
1052  */
1053 void
1054 SPObject::sp_object_private_set(SPObject *object, unsigned int key, gchar const *value)
1056     g_assert(key != SP_ATTR_INVALID);
1058     switch (key) {
1059         case SP_ATTR_ID:
1061                         //XML Tree being used here.
1062             if ( !SP_OBJECT_IS_CLONED(object) && object->getRepr()->type() == Inkscape::XML::ELEMENT_NODE ) {
1063                 SPDocument *document=object->document;
1064                 SPObject *conflict=NULL;
1066                 gchar const *new_id = value;
1068                 if (new_id) {
1069                     conflict = document->getObjectById((char const *)new_id);
1070                 }
1072                 if ( conflict && conflict != object ) {
1073                     if (!document->isSeeking()) {
1074                         sp_object_ref(conflict, NULL);
1075                         // give the conflicting object a new ID
1076                         gchar *new_conflict_id = sp_object_get_unique_id(conflict, NULL);
1077                         SP_OBJECT_REPR(conflict)->setAttribute("id", new_conflict_id);
1078                         g_free(new_conflict_id);
1079                         sp_object_unref(conflict, NULL);
1080                     } else {
1081                         new_id = NULL;
1082                     }
1083                 }
1085                 if (object->getId()) {
1086                     document->bindObjectToId(object->getId(), NULL);
1087                     SPObjectImpl::setId(object, 0);
1088                 }
1090                 if (new_id) {
1091                     SPObjectImpl::setId(object, new_id);
1092                     document->bindObjectToId(object->getId(), object);
1093                 }
1095                 g_free(object->_default_label);
1096                 object->_default_label = NULL;
1097             }
1098             break;
1099         case SP_ATTR_INKSCAPE_LABEL:
1100             g_free(object->_label);
1101             if (value) {
1102                 object->_label = g_strdup(value);
1103             } else {
1104                 object->_label = NULL;
1105             }
1106             g_free(object->_default_label);
1107             object->_default_label = NULL;
1108             break;
1109         case SP_ATTR_INKSCAPE_COLLECT:
1110             if ( value && !strcmp(value, "always") ) {
1111                 object->setCollectionPolicy(SPObject::ALWAYS_COLLECT);
1112             } else {
1113                 object->setCollectionPolicy(SPObject::COLLECT_WITH_PARENT);
1114             }
1115             break;
1116         case SP_ATTR_XML_SPACE:
1117             if (value && !strcmp(value, "preserve")) {
1118                 object->xml_space.value = SP_XML_SPACE_PRESERVE;
1119                 object->xml_space.set = TRUE;
1120             } else if (value && !strcmp(value, "default")) {
1121                 object->xml_space.value = SP_XML_SPACE_DEFAULT;
1122                 object->xml_space.set = TRUE;
1123             } else if (object->parent) {
1124                 SPObject *parent;
1125                 parent = object->parent;
1126                 object->xml_space.value = parent->xml_space.value;
1127             }
1128             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1129             break;
1130         case SP_ATTR_STYLE:
1131             sp_style_read_from_object(object->style, object);
1132             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1133             break;
1134         default:
1135             break;
1136     }
1139 /**
1140  * Call virtual set() function of object.
1141  */
1142 void
1143 SPObject::setKeyValue(unsigned int key, gchar const *value)
1145     //g_assert(object != NULL);
1146     //g_assert(SP_IS_OBJECT(object));
1148     if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->set) {
1149         ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->set(this, key, value);
1150     }
1153 /**
1154  * Read value of key attribute from XML node into object.
1155  */
1156 void
1157 SPObject::readAttr(gchar const *key)
1159     //g_assert(object != NULL);
1160     //g_assert(SP_IS_OBJECT(object));
1161     g_assert(key != NULL);
1163         //XML Tree being used here.
1164     g_assert(this->getRepr() != NULL);
1166     unsigned int keyid = sp_attribute_lookup(key);
1167     if (keyid != SP_ATTR_INVALID) {
1168         /* Retrieve the 'key' attribute from the object's XML representation */
1169         gchar const *value = getRepr()->attribute(key);
1171         setKeyValue(keyid, value);
1172     }
1175 /**
1176  * Callback for attr_changed node event.
1177  */
1178 void
1179 SPObject::sp_object_repr_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *key, gchar const */*oldval*/, gchar const */*newval*/, bool is_interactive, gpointer data)
1181     SPObject *object = SP_OBJECT(data);
1183     object->readAttr(key);
1185     // manual changes to extension attributes require the normal
1186     // attributes, which depend on them, to be updated immediately
1187     if (is_interactive) {
1188         object->updateRepr(0);
1189     }
1192 /**
1193  * Callback for content_changed node event.
1194  */
1195 void
1196 SPObject::sp_object_repr_content_changed(Inkscape::XML::Node */*repr*/, gchar const */*oldcontent*/, gchar const */*newcontent*/, gpointer data)
1198     SPObject *object = SP_OBJECT(data);
1200     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)
1201         (*((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)(object);
1204 /**
1205  * Return string representation of space value.
1206  */
1207 static gchar const*
1208 sp_xml_get_space_string(unsigned int space)
1210     switch (space) {
1211         case SP_XML_SPACE_DEFAULT:
1212             return "default";
1213         case SP_XML_SPACE_PRESERVE:
1214             return "preserve";
1215         default:
1216             return NULL;
1217     }
1220 /**
1221  * Callback for write event.
1222  */
1223 Inkscape::XML::Node *
1224 SPObject::sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
1226     if (!repr && (flags & SP_OBJECT_WRITE_BUILD)) {
1227         repr = SP_OBJECT_REPR(object)->duplicate(doc);
1228         if (!( flags & SP_OBJECT_WRITE_EXT )) {
1229             repr->setAttribute("inkscape:collect", NULL);
1230         }
1231     } else {
1232         repr->setAttribute("id", object->getId());
1234         if (object->xml_space.set) {
1235             char const *xml_space;
1236             xml_space = sp_xml_get_space_string(object->xml_space.value);
1237             repr->setAttribute("xml:space", xml_space);
1238         }
1240         if ( flags & SP_OBJECT_WRITE_EXT &&
1241              object->collectionPolicy() == SPObject::ALWAYS_COLLECT )
1242         {
1243             repr->setAttribute("inkscape:collect", "always");
1244         } else {
1245             repr->setAttribute("inkscape:collect", NULL);
1246         }
1248         SPStyle const *const obj_style = SP_OBJECT_STYLE(object);
1249         if (obj_style) {
1250             gchar *s = sp_style_write_string(obj_style, SP_STYLE_FLAG_IFSET);
1251             repr->setAttribute("style", ( *s ? s : NULL ));
1252             g_free(s);
1253         } else {
1254             /** \todo I'm not sure what to do in this case.  Bug #1165868
1255              * suggests that it can arise, but the submitter doesn't know
1256              * how to do so reliably.  The main two options are either
1257              * leave repr's style attribute unchanged, or explicitly clear it.
1258              * Must also consider what to do with property attributes for
1259              * the element; see below.
1260              */
1261             char const *style_str = repr->attribute("style");
1262             if (!style_str) {
1263                 style_str = "NULL";
1264             }
1265             g_warning("Item's style is NULL; repr style attribute is %s", style_str);
1266         }
1268         /** \note We treat object->style as authoritative.  Its effects have
1269          * been written to the style attribute above; any properties that are
1270          * unset we take to be deliberately unset (e.g. so that clones can
1271          * override the property).
1272          *
1273          * Note that the below has an undesirable consequence of changing the
1274          * appearance on renderers that lack CSS support (e.g. SVG tiny);
1275          * possibly we should write property attributes instead of a style
1276          * attribute.
1277          */
1278         sp_style_unset_property_attrs (object);
1279     }
1281     return repr;
1284 /**
1285  * Update this object's XML node with flags value.
1286  */
1287 Inkscape::XML::Node *
1288 SPObject::updateRepr(unsigned int flags) {
1289     if (!SP_OBJECT_IS_CLONED(this)) {
1290         Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
1291         if (repr) {
1292             return updateRepr(repr->document(), repr, flags);
1293         } else {
1294             g_critical("Attempt to update non-existent repr");
1295             return NULL;
1296         }
1297     } else {
1298         /* cloned objects have no repr */
1299         return NULL;
1300     }
1303 /** Used both to create reprs in the original document, and to create
1304  *  reprs in another document (e.g. a temporary document used when
1305  *  saving as "Plain SVG"
1306  */
1307 Inkscape::XML::Node *
1308 SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags) {
1309     g_assert(doc != NULL);
1311     if (SP_OBJECT_IS_CLONED(this)) {
1312         /* cloned objects have no repr */
1313         return NULL;
1314     }
1315     if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write) {
1316         if (!(flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1317             repr = SP_OBJECT_REPR(this);
1318         }
1319         return ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write(this, doc, repr, flags);
1320     } else {
1321         g_warning("Class %s does not implement ::write", G_OBJECT_TYPE_NAME(this));
1322         if (!repr) {
1323             if (flags & SP_OBJECT_WRITE_BUILD) {
1324                 repr = SP_OBJECT_REPR(this)->duplicate(doc);
1325             }
1326             /// \todo FIXME: else probably error (Lauris) */
1327         } else {
1328             repr->mergeFrom(SP_OBJECT_REPR(this), "id");
1329         }
1330         return repr;
1331     }
1334 /* Modification */
1336 /**
1337  * Add \a flags to \a object's as dirtiness flags, and
1338  * recursively add CHILD_MODIFIED flag to
1339  * parent and ancestors (as far up as necessary).
1340  */
1341 void
1342 SPObject::requestDisplayUpdate(unsigned int flags)
1344     g_return_if_fail( this->document != NULL );
1346     if (update_in_progress) {
1347         g_print("WARNING: Requested update while update in progress, counter = %d\n", update_in_progress);
1348     }
1350     /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1351      * SP_OBJECT_CHILD_MODIFIED_FLAG */
1352     g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1353     g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1354     g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1356     bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1358     this->uflags |= flags;
1360     /* If requestModified has already been called on this object or one of its children, then we
1361      * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1362      */
1363     if (already_propagated) {
1364         SPObject *parent = SP_OBJECT_PARENT(this);
1365         if (parent) {
1366             parent->requestDisplayUpdate(SP_OBJECT_CHILD_MODIFIED_FLAG);
1367         } else {
1368             SP_OBJECT_DOCUMENT(this)->request_modified();
1369         }
1370     }
1373 /**
1374  * Update views
1375  */
1376 void
1377 SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
1379     g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1381     update_in_progress ++;
1383 #ifdef SP_OBJECT_DEBUG_CASCADE
1384     g_print("Update %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), SP_OBJECT_ID(this), flags, this->uflags, this->mflags);
1385 #endif
1387     /* Get this flags */
1388     flags |= this->uflags;
1389     /* Copy flags to modified cascade for later processing */
1390     this->mflags |= this->uflags;
1391     /* We have to clear flags here to allow rescheduling update */
1392     this->uflags = 0;
1394     // Merge style if we have good reasons to think that parent style is changed */
1395     /** \todo
1396      * I am not sure whether we should check only propagated
1397      * flag. We are currently assuming that style parsing is
1398      * done immediately. I think this is correct (Lauris).
1399      */
1400     if ((flags & SP_OBJECT_STYLE_MODIFIED_FLAG) && (flags & SP_OBJECT_PARENT_MODIFIED_FLAG)) {
1401         if (this->style && this->parent) {
1402             sp_style_merge_from_parent(this->style, this->parent->style);
1403         }
1404     }
1406     try
1407     {
1408         if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update)
1409             ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);
1410     }
1411     catch(...)
1412     {
1413         /** \todo
1414         * in case of catching an exception we need to inform the user somehow that the document is corrupted
1415         * maybe by implementing an document flag documentOk
1416         * or by a modal error dialog
1417         */
1418         g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
1419     }
1421     update_in_progress --;
1424 /**
1425  * Request modified always bubbles *up* the tree, as opposed to
1426  * request display update, which trickles down and relies on the
1427  * flags set during this pass...
1428  */
1429 void
1430 SPObject::requestModified(unsigned int flags)
1432     g_return_if_fail( this->document != NULL );
1434     /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1435      * SP_OBJECT_CHILD_MODIFIED_FLAG */
1436     g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1437     g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1438     g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1440     bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1442     this->mflags |= flags;
1444     /* If requestModified has already been called on this object or one of its children, then we
1445      * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1446      */
1447     if (already_propagated) {
1448         SPObject *parent=SP_OBJECT_PARENT(this);
1449         if (parent) {
1450             parent->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
1451         } else {
1452             SP_OBJECT_DOCUMENT(this)->request_modified();
1453         }
1454     }
1457 /**
1458  *  Emits the MODIFIED signal with the object's flags.
1459  *  The object's mflags are the original set aside during the update pass for
1460  *  later delivery here.  Once emitModified() is called, those flags don't
1461  *  need to be stored any longer.
1462  */
1463 void
1464 SPObject::emitModified(unsigned int flags)
1466     /* only the MODIFIED_CASCADE flag is legal here */
1467     g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1469 #ifdef SP_OBJECT_DEBUG_CASCADE
1470     g_print("Modified %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), SP_OBJECT_ID(this), flags, this->uflags, this->mflags);
1471 #endif
1473     flags |= this->mflags;
1474     /* We have to clear mflags beforehand, as signal handlers may
1475      * make changes and therefore queue new modification notifications
1476      * themselves. */
1477     this->mflags = 0;
1479     g_object_ref(G_OBJECT(this));
1480     SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
1481     if (klass->modified) {
1482         klass->modified(this, flags);
1483     }
1484     _modified_signal.emit(this, flags);
1485     g_object_unref(G_OBJECT(this));
1488 gchar const *
1489 SPObject::getTagName(SPException *ex) const
1491         g_assert(repr != NULL);
1492     /* If exception is not clear, return */
1493     if (!SP_EXCEPTION_IS_OK(ex)) {
1494         return NULL;
1495     }
1497     /// \todo fixme: Exception if object is NULL? */
1498         //XML Tree being used here.
1499     return getRepr()->name();
1502 gchar const *
1503 SPObject::getAttribute(gchar const *key, SPException *ex) const
1505         g_assert(this->repr != NULL);
1506     /* If exception is not clear, return */
1507     if (!SP_EXCEPTION_IS_OK(ex)) {
1508         return NULL;
1509     }
1511     /// \todo fixme: Exception if object is NULL? */
1512         //XML Tree being used here.
1513     return (gchar const *) getRepr()->attribute(key);
1516 void
1517 SPObject::setAttribute(gchar const *key, gchar const *value, SPException *ex)
1519         g_assert(this->repr != NULL);
1520     /* If exception is not clear, return */
1521     g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1523     /// \todo fixme: Exception if object is NULL? */
1524         //XML Tree being used here.
1525     getRepr()->setAttribute(key, value, false);
1528 void
1529 SPObject::removeAttribute(gchar const *key, SPException *ex)
1531     /* If exception is not clear, return */
1532     g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1534     /// \todo fixme: Exception if object is NULL? */
1535         //XML Tree being used here.
1536     getRepr()->setAttribute(key, NULL, false);
1539 bool SPObject::storeAsDouble( gchar const *key, double *val ) const
1541         g_assert(this->getRepr()!= NULL);
1542         return sp_repr_get_double(((Inkscape::XML::Node *)(this->getRepr())),key,val);
1545 /* Helper */
1547 gchar *
1548 SPObject::sp_object_get_unique_id(SPObject *object, gchar const *id)
1550     static unsigned long count = 0;
1552     g_assert(SP_IS_OBJECT(object));
1554     count++;
1556         //XML Tree being used here.
1557     gchar const *name = object->getRepr()->name();
1558     g_assert(name != NULL);
1560     gchar const *local = strchr(name, ':');
1561     if (local) {
1562         name = local + 1;
1563     }
1565     if (id != NULL) {
1566         if (object->document->getObjectById(id) == NULL) {
1567             return g_strdup(id);
1568         }
1569     }
1571     size_t const name_len = strlen(name);
1572     size_t const buflen = name_len + (sizeof(count) * 10 / 4) + 1;
1573     gchar *const buf = (gchar *) g_malloc(buflen);
1574     memcpy(buf, name, name_len);
1575     gchar *const count_buf = buf + name_len;
1576     size_t const count_buflen = buflen - name_len;
1577     do {
1578         ++count;
1579         g_snprintf(count_buf, count_buflen, "%lu", count);
1580     } while ( object->document->getObjectById(buf) != NULL );
1581     return buf;
1584 /* Style */
1586 /**
1587  * Returns an object style property.
1588  *
1589  * \todo
1590  * fixme: Use proper CSS parsing.  The current version is buggy
1591  * in a number of situations where key is a substring of the
1592  * style string other than as a property name (including
1593  * where key is a substring of a property name), and is also
1594  * buggy in its handling of inheritance for properties that
1595  * aren't inherited by default.  It also doesn't allow for
1596  * the case where the property is specified but with an invalid
1597  * value (in which case I believe the CSS2 error-handling
1598  * behaviour applies, viz. behave as if the property hadn't
1599  * been specified).  Also, the current code doesn't use CRSelEng
1600  * stuff to take a value from stylesheets.  Also, we aren't
1601  * setting any hooks to force an update for changes in any of
1602  * the inputs (i.e., in any of the elements that this function
1603  * queries).
1604  *
1605  * \par
1606  * Given that the default value for a property depends on what
1607  * property it is (e.g., whether to inherit or not), and given
1608  * the above comment about ignoring invalid values, and that the
1609  * repr parent isn't necessarily the right element to inherit
1610  * from (e.g., maybe we need to inherit from the referencing
1611  * <use> element instead), we should probably make the caller
1612  * responsible for ascending the repr tree as necessary.
1613  */
1614 gchar const *
1615 SPObject::getStyleProperty(gchar const *key, gchar const *def) const
1617     //g_return_val_if_fail(object != NULL, NULL);
1618     //g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
1619     g_return_val_if_fail(key != NULL, NULL);
1621         //XML Tree being used here.
1622     gchar const *style = getRepr()->attribute("style");
1623     if (style) {
1624         size_t const len = strlen(key);
1625         char const *p;
1626         while ( (p = strstr(style, key))
1627                 != NULL )
1628         {
1629             p += len;
1630             while ((*p <= ' ') && *p) p++;
1631             if (*p++ != ':') break;
1632             while ((*p <= ' ') && *p) p++;
1633             size_t const inherit_len = sizeof("inherit") - 1;
1634             if (*p
1635                 && !(strneq(p, "inherit", inherit_len)
1636                      && (p[inherit_len] == '\0'
1637                          || p[inherit_len] == ';'
1638                          || g_ascii_isspace(p[inherit_len])))) {
1639                 return p;
1640             }
1641         }
1642     }
1644         //XML Tree being used here.
1645     gchar const *val = getRepr()->attribute(key);
1646     if (val && !streq(val, "inherit")) {
1647         return val;
1648     }
1649     if (this->parent) {
1650         return (this->parent)->getStyleProperty(key, def);
1651     }
1653     return def;
1656 /**
1657  * Lifts SVG version of all root objects to version.
1658  */
1659 void
1660 SPObject::_requireSVGVersion(Inkscape::Version version) {
1661     for ( SPObject::ParentIterator iter=this ; iter ; ++iter ) {
1662         SPObject *object=iter;
1663         if (SP_IS_ROOT(object)) {
1664             SPRoot *root=SP_ROOT(object);
1665             if ( root->version.svg < version ) {
1666                 root->version.svg = version;
1667             }
1668         }
1669     }
1672 /**
1673  * Returns previous object in sibling list or NULL.
1674  */
1675 SPObject *
1676 SPObject::prev()
1678     SPObject *parent = SP_OBJECT_PARENT(this);
1679     for ( SPObject *i = parent->first_child(); i; i = SP_OBJECT_NEXT(i) ) {
1680         if (SP_OBJECT_NEXT(i) == this)
1681             return i;
1682     }
1683     return NULL;
1686 /* Titles and descriptions */
1688 /* Note:
1689    Titles and descriptions are stored in 'title' and 'desc' child elements
1690    (see section 5.4 of the SVG 1.0 and 1.1 specifications).  The spec allows
1691    an element to have more than one 'title' child element, but strongly
1692    recommends against this and requires using the first one if a choice must
1693    be made.  The same applies to 'desc' elements.  Therefore, these functions
1694    ignore all but the first 'title' child element and first 'desc' child
1695    element, except when deleting a title or description.
1696 */
1698 /**
1699  * Returns the title of this object, or NULL if there is none.
1700  * The caller must free the returned string using g_free() - see comment
1701  * for getTitleOrDesc() below.
1702  */
1703 gchar *
1704 SPObject::title() const
1706     return getTitleOrDesc("svg:title");
1709 /**
1710  * Sets the title of this object
1711  * A NULL first argument is interpreted as meaning that the existing title
1712  * (if any) should be deleted.
1713  * The second argument is optional - see setTitleOrDesc() below for details.
1714  */
1715 bool
1716 SPObject::setTitle(gchar const *title, bool verbatim)
1718     return setTitleOrDesc(title, "svg:title", verbatim);
1721 /**
1722  * Returns the description of this object, or NULL if there is none.
1723  * The caller must free the returned string using g_free() - see comment
1724  * for getTitleOrDesc() below.
1725  */
1726 gchar *
1727 SPObject::desc() const
1729     return getTitleOrDesc("svg:desc");
1732 /**
1733  * Sets the description of this object.
1734  * A NULL first argument is interpreted as meaning that the existing
1735  * description (if any) should be deleted.
1736  * The second argument is optional - see setTitleOrDesc() below for details.
1737  */
1738 bool
1739 SPObject::setDesc(gchar const *desc, bool verbatim)
1741     return setTitleOrDesc(desc, "svg:desc", verbatim);
1744 /**
1745  * Returns the title or description of this object, or NULL if there is none.
1746  *
1747  * The SVG spec allows 'title' and 'desc' elements to contain text marked up
1748  * using elements from other namespaces.  Therefore, this function cannot
1749  * in general just return a pointer to an existing string - it must instead
1750  * construct a string containing the title or description without the mark-up.
1751  * Consequently, the return value is a newly allocated string (or NULL), and
1752  * must be freed (using g_free()) by the caller.
1753  */
1754 gchar *
1755 SPObject::getTitleOrDesc(gchar const *svg_tagname) const
1757     SPObject *elem = findFirstChild(svg_tagname);
1758     if (elem == NULL) return NULL;
1759     return g_string_free(elem->textualContent(), FALSE);
1762 /**
1763  * Sets or deletes the title or description of this object.
1764  * A NULL 'value' argument causes the title or description to be deleted.
1765  *
1766  * 'verbatim' parameter:
1767  * If verbatim==true, then the title or description is set to exactly the
1768  * specified value.  If verbatim==false then two exceptions are made:
1769  *   (1) If the specified value is just whitespace, then the title/description
1770  *       is deleted.
1771  *   (2) If the specified value is the same as the current value except for
1772  *       mark-up, then the current value is left unchanged.
1773  * This is usually the desired behaviour, so 'verbatim' defaults to false for
1774  * setTitle() and setDesc().
1775  *
1776  * The return value is true if a change was made to the title/description,
1777  * and usually false otherwise.
1778  */
1779 bool
1780 SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim)
1782     if (!verbatim) {
1783         // If the new title/description is just whitespace,
1784         // treat it as though it were NULL.
1785         if (value) {
1786             bool just_whitespace = true;
1787             for (const gchar *cp = value; *cp; ++cp) {
1788                 if (!std::strchr("\r\n \t", *cp)) {
1789                     just_whitespace = false;
1790                     break;
1791                 }
1792             }
1793             if (just_whitespace) value = NULL;
1794         }
1795         // Don't stomp on mark-up if there is no real change.
1796         if (value) {
1797             gchar *current_value = getTitleOrDesc(svg_tagname);
1798             if (current_value) {
1799                 bool different = std::strcmp(current_value, value);
1800                 g_free(current_value);
1801                 if (!different) return false;
1802             }
1803         }
1804     }
1806     SPObject *elem = findFirstChild(svg_tagname);
1808     if (value == NULL) {
1809         if (elem == NULL) return false;
1810         // delete the title/description(s)
1811         while (elem) {
1812             elem->deleteObject();
1813             elem = findFirstChild(svg_tagname);
1814         }
1815         return true;
1816     }
1818     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
1820     if (elem == NULL) {
1821         // create a new 'title' or 'desc' element, putting it at the
1822         // beginning (in accordance with the spec's recommendations)
1823         Inkscape::XML::Node *xml_elem = xml_doc->createElement(svg_tagname);
1824         repr->addChild(xml_elem, NULL);
1825         elem = document->getObjectByRepr(xml_elem);
1826         Inkscape::GC::release(xml_elem);
1827     }
1828     else {
1829         // remove the current content of the 'text' or 'desc' element
1830         SPObject *child;
1831         while (NULL != (child = elem->firstChild())) child->deleteObject();
1832     }
1834     // add the new content
1835     elem->appendChildRepr(xml_doc->createTextNode(value));
1836     return true;
1839 /**
1840  * Find the first child of this object with a given tag name,
1841  * and return it.  Returns NULL if there is no matching child.
1842  */
1843 SPObject *
1844 SPObject::findFirstChild(gchar const *tagname) const
1846     for (SPObject *child = children; child; child = child->next)
1847     {
1848         if (child->repr->type() == Inkscape::XML::ELEMENT_NODE &&
1849             !strcmp(child->repr->name(), tagname)) return child;
1850     }
1851     return NULL;
1854 /**
1855  * Return the full textual content of an element (typically all the
1856  * content except the tags).
1857  * Must not be used on anything except elements.
1858  */
1859 GString*
1860 SPObject::textualContent() const
1862     GString* text = g_string_new("");
1864     for (const SPObject *child = firstChild(); child; child = child->next)
1865     {
1866         Inkscape::XML::NodeType child_type = child->repr->type();
1868         if (child_type == Inkscape::XML::ELEMENT_NODE) {
1869             GString * new_text = child->textualContent();
1870             g_string_append(text, new_text->str);
1871             g_string_free(new_text, TRUE);
1872         }
1873         else if (child_type == Inkscape::XML::TEXT_NODE) {
1874             g_string_append(text, child->repr->content());
1875         }
1876     }
1877     return text;
1880 /*
1881   Local Variables:
1882   mode:c++
1883   c-file-style:"stroustrup"
1884   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1885   indent-tabs-mode:nil
1886   fill-column:99
1887   End:
1888 */
1889 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :