Code

f81d5bdb59897b5d5a1ce18c452d5b267367ea5c
[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     sp_object_repr_child_added,
109     sp_object_repr_child_removed,
110     sp_object_repr_attr_changed,
111     sp_object_repr_content_changed,
112     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 /**
149  * Registers the SPObject class with Gdk and returns its type number.
150  */
151 GType
152 sp_object_get_type(void)
154     static GType type = 0;
155     if (!type) {
156         GTypeInfo info = {
157             sizeof(SPObjectClass),
158             NULL, NULL,
159             (GClassInitFunc) sp_object_class_init,
160             NULL, NULL,
161             sizeof(SPObject),
162             16,
163             (GInstanceInitFunc) sp_object_init,
164             NULL
165         };
166         type = g_type_register_static(G_TYPE_OBJECT, "SPObject", &info, (GTypeFlags)0);
167     }
168     return type;
171 /**
172  * Initializes the SPObject vtable.
173  */
174 static void
175 sp_object_class_init(SPObjectClass *klass)
177     GObjectClass *object_class;
179     object_class = (GObjectClass *) klass;
181     parent_class = (GObjectClass *) g_type_class_ref(G_TYPE_OBJECT);
183     object_class->finalize = sp_object_finalize;
185     klass->child_added = sp_object_child_added;
186     klass->remove_child = sp_object_remove_child;
187     klass->order_changed = sp_object_order_changed;
189     klass->release = sp_object_release;
191     klass->build = sp_object_build;
193     klass->set = sp_object_private_set;
194     klass->write = sp_object_private_write;
197 /**
198  * Callback to initialize the SPObject object.
199  */
200 static void
201 sp_object_init(SPObject *object)
203     debug("id=%x, typename=%s",object, g_type_name_from_instance((GTypeInstance*)object));
205     object->hrefcount = 0;
206     object->_total_hrefcount = 0;
207     object->document = NULL;
208     object->children = object->_last_child = NULL;
209     object->parent = object->next = NULL;
211         //used XML Tree here.
212         Inkscape::XML::Node *repr = object->getRepr();
213         repr = NULL;
214     SPObjectImpl::setIdNull(object);
216     object->_collection_policy = SPObject::COLLECT_WITH_PARENT;
218     new (&object->_release_signal) sigc::signal<void, SPObject *>();
219     new (&object->_modified_signal) sigc::signal<void, SPObject *, unsigned int>();
220     new (&object->_delete_signal) sigc::signal<void, SPObject *>();
221     new (&object->_position_changed_signal) sigc::signal<void, SPObject *>();
222     object->_successor = NULL;
224     // FIXME: now we create style for all objects, but per SVG, only the following can have style attribute:
225     // vg, g, defs, desc, title, symbol, use, image, switch, path, rect, circle, ellipse, line, polyline,
226     // polygon, text, tspan, tref, textPath, altGlyph, glyphRef, marker, linearGradient, radialGradient,
227     // stop, pattern, clipPath, mask, filter, feImage, a, font, glyph, missing-glyph, foreignObject
228     object->style = sp_style_new_from_object(object);
230     object->_label = NULL;
231     object->_default_label = NULL;
234 /**
235  * Callback to destroy all members and connections of object and itself.
236  */
237 static void
238 sp_object_finalize(GObject *object)
240     SPObject *spobject = (SPObject *)object;
242     g_free(spobject->_label);
243     g_free(spobject->_default_label);
244     spobject->_label = NULL;
245     spobject->_default_label = NULL;
247     if (spobject->_successor) {
248         sp_object_unref(spobject->_successor, NULL);
249         spobject->_successor = NULL;
250     }
252     if (((GObjectClass *) (parent_class))->finalize) {
253         (* ((GObjectClass *) (parent_class))->finalize)(object);
254     }
256     spobject->_release_signal.~signal();
257     spobject->_modified_signal.~signal();
258     spobject->_delete_signal.~signal();
259     spobject->_position_changed_signal.~signal();
262 namespace {
264 namespace Debug = Inkscape::Debug;
265 namespace Util = Inkscape::Util;
267 typedef Debug::SimpleEvent<Debug::Event::REFCOUNT> BaseRefCountEvent;
269 class RefCountEvent : public BaseRefCountEvent {
270 public:
271     RefCountEvent(SPObject *object, int bias, Util::ptr_shared<char> name)
272     : BaseRefCountEvent(name)
273     {
274         _addProperty("object", Util::format("%p", object));
275         _addProperty("class", Debug::demangle(g_type_name(G_TYPE_FROM_INSTANCE(object))));
276         _addProperty("new-refcount", Util::format("%d", G_OBJECT(object)->ref_count + bias));
277     }
278 };
280 class RefEvent : public RefCountEvent {
281 public:
282     RefEvent(SPObject *object)
283     : RefCountEvent(object, 1, Util::share_static_string("sp-object-ref"))
284     {}
285 };
287 class UnrefEvent : public RefCountEvent {
288 public:
289     UnrefEvent(SPObject *object)
290     : RefCountEvent(object, -1, Util::share_static_string("sp-object-unref"))
291     {}
292 };
296 gchar const* SPObject::getId() const {
297     return id;
300 Inkscape::XML::Node * SPObject::getRepr() {
301         return repr;
304 Inkscape::XML::Node const* SPObject::getRepr() const{
305             return repr;
309 /**
310  * Increase reference count of object, with possible debugging.
311  *
312  * \param owner If non-NULL, make debug log entry.
313  * \return object, NULL is error.
314  * \pre object points to real object
315  */
316 SPObject *
317 sp_object_ref(SPObject *object, SPObject *owner)
319     g_return_val_if_fail(object != NULL, NULL);
320     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
321     g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
323     Inkscape::Debug::EventTracker<RefEvent> tracker(object);
324     g_object_ref(G_OBJECT(object));
325     return object;
328 /**
329  * Decrease reference count of object, with possible debugging and
330  * finalization.
331  *
332  * \param owner If non-NULL, make debug log entry.
333  * \return always NULL
334  * \pre object points to real object
335  */
336 SPObject *
337 sp_object_unref(SPObject *object, SPObject *owner)
339     g_return_val_if_fail(object != NULL, NULL);
340     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
341     g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
343     Inkscape::Debug::EventTracker<UnrefEvent> tracker(object);
344     g_object_unref(G_OBJECT(object));
345     return NULL;
348 /**
349  * Increase weak refcount.
350  *
351  * Hrefcount is used for weak references, for example, to
352  * determine whether any graphical element references a certain gradient
353  * node.
354  * \param owner Ignored.
355  * \return object, NULL is error
356  * \pre object points to real object
357  */
358 SPObject *
359 sp_object_href(SPObject *object, gpointer /*owner*/)
361     g_return_val_if_fail(object != NULL, NULL);
362     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
364     object->hrefcount++;
365     object->_updateTotalHRefCount(1);
367     return object;
370 /**
371  * Decrease weak refcount.
372  *
373  * Hrefcount is used for weak references, for example, to determine whether
374  * any graphical element references a certain gradient node.
375  * \param owner Ignored.
376  * \return always NULL
377  * \pre object points to real object and hrefcount>0
378  */
379 SPObject *
380 sp_object_hunref(SPObject *object, gpointer /*owner*/)
382     g_return_val_if_fail(object != NULL, NULL);
383     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
384     g_return_val_if_fail(object->hrefcount > 0, NULL);
386     object->hrefcount--;
387     object->_updateTotalHRefCount(-1);
389     return NULL;
392 /**
393  * Adds increment to _total_hrefcount of object and its parents.
394  */
395 void
396 SPObject::_updateTotalHRefCount(int increment) {
397     SPObject *topmost_collectable = NULL;
398     for ( SPObject *iter = this ; iter ; iter = SP_OBJECT_PARENT(iter) ) {
399         iter->_total_hrefcount += increment;
400         if ( iter->_total_hrefcount < iter->hrefcount ) {
401             g_critical("HRefs overcounted");
402         }
403         if ( iter->_total_hrefcount == 0 &&
404              iter->_collection_policy != COLLECT_WITH_PARENT )
405         {
406             topmost_collectable = iter;
407         }
408     }
409     if (topmost_collectable) {
410         topmost_collectable->requestOrphanCollection();
411     }
414 /**
415  * True if object is non-NULL and this is some in/direct parent of object.
416  */
417 bool
418 SPObject::isAncestorOf(SPObject const *object) const {
419     g_return_val_if_fail(object != NULL, false);
420     object = SP_OBJECT_PARENT(object);
421     while (object) {
422         if ( object == this ) {
423             return true;
424         }
425         object = SP_OBJECT_PARENT(object);
426     }
427     return false;
430 namespace {
432 bool same_objects(SPObject const &a, SPObject const &b) {
433     return &a == &b;
438 /**
439  * Returns youngest object being parent to this and object.
440  */
441 SPObject const *
442 SPObject::nearestCommonAncestor(SPObject const *object) const {
443     g_return_val_if_fail(object != NULL, NULL);
445     using Inkscape::Algorithms::longest_common_suffix;
446     return longest_common_suffix<SPObject::ConstParentIterator>(this, object, NULL, &same_objects);
449 SPObject const *AncestorSon(SPObject const *obj, SPObject const *ancestor) {
450     if (obj == NULL || ancestor == NULL)
451         return NULL;
452     if (SP_OBJECT_PARENT(obj) == ancestor)
453         return obj;
454     return AncestorSon(SP_OBJECT_PARENT(obj), ancestor);
457 /**
458  * Compares height of objects in tree.
459  *
460  * Works for different-parent objects, so long as they have a common ancestor.
461  * \return \verbatim
462  *    0    positions are equivalent
463  *    1    first object's position is greater than the second
464  *   -1    first object's position is less than the second   \endverbatim
465  */
466 int
467 sp_object_compare_position(SPObject const *first, SPObject const *second)
469     if (first == second) return 0;
471     SPObject const *ancestor = first->nearestCommonAncestor(second);
472     if (ancestor == NULL) return 0; // cannot compare, no common ancestor!
474     // we have an object and its ancestor (should not happen when sorting selection)
475     if (ancestor == first)
476         return 1;
477     if (ancestor == second)
478         return -1;
480     SPObject const *to_first = AncestorSon(first, ancestor);
481     SPObject const *to_second = AncestorSon(second, ancestor);
483     g_assert(SP_OBJECT_PARENT(to_second) == SP_OBJECT_PARENT(to_first));
485     return sp_repr_compare_position(SP_OBJECT_REPR(to_first), SP_OBJECT_REPR(to_second));
489 /**
490  * Append repr as child of this object.
491  * \pre this is not a cloned object
492  */
493 SPObject *
494 SPObject::appendChildRepr(Inkscape::XML::Node *repr) {
495     if (!SP_OBJECT_IS_CLONED(this)) {
496         SP_OBJECT_REPR(this)->appendChild(repr);
497         return SP_OBJECT_DOCUMENT(this)->getObjectByRepr(repr);
498     } else {
499         g_critical("Attempt to append repr as child of cloned object");
500         return NULL;
501     }
504 /**
505  * Retrieves the children as a GSList object, optionally ref'ing the children
506  * in the process, if add_ref is specified.
507  */
508 GSList *SPObject::childList(bool add_ref, Action) {
509     GSList *l = NULL;
510     for (SPObject *child = this->first_child() ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
511         if (add_ref)
512             g_object_ref (G_OBJECT (child));
514         l = g_slist_prepend (l, child);
515     }
516     return l;
520 /** Gets the label property for the object or a default if no label
521  *  is defined.
522  */
523 gchar const *
524 SPObject::label() const {
525     return _label;
528 /** Returns a default label property for the object. */
529 gchar const *
530 SPObject::defaultLabel() const {
531     if (_label) {
532         return _label;
533     } else {
534         if (!_default_label) {
535             gchar const *id=SP_OBJECT_ID(this);
536             if (id) {
537                 _default_label = g_strdup_printf("#%s", id);
538             } else {
539                 _default_label = g_strdup_printf("<%s>", SP_OBJECT_REPR(this)->name());
540             }
541         }
542         return _default_label;
543     }
546 /** Sets the label property for the object */
547 void
548 SPObject::setLabel(gchar const *label) {
549     SP_OBJECT_REPR(this)->setAttribute("inkscape:label", label, false);
553 /** Queues the object for orphan collection */
554 void
555 SPObject::requestOrphanCollection() {
556     g_return_if_fail(document != NULL);
558     // do not remove style or script elements (Bug #276244)
559     if (SP_IS_STYLE_ELEM(this))
560         return;
561     if (SP_IS_SCRIPT(this))
562         return;
564     document->queueForOrphanCollection(this);
566     /** \todo
567      * This is a temporary hack added to make fill&stroke rebuild its
568      * gradient list when the defs are vacuumed.  gradient-vector.cpp
569      * listens to the modified signal on defs, and now we give it that
570      * signal.  Mental says that this should be made automatic by
571      * merging SPObjectGroup with SPObject; SPObjectGroup would issue
572      * this signal automatically. Or maybe just derive SPDefs from
573      * SPObjectGroup?
574      */
576     this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
579 /** Sends the delete signal to all children of this object recursively */
580 void
581 SPObject::_sendDeleteSignalRecursive() {
582     for (SPObject *child = this->first_child(); child; child = SP_OBJECT_NEXT(child)) {
583         child->_delete_signal.emit(child);
584         child->_sendDeleteSignalRecursive();
585     }
588 /**
589  * Deletes the object reference, unparenting it from its parent.
590  *
591  * If the \a propagate parameter is set to true, it emits a delete
592  * signal.  If the \a propagate_descendants parameter is true, it
593  * recursively sends the delete signal to children.
594  */
595 void
596 SPObject::deleteObject(bool propagate, bool propagate_descendants)
598     sp_object_ref(this, NULL);
599     if (propagate) {
600         _delete_signal.emit(this);
601     }
602     if (propagate_descendants) {
603         this->_sendDeleteSignalRecursive();
604     }
606     Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
607     if (repr && sp_repr_parent(repr)) {
608         sp_repr_unparent(repr);
609     }
611     if (_successor) {
612         _successor->deleteObject(propagate, propagate_descendants);
613     }
614     sp_object_unref(this, NULL);
617 /**
618  * Put object into object tree, under parent, and behind prev;
619  * also update object's XML space.
620  */
621 void
622 SPObject::attach(SPObject *object, SPObject *prev)
624     //g_return_if_fail(parent != NULL);
625     //g_return_if_fail(SP_IS_OBJECT(parent));
626     g_return_if_fail(object != NULL);
627     g_return_if_fail(SP_IS_OBJECT(object));
628     g_return_if_fail(!prev || SP_IS_OBJECT(prev));
629     g_return_if_fail(!prev || prev->parent == this);
630     g_return_if_fail(!object->parent);
632     sp_object_ref(object, this);
633     object->parent = this;
634     this->_updateTotalHRefCount(object->_total_hrefcount);
636     SPObject *next;
637     if (prev) {
638         next = prev->next;
639         prev->next = object;
640     } else {
641         next = this->children;
642         this->children = object;
643     }
644     object->next = next;
645     if (!next) {
646         this->_last_child = object;
647     }
648     if (!object->xml_space.set)
649         object->xml_space.value = this->xml_space.value;
652 /**
653  * In list of object's siblings, move object behind prev.
654  */
655 void
656 SPObject::reorder(SPObject *prev) {
657     //g_return_if_fail(object != NULL);
658     //g_return_if_fail(SP_IS_OBJECT(object));
659     g_return_if_fail(this->parent != NULL);
660     g_return_if_fail(this != prev);
661     g_return_if_fail(!prev || SP_IS_OBJECT(prev));
662     g_return_if_fail(!prev || prev->parent == this->parent);
664     SPObject *const parent=this->parent;
666     SPObject *old_prev=NULL;
667     for ( SPObject *child = parent->children ; child && child != this ;
668           child = child->next )
669     {
670         old_prev = child;
671     }
673     SPObject *next=this->next;
674     if (old_prev) {
675         old_prev->next = next;
676     } else {
677         parent->children = next;
678     }
679     if (!next) {
680         parent->_last_child = old_prev;
681     }
682     if (prev) {
683         next = prev->next;
684         prev->next = this;
685     } else {
686         next = parent->children;
687         parent->children = this;
688     }
689     this->next = next;
690     if (!next) {
691         parent->_last_child = this;
692     }
695 /**
696  * Remove object from parent's children, release and unref it.
697  */
698 void
699 SPObject::detach(SPObject *object) {
700     //g_return_if_fail(parent != NULL);
701     //g_return_if_fail(SP_IS_OBJECT(parent));
702     g_return_if_fail(object != NULL);
703     g_return_if_fail(SP_IS_OBJECT(object));
704     g_return_if_fail(object->parent == this);
706     object->releaseReferences();
708     SPObject *prev=NULL;
709     for ( SPObject *child = this->children ; child && child != object ;
710           child = child->next )
711     {
712         prev = child;
713     }
715     SPObject *next=object->next;
716     if (prev) {
717         prev->next = next;
718     } else {
719         this->children = next;
720     }
721     if (!next) {
722         this->_last_child = prev;
723     }
725     object->next = NULL;
726     object->parent = NULL;
728     this->_updateTotalHRefCount(-object->_total_hrefcount);
729     sp_object_unref(object, this);
732 /**
733  * Return object's child whose node pointer equals repr.
734  */
735 SPObject *
736 SPObject::get_child_by_repr(Inkscape::XML::Node *repr)
738     //g_return_val_if_fail(object != NULL, NULL);
739     //g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
740     g_return_val_if_fail(repr != NULL, NULL);
742     if (this->_last_child && SP_OBJECT_REPR(this->_last_child) == repr)
743         return this->_last_child;   // optimization for common scenario
744     for ( SPObject *child = this->children ; child ; child = child->next ) {
745         if ( SP_OBJECT_REPR(child) == repr ) {
746             return child;
747         }
748     }
750     return NULL;
753 /**
754  * Callback for child_added event.
755  * Invoked whenever the given mutation event happens in the XML tree.
756  */
757 static void
758 sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
760     GType type = sp_repr_type_lookup(child);
761     if (!type) {
762         return;
763     }
764     SPObject *ochild = SP_OBJECT(g_object_new(type, 0));
765     SPObject *prev = ref ? object->get_child_by_repr(ref) : NULL;
766     object->attach(ochild, prev);
767     sp_object_unref(ochild, NULL);
769     ochild->invoke_build(object->document, child, SP_OBJECT_IS_CLONED(object));
772 /**
773  * Removes, releases and unrefs all children of object.
774  *
775  * This is the opposite of build. It has to be invoked as soon as the
776  * object is removed from the tree, even if it is still alive according
777  * to reference count. The frontend unregisters the object from the
778  * document and releases the SPRepr bindings; implementations should free
779  * state data and release all child objects.  Invoking release on
780  * SPRoot destroys the whole document tree.
781  * \see sp_object_build()
782  */
783 static void sp_object_release(SPObject *object)
785     debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
786     while (object->children) {
787         object->detach(object->children);
788     }
791 /**
792  * Remove object's child whose node equals repr, release and
793  * unref it.
794  *
795  * Invoked whenever the given mutation event happens in the XML
796  * tree, BEFORE removal from the XML tree happens, so grouping
797  * objects can safely release the child data.
798  */
799 static void
800 sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child)
802     debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
803     SPObject *ochild = object->get_child_by_repr(child);
804     g_return_if_fail (ochild != NULL || !strcmp("comment", child->name())); // comments have no objects
805     if (ochild)
806         object->detach(ochild);
809 /**
810  * Move object corresponding to child after sibling object corresponding
811  * to new_ref.
812  * Invoked whenever the given mutation event happens in the XML tree.
813  * \param old_ref Ignored
814  */
815 static void sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node */*old_ref*/,
816                                     Inkscape::XML::Node *new_ref)
818     SPObject *ochild = object->get_child_by_repr(child);
819     g_return_if_fail(ochild != NULL);
820     SPObject *prev = new_ref ? object->get_child_by_repr(new_ref) : NULL;
821     ochild->reorder(prev);
822     ochild->_position_changed_signal.emit(ochild);
825 /**
826  * Virtual build callback.
827  *
828  * This has to be invoked immediately after creation of an SPObject. The
829  * frontend method ensures that the new object is properly attached to
830  * the document and repr; implementation then will parse all of the attributes,
831  * generate the children objects and so on.  Invoking build on the SPRoot
832  * object results in creation of the whole document tree (this is, what
833  * SPDocument does after the creation of the XML tree).
834  * \see sp_object_release()
835  */
836 static void
837 sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
839     /* Nothing specific here */
840     debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
842     sp_object_read_attr(object, "xml:space");
843     sp_object_read_attr(object, "inkscape:label");
844     sp_object_read_attr(object, "inkscape:collect");
846     for (Inkscape::XML::Node *rchild = repr->firstChild() ; rchild != NULL; rchild = rchild->next()) {
847         GType type = sp_repr_type_lookup(rchild);
848         if (!type) {
849             continue;
850         }
851         SPObject *child = SP_OBJECT(g_object_new(type, 0));
852         object->attach(child, object->lastChild());
853         sp_object_unref(child, NULL);
854         child->invoke_build(document, rchild, SP_OBJECT_IS_CLONED(object));
855     }
858 void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
860     debug("id=%x, typename=%s", this, g_type_name_from_instance((GTypeInstance*)this));
862     //g_assert(object != NULL);
863     //g_assert(SP_IS_OBJECT(object));
864     g_assert(document != NULL);
865     g_assert(repr != NULL);
867     g_assert(this->document == NULL);
868     g_assert(this->repr == NULL);
869     g_assert(this->getId() == NULL);
871     /* Bookkeeping */
873     this->document = document;
874     this->repr = repr;
875     if (!cloned)
876         Inkscape::GC::anchor(repr);
877     this->cloned = cloned;
879     if (!SP_OBJECT_IS_CLONED(this)) {
880         this->document->bindObjectToRepr(this->repr, this);
882         if (Inkscape::XML::id_permitted(this->repr)) {
883             /* If we are not cloned, and not seeking, force unique id */
884             gchar const *id = this->repr->attribute("id");
885             if (!document->isSeeking()) {
886                 {
887                     gchar *realid = sp_object_get_unique_id(this, id);
888                     g_assert(realid != NULL);
890                     this->document->bindObjectToId(realid, this);
891                     SPObjectImpl::setId(this, realid);
892                     g_free(realid);
893                 }
895                 /* Redefine ID, if required */
896                 if ((id == NULL) || (strcmp(id, this->getId()) != 0)) {
897                     this->repr->setAttribute("id", this->getId());
898                 }
899             } else if (id) {
900                 // bind if id, but no conflict -- otherwise, we can expect
901                 // a subsequent setting of the id attribute
902                 if (!this->document->getObjectById(id)) {
903                     this->document->bindObjectToId(id, this);
904                     SPObjectImpl::setId(this, id);
905                 }
906             }
907         }
908     } else {
909         g_assert(this->getId() == NULL);
910     }
912     /* Invoke derived methods, if any */
913     if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->build) {
914         (*((SPObjectClass *) G_OBJECT_GET_CLASS(this))->build)(this, document, repr);
915     }
917     /* Signalling (should be connected AFTER processing derived methods */
918     sp_repr_add_listener(repr, &object_event_vector, this);
921 void SPObject::releaseReferences() {
922     g_assert(this->document);
923     g_assert(this->repr);
925     sp_repr_remove_listener_by_data(this->repr, this);
927     this->_release_signal.emit(this);
928     SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
929     if (klass->release) {
930         klass->release(this);
931     }
933     /* all hrefs should be released by the "release" handlers */
934     g_assert(this->hrefcount == 0);
936     if (!SP_OBJECT_IS_CLONED(this)) {
937         if (this->id) {
938             this->document->bindObjectToId(this->id, NULL);
939         }
940         g_free(this->id);
941         this->id = NULL;
943         g_free(this->_default_label);
944         this->_default_label = NULL;
946         this->document->bindObjectToRepr(this->repr, NULL);
948         Inkscape::GC::release(this->repr);
949     } else {
950         g_assert(!this->id);
951     }
953     if (this->style) {
954         this->style = sp_style_unref(this->style);
955     }
957     this->document = NULL;
958     this->repr = NULL;
962 SPObject *SPObject::getNext()
964     return next;
967 SPObject *SPObject::getPrev()
969     return sp_object_prev(this);
972 /**
973  * Callback for child_added node event.
974  */
975 static void
976 sp_object_repr_child_added(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data)
978     SPObject *object = SP_OBJECT(data);
980     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->child_added)
981         (*((SPObjectClass *)G_OBJECT_GET_CLASS(object))->child_added)(object, child, ref);
984 /**
985  * Callback for remove_child node event.
986  */
987 static void
988 sp_object_repr_child_removed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node */*ref*/, gpointer data)
990     SPObject *object = SP_OBJECT(data);
992     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->remove_child) {
993         (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->remove_child)(object, child);
994     }
997 /**
998  * Callback for order_changed node event.
999  *
1000  * \todo fixme:
1001  */
1002 static void
1003 sp_object_repr_order_changed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data)
1005     SPObject *object = SP_OBJECT(data);
1007     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->order_changed) {
1008         (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->order_changed)(object, child, old, newer);
1009     }
1012 /**
1013  * Callback for set event.
1014  */
1015 static void
1016 sp_object_private_set(SPObject *object, unsigned int key, gchar const *value)
1018     g_assert(key != SP_ATTR_INVALID);
1020     switch (key) {
1021         case SP_ATTR_ID:
1023                         //XML Tree being used here.
1024             if ( !SP_OBJECT_IS_CLONED(object) && object->getRepr()->type() == Inkscape::XML::ELEMENT_NODE ) {
1025                 SPDocument *document=object->document;
1026                 SPObject *conflict=NULL;
1028                 gchar const *new_id = value;
1030                 if (new_id) {
1031                     conflict = document->getObjectById((char const *)new_id);
1032                 }
1034                 if ( conflict && conflict != object ) {
1035                     if (!document->isSeeking()) {
1036                         sp_object_ref(conflict, NULL);
1037                         // give the conflicting object a new ID
1038                         gchar *new_conflict_id = sp_object_get_unique_id(conflict, NULL);
1039                         SP_OBJECT_REPR(conflict)->setAttribute("id", new_conflict_id);
1040                         g_free(new_conflict_id);
1041                         sp_object_unref(conflict, NULL);
1042                     } else {
1043                         new_id = NULL;
1044                     }
1045                 }
1047                 if (object->getId()) {
1048                     document->bindObjectToId(object->getId(), NULL);
1049                     SPObjectImpl::setId(object, 0);
1050                 }
1052                 if (new_id) {
1053                     SPObjectImpl::setId(object, new_id);
1054                     document->bindObjectToId(object->getId(), object);
1055                 }
1057                 g_free(object->_default_label);
1058                 object->_default_label = NULL;
1059             }
1060             break;
1061         case SP_ATTR_INKSCAPE_LABEL:
1062             g_free(object->_label);
1063             if (value) {
1064                 object->_label = g_strdup(value);
1065             } else {
1066                 object->_label = NULL;
1067             }
1068             g_free(object->_default_label);
1069             object->_default_label = NULL;
1070             break;
1071         case SP_ATTR_INKSCAPE_COLLECT:
1072             if ( value && !strcmp(value, "always") ) {
1073                 object->setCollectionPolicy(SPObject::ALWAYS_COLLECT);
1074             } else {
1075                 object->setCollectionPolicy(SPObject::COLLECT_WITH_PARENT);
1076             }
1077             break;
1078         case SP_ATTR_XML_SPACE:
1079             if (value && !strcmp(value, "preserve")) {
1080                 object->xml_space.value = SP_XML_SPACE_PRESERVE;
1081                 object->xml_space.set = TRUE;
1082             } else if (value && !strcmp(value, "default")) {
1083                 object->xml_space.value = SP_XML_SPACE_DEFAULT;
1084                 object->xml_space.set = TRUE;
1085             } else if (object->parent) {
1086                 SPObject *parent;
1087                 parent = object->parent;
1088                 object->xml_space.value = parent->xml_space.value;
1089             }
1090             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1091             break;
1092         case SP_ATTR_STYLE:
1093             sp_style_read_from_object(object->style, object);
1094             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1095             break;
1096         default:
1097             break;
1098     }
1101 /**
1102  * Call virtual set() function of object.
1103  */
1104 void
1105 sp_object_set(SPObject *object, unsigned int key, gchar const *value)
1107     g_assert(object != NULL);
1108     g_assert(SP_IS_OBJECT(object));
1110     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->set) {
1111         ((SPObjectClass *) G_OBJECT_GET_CLASS(object))->set(object, key, value);
1112     }
1115 /**
1116  * Read value of key attribute from XML node into object.
1117  */
1118 void
1119 sp_object_read_attr(SPObject *object, gchar const *key)
1121     g_assert(object != NULL);
1122     g_assert(SP_IS_OBJECT(object));
1123     g_assert(key != NULL);
1125         //XML Tree being used here.
1126     g_assert(object->getRepr() != NULL);
1128     unsigned int keyid = sp_attribute_lookup(key);
1129     if (keyid != SP_ATTR_INVALID) {
1130         /* Retrieve the 'key' attribute from the object's XML representation */
1131         gchar const *value = object->getRepr()->attribute(key);
1133         sp_object_set(object, keyid, value);
1134     }
1137 /**
1138  * Callback for attr_changed node event.
1139  */
1140 static void
1141 sp_object_repr_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *key, gchar const */*oldval*/, gchar const */*newval*/, bool is_interactive, gpointer data)
1143     SPObject *object = SP_OBJECT(data);
1145     sp_object_read_attr(object, key);
1147     // manual changes to extension attributes require the normal
1148     // attributes, which depend on them, to be updated immediately
1149     if (is_interactive) {
1150         object->updateRepr(0);
1151     }
1154 /**
1155  * Callback for content_changed node event.
1156  */
1157 static void
1158 sp_object_repr_content_changed(Inkscape::XML::Node */*repr*/, gchar const */*oldcontent*/, gchar const */*newcontent*/, gpointer data)
1160     SPObject *object = SP_OBJECT(data);
1162     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)
1163         (*((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)(object);
1166 /**
1167  * Return string representation of space value.
1168  */
1169 static gchar const*
1170 sp_xml_get_space_string(unsigned int space)
1172     switch (space) {
1173         case SP_XML_SPACE_DEFAULT:
1174             return "default";
1175         case SP_XML_SPACE_PRESERVE:
1176             return "preserve";
1177         default:
1178             return NULL;
1179     }
1182 /**
1183  * Callback for write event.
1184  */
1185 static Inkscape::XML::Node *
1186 sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
1188     if (!repr && (flags & SP_OBJECT_WRITE_BUILD)) {
1189         repr = SP_OBJECT_REPR(object)->duplicate(doc);
1190         if (!( flags & SP_OBJECT_WRITE_EXT )) {
1191             repr->setAttribute("inkscape:collect", NULL);
1192         }
1193     } else {
1194         repr->setAttribute("id", object->getId());
1196         if (object->xml_space.set) {
1197             char const *xml_space;
1198             xml_space = sp_xml_get_space_string(object->xml_space.value);
1199             repr->setAttribute("xml:space", xml_space);
1200         }
1202         if ( flags & SP_OBJECT_WRITE_EXT &&
1203              object->collectionPolicy() == SPObject::ALWAYS_COLLECT )
1204         {
1205             repr->setAttribute("inkscape:collect", "always");
1206         } else {
1207             repr->setAttribute("inkscape:collect", NULL);
1208         }
1210         SPStyle const *const obj_style = SP_OBJECT_STYLE(object);
1211         if (obj_style) {
1212             gchar *s = sp_style_write_string(obj_style, SP_STYLE_FLAG_IFSET);
1213             repr->setAttribute("style", ( *s ? s : NULL ));
1214             g_free(s);
1215         } else {
1216             /** \todo I'm not sure what to do in this case.  Bug #1165868
1217              * suggests that it can arise, but the submitter doesn't know
1218              * how to do so reliably.  The main two options are either
1219              * leave repr's style attribute unchanged, or explicitly clear it.
1220              * Must also consider what to do with property attributes for
1221              * the element; see below.
1222              */
1223             char const *style_str = repr->attribute("style");
1224             if (!style_str) {
1225                 style_str = "NULL";
1226             }
1227             g_warning("Item's style is NULL; repr style attribute is %s", style_str);
1228         }
1230         /** \note We treat object->style as authoritative.  Its effects have
1231          * been written to the style attribute above; any properties that are
1232          * unset we take to be deliberately unset (e.g. so that clones can
1233          * override the property).
1234          *
1235          * Note that the below has an undesirable consequence of changing the
1236          * appearance on renderers that lack CSS support (e.g. SVG tiny);
1237          * possibly we should write property attributes instead of a style
1238          * attribute.
1239          */
1240         sp_style_unset_property_attrs (object);
1241     }
1243     return repr;
1246 /**
1247  * Update this object's XML node with flags value.
1248  */
1249 Inkscape::XML::Node *
1250 SPObject::updateRepr(unsigned int flags) {
1251     if (!SP_OBJECT_IS_CLONED(this)) {
1252         Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
1253         if (repr) {
1254             return updateRepr(repr->document(), repr, flags);
1255         } else {
1256             g_critical("Attempt to update non-existent repr");
1257             return NULL;
1258         }
1259     } else {
1260         /* cloned objects have no repr */
1261         return NULL;
1262     }
1265 /** Used both to create reprs in the original document, and to create
1266  *  reprs in another document (e.g. a temporary document used when
1267  *  saving as "Plain SVG"
1268  */
1269 Inkscape::XML::Node *
1270 SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags) {
1271     g_assert(doc != NULL);
1273     if (SP_OBJECT_IS_CLONED(this)) {
1274         /* cloned objects have no repr */
1275         return NULL;
1276     }
1277     if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write) {
1278         if (!(flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1279             repr = SP_OBJECT_REPR(this);
1280         }
1281         return ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write(this, doc, repr, flags);
1282     } else {
1283         g_warning("Class %s does not implement ::write", G_OBJECT_TYPE_NAME(this));
1284         if (!repr) {
1285             if (flags & SP_OBJECT_WRITE_BUILD) {
1286                 repr = SP_OBJECT_REPR(this)->duplicate(doc);
1287             }
1288             /// \todo FIXME: else probably error (Lauris) */
1289         } else {
1290             repr->mergeFrom(SP_OBJECT_REPR(this), "id");
1291         }
1292         return repr;
1293     }
1296 /* Modification */
1298 /**
1299  * Add \a flags to \a object's as dirtiness flags, and
1300  * recursively add CHILD_MODIFIED flag to
1301  * parent and ancestors (as far up as necessary).
1302  */
1303 void
1304 SPObject::requestDisplayUpdate(unsigned int flags)
1306     g_return_if_fail( this->document != NULL );
1308     if (update_in_progress) {
1309         g_print("WARNING: Requested update while update in progress, counter = %d\n", update_in_progress);
1310     }
1312     /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1313      * SP_OBJECT_CHILD_MODIFIED_FLAG */
1314     g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1315     g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1316     g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1318     bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1320     this->uflags |= flags;
1322     /* If requestModified has already been called on this object or one of its children, then we
1323      * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1324      */
1325     if (already_propagated) {
1326         SPObject *parent = SP_OBJECT_PARENT(this);
1327         if (parent) {
1328             parent->requestDisplayUpdate(SP_OBJECT_CHILD_MODIFIED_FLAG);
1329         } else {
1330             SP_OBJECT_DOCUMENT(this)->request_modified();
1331         }
1332     }
1335 /**
1336  * Update views
1337  */
1338 void
1339 SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
1341     g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1343     update_in_progress ++;
1345 #ifdef SP_OBJECT_DEBUG_CASCADE
1346     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);
1347 #endif
1349     /* Get this flags */
1350     flags |= this->uflags;
1351     /* Copy flags to modified cascade for later processing */
1352     this->mflags |= this->uflags;
1353     /* We have to clear flags here to allow rescheduling update */
1354     this->uflags = 0;
1356     // Merge style if we have good reasons to think that parent style is changed */
1357     /** \todo
1358      * I am not sure whether we should check only propagated
1359      * flag. We are currently assuming that style parsing is
1360      * done immediately. I think this is correct (Lauris).
1361      */
1362     if ((flags & SP_OBJECT_STYLE_MODIFIED_FLAG) && (flags & SP_OBJECT_PARENT_MODIFIED_FLAG)) {
1363         if (this->style && this->parent) {
1364             sp_style_merge_from_parent(this->style, this->parent->style);
1365         }
1366     }
1368     try
1369     {
1370         if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update)
1371             ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);
1372     }
1373     catch(...)
1374     {
1375         /** \todo
1376         * in case of catching an exception we need to inform the user somehow that the document is corrupted
1377         * maybe by implementing an document flag documentOk
1378         * or by a modal error dialog
1379         */
1380         g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
1381     }
1383     update_in_progress --;
1386 /**
1387  * Request modified always bubbles *up* the tree, as opposed to
1388  * request display update, which trickles down and relies on the
1389  * flags set during this pass...
1390  */
1391 void
1392 SPObject::requestModified(unsigned int flags)
1394     g_return_if_fail( this->document != NULL );
1396     /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1397      * SP_OBJECT_CHILD_MODIFIED_FLAG */
1398     g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1399     g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1400     g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1402     bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1404     this->mflags |= flags;
1406     /* If requestModified has already been called on this object or one of its children, then we
1407      * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1408      */
1409     if (already_propagated) {
1410         SPObject *parent=SP_OBJECT_PARENT(this);
1411         if (parent) {
1412             parent->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
1413         } else {
1414             SP_OBJECT_DOCUMENT(this)->request_modified();
1415         }
1416     }
1419 /**
1420  *  Emits the MODIFIED signal with the object's flags.
1421  *  The object's mflags are the original set aside during the update pass for
1422  *  later delivery here.  Once emitModified() is called, those flags don't
1423  *  need to be stored any longer.
1424  */
1425 void
1426 SPObject::emitModified(unsigned int flags)
1428     /* only the MODIFIED_CASCADE flag is legal here */
1429     g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1431 #ifdef SP_OBJECT_DEBUG_CASCADE
1432     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);
1433 #endif
1435     flags |= this->mflags;
1436     /* We have to clear mflags beforehand, as signal handlers may
1437      * make changes and therefore queue new modification notifications
1438      * themselves. */
1439     this->mflags = 0;
1441     g_object_ref(G_OBJECT(this));
1442     SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
1443     if (klass->modified) {
1444         klass->modified(this, flags);
1445     }
1446     _modified_signal.emit(this, flags);
1447     g_object_unref(G_OBJECT(this));
1450 gchar const *
1451 sp_object_tagName_get(SPObject const *object, SPException *ex)
1453     /* If exception is not clear, return */
1454     if (!SP_EXCEPTION_IS_OK(ex)) {
1455         return NULL;
1456     }
1458     /// \todo fixme: Exception if object is NULL? */
1459         //XML Tree being used here.
1460     return object->getRepr()->name();
1463 gchar const *
1464 sp_object_getAttribute(SPObject const *object, gchar const *key, SPException *ex)
1466     /* If exception is not clear, return */
1467     if (!SP_EXCEPTION_IS_OK(ex)) {
1468         return NULL;
1469     }
1471     /// \todo fixme: Exception if object is NULL? */
1472         //XML Tree being used here.
1473     return (gchar const *) object->getRepr()->attribute(key);
1476 void
1477 sp_object_setAttribute(SPObject *object, gchar const *key, gchar const *value, SPException *ex)
1479     /* If exception is not clear, return */
1480     g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1482     /// \todo fixme: Exception if object is NULL? */
1483         //XML Tree being used here.
1484     object->getRepr()->setAttribute(key, value, false);
1487 void
1488 sp_object_removeAttribute(SPObject *object, gchar const *key, SPException *ex)
1490     /* If exception is not clear, return */
1491     g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1493     /// \todo fixme: Exception if object is NULL? */
1494         //XML Tree being used here.
1495     object->getRepr()->setAttribute(key, NULL, false);
1498 /* Helper */
1500 static gchar *
1501 sp_object_get_unique_id(SPObject *object, gchar const *id)
1503     static unsigned long count = 0;
1505     g_assert(SP_IS_OBJECT(object));
1507     count++;
1509         //XML Tree being used here.
1510     gchar const *name = object->getRepr()->name();
1511     g_assert(name != NULL);
1513     gchar const *local = strchr(name, ':');
1514     if (local) {
1515         name = local + 1;
1516     }
1518     if (id != NULL) {
1519         if (object->document->getObjectById(id) == NULL) {
1520             return g_strdup(id);
1521         }
1522     }
1524     size_t const name_len = strlen(name);
1525     size_t const buflen = name_len + (sizeof(count) * 10 / 4) + 1;
1526     gchar *const buf = (gchar *) g_malloc(buflen);
1527     memcpy(buf, name, name_len);
1528     gchar *const count_buf = buf + name_len;
1529     size_t const count_buflen = buflen - name_len;
1530     do {
1531         ++count;
1532         g_snprintf(count_buf, count_buflen, "%lu", count);
1533     } while ( object->document->getObjectById(buf) != NULL );
1534     return buf;
1537 /* Style */
1539 /**
1540  * Returns an object style property.
1541  *
1542  * \todo
1543  * fixme: Use proper CSS parsing.  The current version is buggy
1544  * in a number of situations where key is a substring of the
1545  * style string other than as a property name (including
1546  * where key is a substring of a property name), and is also
1547  * buggy in its handling of inheritance for properties that
1548  * aren't inherited by default.  It also doesn't allow for
1549  * the case where the property is specified but with an invalid
1550  * value (in which case I believe the CSS2 error-handling
1551  * behaviour applies, viz. behave as if the property hadn't
1552  * been specified).  Also, the current code doesn't use CRSelEng
1553  * stuff to take a value from stylesheets.  Also, we aren't
1554  * setting any hooks to force an update for changes in any of
1555  * the inputs (i.e., in any of the elements that this function
1556  * queries).
1557  *
1558  * \par
1559  * Given that the default value for a property depends on what
1560  * property it is (e.g., whether to inherit or not), and given
1561  * the above comment about ignoring invalid values, and that the
1562  * repr parent isn't necessarily the right element to inherit
1563  * from (e.g., maybe we need to inherit from the referencing
1564  * <use> element instead), we should probably make the caller
1565  * responsible for ascending the repr tree as necessary.
1566  */
1567 gchar const *
1568 sp_object_get_style_property(SPObject const *object, gchar const *key, gchar const *def)
1570     g_return_val_if_fail(object != NULL, NULL);
1571     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
1572     g_return_val_if_fail(key != NULL, NULL);
1574         //XML Tree being used here.
1575     gchar const *style = object->getRepr()->attribute("style");
1576     if (style) {
1577         size_t const len = strlen(key);
1578         char const *p;
1579         while ( (p = strstr(style, key))
1580                 != NULL )
1581         {
1582             p += len;
1583             while ((*p <= ' ') && *p) p++;
1584             if (*p++ != ':') break;
1585             while ((*p <= ' ') && *p) p++;
1586             size_t const inherit_len = sizeof("inherit") - 1;
1587             if (*p
1588                 && !(strneq(p, "inherit", inherit_len)
1589                      && (p[inherit_len] == '\0'
1590                          || p[inherit_len] == ';'
1591                          || g_ascii_isspace(p[inherit_len])))) {
1592                 return p;
1593             }
1594         }
1595     }
1597         //XML Tree being used here.
1598     gchar const *val = object->getRepr()->attribute(key);
1599     if (val && !streq(val, "inherit")) {
1600         return val;
1601     }
1602     if (object->parent) {
1603         return sp_object_get_style_property(object->parent, key, def);
1604     }
1606     return def;
1609 /**
1610  * Lifts SVG version of all root objects to version.
1611  */
1612 void
1613 SPObject::_requireSVGVersion(Inkscape::Version version) {
1614     for ( SPObject::ParentIterator iter=this ; iter ; ++iter ) {
1615         SPObject *object=iter;
1616         if (SP_IS_ROOT(object)) {
1617             SPRoot *root=SP_ROOT(object);
1618             if ( root->version.svg < version ) {
1619                 root->version.svg = version;
1620             }
1621         }
1622     }
1625 /**
1626  * Returns previous object in sibling list or NULL.
1627  */
1628 SPObject *
1629 sp_object_prev(SPObject *child)
1631     SPObject *parent = SP_OBJECT_PARENT(child);
1632     for ( SPObject *i = parent->first_child(); i; i = SP_OBJECT_NEXT(i) ) {
1633         if (SP_OBJECT_NEXT(i) == child)
1634             return i;
1635     }
1636     return NULL;
1639 /* Titles and descriptions */
1641 /* Note:
1642    Titles and descriptions are stored in 'title' and 'desc' child elements
1643    (see section 5.4 of the SVG 1.0 and 1.1 specifications).  The spec allows
1644    an element to have more than one 'title' child element, but strongly
1645    recommends against this and requires using the first one if a choice must
1646    be made.  The same applies to 'desc' elements.  Therefore, these functions
1647    ignore all but the first 'title' child element and first 'desc' child
1648    element, except when deleting a title or description.
1649 */
1651 /**
1652  * Returns the title of this object, or NULL if there is none.
1653  * The caller must free the returned string using g_free() - see comment
1654  * for getTitleOrDesc() below.
1655  */
1656 gchar *
1657 SPObject::title() const
1659     return getTitleOrDesc("svg:title");
1662 /**
1663  * Sets the title of this object
1664  * A NULL first argument is interpreted as meaning that the existing title
1665  * (if any) should be deleted.
1666  * The second argument is optional - see setTitleOrDesc() below for details.
1667  */
1668 bool
1669 SPObject::setTitle(gchar const *title, bool verbatim)
1671     return setTitleOrDesc(title, "svg:title", verbatim);
1674 /**
1675  * Returns the description of this object, or NULL if there is none.
1676  * The caller must free the returned string using g_free() - see comment
1677  * for getTitleOrDesc() below.
1678  */
1679 gchar *
1680 SPObject::desc() const
1682     return getTitleOrDesc("svg:desc");
1685 /**
1686  * Sets the description of this object.
1687  * A NULL first argument is interpreted as meaning that the existing
1688  * description (if any) should be deleted.
1689  * The second argument is optional - see setTitleOrDesc() below for details.
1690  */
1691 bool
1692 SPObject::setDesc(gchar const *desc, bool verbatim)
1694     return setTitleOrDesc(desc, "svg:desc", verbatim);
1697 /**
1698  * Returns the title or description of this object, or NULL if there is none.
1699  *
1700  * The SVG spec allows 'title' and 'desc' elements to contain text marked up
1701  * using elements from other namespaces.  Therefore, this function cannot
1702  * in general just return a pointer to an existing string - it must instead
1703  * construct a string containing the title or description without the mark-up.
1704  * Consequently, the return value is a newly allocated string (or NULL), and
1705  * must be freed (using g_free()) by the caller.
1706  */
1707 gchar *
1708 SPObject::getTitleOrDesc(gchar const *svg_tagname) const
1710     SPObject *elem = findFirstChild(svg_tagname);
1711     if (elem == NULL) return NULL;
1712     return g_string_free(elem->textualContent(), FALSE);
1715 /**
1716  * Sets or deletes the title or description of this object.
1717  * A NULL 'value' argument causes the title or description to be deleted.
1718  *
1719  * 'verbatim' parameter:
1720  * If verbatim==true, then the title or description is set to exactly the
1721  * specified value.  If verbatim==false then two exceptions are made:
1722  *   (1) If the specified value is just whitespace, then the title/description
1723  *       is deleted.
1724  *   (2) If the specified value is the same as the current value except for
1725  *       mark-up, then the current value is left unchanged.
1726  * This is usually the desired behaviour, so 'verbatim' defaults to false for
1727  * setTitle() and setDesc().
1728  *
1729  * The return value is true if a change was made to the title/description,
1730  * and usually false otherwise.
1731  */
1732 bool
1733 SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim)
1735     if (!verbatim) {
1736         // If the new title/description is just whitespace,
1737         // treat it as though it were NULL.
1738         if (value) {
1739             bool just_whitespace = true;
1740             for (const gchar *cp = value; *cp; ++cp) {
1741                 if (!std::strchr("\r\n \t", *cp)) {
1742                     just_whitespace = false;
1743                     break;
1744                 }
1745             }
1746             if (just_whitespace) value = NULL;
1747         }
1748         // Don't stomp on mark-up if there is no real change.
1749         if (value) {
1750             gchar *current_value = getTitleOrDesc(svg_tagname);
1751             if (current_value) {
1752                 bool different = std::strcmp(current_value, value);
1753                 g_free(current_value);
1754                 if (!different) return false;
1755             }
1756         }
1757     }
1759     SPObject *elem = findFirstChild(svg_tagname);
1761     if (value == NULL) {
1762         if (elem == NULL) return false;
1763         // delete the title/description(s)
1764         while (elem) {
1765             elem->deleteObject();
1766             elem = findFirstChild(svg_tagname);
1767         }
1768         return true;
1769     }
1771     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
1773     if (elem == NULL) {
1774         // create a new 'title' or 'desc' element, putting it at the
1775         // beginning (in accordance with the spec's recommendations)
1776         Inkscape::XML::Node *xml_elem = xml_doc->createElement(svg_tagname);
1777         repr->addChild(xml_elem, NULL);
1778         elem = document->getObjectByRepr(xml_elem);
1779         Inkscape::GC::release(xml_elem);
1780     }
1781     else {
1782         // remove the current content of the 'text' or 'desc' element
1783         SPObject *child;
1784         while (NULL != (child = elem->firstChild())) child->deleteObject();
1785     }
1787     // add the new content
1788     elem->appendChildRepr(xml_doc->createTextNode(value));
1789     return true;
1792 /**
1793  * Find the first child of this object with a given tag name,
1794  * and return it.  Returns NULL if there is no matching child.
1795  */
1796 SPObject *
1797 SPObject::findFirstChild(gchar const *tagname) const
1799     for (SPObject *child = children; child; child = child->next)
1800     {
1801         if (child->repr->type() == Inkscape::XML::ELEMENT_NODE &&
1802             !strcmp(child->repr->name(), tagname)) return child;
1803     }
1804     return NULL;
1807 /**
1808  * Return the full textual content of an element (typically all the
1809  * content except the tags).
1810  * Must not be used on anything except elements.
1811  */
1812 GString*
1813 SPObject::textualContent() const
1815     GString* text = g_string_new("");
1817     for (const SPObject *child = firstChild(); child; child = child->next)
1818     {
1819         Inkscape::XML::NodeType child_type = child->repr->type();
1821         if (child_type == Inkscape::XML::ELEMENT_NODE) {
1822             GString * new_text = child->textualContent();
1823             g_string_append(text, new_text->str);
1824             g_string_free(new_text, TRUE);
1825         }
1826         else if (child_type == Inkscape::XML::TEXT_NODE) {
1827             g_string_append(text, child->repr->content());
1828         }
1829     }
1830     return text;
1833 /*
1834   Local Variables:
1835   mode:c++
1836   c-file-style:"stroustrup"
1837   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1838   indent-tabs-mode:nil
1839   fill-column:99
1840   End:
1841 */
1842 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :