Code

Yet mor in c++ification
[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 /**
507  * Retrieves the children as a GSList object, optionally ref'ing the children
508  * in the process, if add_ref is specified.
509  */
510 GSList *SPObject::childList(bool add_ref, Action) {
511     GSList *l = NULL;
512     for (SPObject *child = this->first_child() ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
513         if (add_ref)
514             g_object_ref (G_OBJECT (child));
516         l = g_slist_prepend (l, child);
517     }
518     return l;
522 /** Gets the label property for the object or a default if no label
523  *  is defined.
524  */
525 gchar const *
526 SPObject::label() const {
527     return _label;
530 /** Returns a default label property for the object. */
531 gchar const *
532 SPObject::defaultLabel() const {
533     if (_label) {
534         return _label;
535     } else {
536         if (!_default_label) {
537             gchar const *id=SP_OBJECT_ID(this);
538             if (id) {
539                 _default_label = g_strdup_printf("#%s", id);
540             } else {
541                 _default_label = g_strdup_printf("<%s>", SP_OBJECT_REPR(this)->name());
542             }
543         }
544         return _default_label;
545     }
548 /** Sets the label property for the object */
549 void
550 SPObject::setLabel(gchar const *label) {
551     SP_OBJECT_REPR(this)->setAttribute("inkscape:label", label, false);
555 /** Queues the object for orphan collection */
556 void
557 SPObject::requestOrphanCollection() {
558     g_return_if_fail(document != NULL);
560     // do not remove style or script elements (Bug #276244)
561     if (SP_IS_STYLE_ELEM(this))
562         return;
563     if (SP_IS_SCRIPT(this))
564         return;
566     document->queueForOrphanCollection(this);
568     /** \todo
569      * This is a temporary hack added to make fill&stroke rebuild its
570      * gradient list when the defs are vacuumed.  gradient-vector.cpp
571      * listens to the modified signal on defs, and now we give it that
572      * signal.  Mental says that this should be made automatic by
573      * merging SPObjectGroup with SPObject; SPObjectGroup would issue
574      * this signal automatically. Or maybe just derive SPDefs from
575      * SPObjectGroup?
576      */
578     this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
581 /** Sends the delete signal to all children of this object recursively */
582 void
583 SPObject::_sendDeleteSignalRecursive() {
584     for (SPObject *child = this->first_child(); child; child = SP_OBJECT_NEXT(child)) {
585         child->_delete_signal.emit(child);
586         child->_sendDeleteSignalRecursive();
587     }
590 /**
591  * Deletes the object reference, unparenting it from its parent.
592  *
593  * If the \a propagate parameter is set to true, it emits a delete
594  * signal.  If the \a propagate_descendants parameter is true, it
595  * recursively sends the delete signal to children.
596  */
597 void
598 SPObject::deleteObject(bool propagate, bool propagate_descendants)
600     sp_object_ref(this, NULL);
601     if (propagate) {
602         _delete_signal.emit(this);
603     }
604     if (propagate_descendants) {
605         this->_sendDeleteSignalRecursive();
606     }
608     Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
609     if (repr && sp_repr_parent(repr)) {
610         sp_repr_unparent(repr);
611     }
613     if (_successor) {
614         _successor->deleteObject(propagate, propagate_descendants);
615     }
616     sp_object_unref(this, NULL);
619 /**
620  * Put object into object tree, under parent, and behind prev;
621  * also update object's XML space.
622  */
623 void
624 SPObject::attach(SPObject *object, SPObject *prev)
626     //g_return_if_fail(parent != NULL);
627     //g_return_if_fail(SP_IS_OBJECT(parent));
628     g_return_if_fail(object != NULL);
629     g_return_if_fail(SP_IS_OBJECT(object));
630     g_return_if_fail(!prev || SP_IS_OBJECT(prev));
631     g_return_if_fail(!prev || prev->parent == this);
632     g_return_if_fail(!object->parent);
634     sp_object_ref(object, this);
635     object->parent = this;
636     this->_updateTotalHRefCount(object->_total_hrefcount);
638     SPObject *next;
639     if (prev) {
640         next = prev->next;
641         prev->next = object;
642     } else {
643         next = this->children;
644         this->children = object;
645     }
646     object->next = next;
647     if (!next) {
648         this->_last_child = object;
649     }
650     if (!object->xml_space.set)
651         object->xml_space.value = this->xml_space.value;
654 /**
655  * In list of object's siblings, move object behind prev.
656  */
657 void
658 SPObject::reorder(SPObject *prev) {
659     //g_return_if_fail(object != NULL);
660     //g_return_if_fail(SP_IS_OBJECT(object));
661     g_return_if_fail(this->parent != NULL);
662     g_return_if_fail(this != prev);
663     g_return_if_fail(!prev || SP_IS_OBJECT(prev));
664     g_return_if_fail(!prev || prev->parent == this->parent);
666     SPObject *const parent=this->parent;
668     SPObject *old_prev=NULL;
669     for ( SPObject *child = parent->children ; child && child != this ;
670           child = child->next )
671     {
672         old_prev = child;
673     }
675     SPObject *next=this->next;
676     if (old_prev) {
677         old_prev->next = next;
678     } else {
679         parent->children = next;
680     }
681     if (!next) {
682         parent->_last_child = old_prev;
683     }
684     if (prev) {
685         next = prev->next;
686         prev->next = this;
687     } else {
688         next = parent->children;
689         parent->children = this;
690     }
691     this->next = next;
692     if (!next) {
693         parent->_last_child = this;
694     }
697 /**
698  * Remove object from parent's children, release and unref it.
699  */
700 void
701 SPObject::detach(SPObject *object) {
702     //g_return_if_fail(parent != NULL);
703     //g_return_if_fail(SP_IS_OBJECT(parent));
704     g_return_if_fail(object != NULL);
705     g_return_if_fail(SP_IS_OBJECT(object));
706     g_return_if_fail(object->parent == this);
708     object->releaseReferences();
710     SPObject *prev=NULL;
711     for ( SPObject *child = this->children ; child && child != object ;
712           child = child->next )
713     {
714         prev = child;
715     }
717     SPObject *next=object->next;
718     if (prev) {
719         prev->next = next;
720     } else {
721         this->children = next;
722     }
723     if (!next) {
724         this->_last_child = prev;
725     }
727     object->next = NULL;
728     object->parent = NULL;
730     this->_updateTotalHRefCount(-object->_total_hrefcount);
731     sp_object_unref(object, this);
734 /**
735  * Return object's child whose node pointer equals repr.
736  */
737 SPObject *
738 SPObject::get_child_by_repr(Inkscape::XML::Node *repr)
740     //g_return_val_if_fail(object != NULL, NULL);
741     //g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
742     g_return_val_if_fail(repr != NULL, NULL);
744     if (this->_last_child && SP_OBJECT_REPR(this->_last_child) == repr)
745         return this->_last_child;   // optimization for common scenario
746     for ( SPObject *child = this->children ; child ; child = child->next ) {
747         if ( SP_OBJECT_REPR(child) == repr ) {
748             return child;
749         }
750     }
752     return NULL;
755 /**
756  * Callback for child_added event.
757  * Invoked whenever the given mutation event happens in the XML tree.
758  */
759 void
760 SPObject::sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
762     GType type = sp_repr_type_lookup(child);
763     if (!type) {
764         return;
765     }
766     SPObject *ochild = SP_OBJECT(g_object_new(type, 0));
767     SPObject *prev = ref ? object->get_child_by_repr(ref) : NULL;
768     object->attach(ochild, prev);
769     sp_object_unref(ochild, NULL);
771     ochild->invoke_build(object->document, child, SP_OBJECT_IS_CLONED(object));
774 /**
775  * Removes, releases and unrefs all children of object.
776  *
777  * This is the opposite of build. It has to be invoked as soon as the
778  * object is removed from the tree, even if it is still alive according
779  * to reference count. The frontend unregisters the object from the
780  * document and releases the SPRepr bindings; implementations should free
781  * state data and release all child objects.  Invoking release on
782  * SPRoot destroys the whole document tree.
783  * \see sp_object_build()
784  */
785 void SPObject::sp_object_release(SPObject *object)
787     debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
788     while (object->children) {
789         object->detach(object->children);
790     }
793 /**
794  * Remove object's child whose node equals repr, release and
795  * unref it.
796  *
797  * Invoked whenever the given mutation event happens in the XML
798  * tree, BEFORE removal from the XML tree happens, so grouping
799  * objects can safely release the child data.
800  */
801 void
802 SPObject::sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child)
804     debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
805     SPObject *ochild = object->get_child_by_repr(child);
806     g_return_if_fail (ochild != NULL || !strcmp("comment", child->name())); // comments have no objects
807     if (ochild)
808         object->detach(ochild);
811 /**
812  * Move object corresponding to child after sibling object corresponding
813  * to new_ref.
814  * Invoked whenever the given mutation event happens in the XML tree.
815  * \param old_ref Ignored
816  */
817 void SPObject::sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node */*old_ref*/,
818                                     Inkscape::XML::Node *new_ref)
820     SPObject *ochild = object->get_child_by_repr(child);
821     g_return_if_fail(ochild != NULL);
822     SPObject *prev = new_ref ? object->get_child_by_repr(new_ref) : NULL;
823     ochild->reorder(prev);
824     ochild->_position_changed_signal.emit(ochild);
827 /**
828  * Virtual build callback.
829  *
830  * This has to be invoked immediately after creation of an SPObject. The
831  * frontend method ensures that the new object is properly attached to
832  * the document and repr; implementation then will parse all of the attributes,
833  * generate the children objects and so on.  Invoking build on the SPRoot
834  * object results in creation of the whole document tree (this is, what
835  * SPDocument does after the creation of the XML tree).
836  * \see sp_object_release()
837  */
838 void
839 SPObject::sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
841     /* Nothing specific here */
842     debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
844     object->readAttr("xml:space");
845     object->readAttr("inkscape:label");
846     object->readAttr("inkscape:collect");
848     for (Inkscape::XML::Node *rchild = repr->firstChild() ; rchild != NULL; rchild = rchild->next()) {
849         GType type = sp_repr_type_lookup(rchild);
850         if (!type) {
851             continue;
852         }
853         SPObject *child = SP_OBJECT(g_object_new(type, 0));
854         object->attach(child, object->lastChild());
855         sp_object_unref(child, NULL);
856         child->invoke_build(document, rchild, SP_OBJECT_IS_CLONED(object));
857     }
860 void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
862     debug("id=%x, typename=%s", this, g_type_name_from_instance((GTypeInstance*)this));
864     //g_assert(object != NULL);
865     //g_assert(SP_IS_OBJECT(object));
866     g_assert(document != NULL);
867     g_assert(repr != NULL);
869     g_assert(this->document == NULL);
870     g_assert(this->repr == NULL);
871     g_assert(this->getId() == NULL);
873     /* Bookkeeping */
875     this->document = document;
876     this->repr = repr;
877     if (!cloned)
878         Inkscape::GC::anchor(repr);
879     this->cloned = cloned;
881     if (!SP_OBJECT_IS_CLONED(this)) {
882         this->document->bindObjectToRepr(this->repr, this);
884         if (Inkscape::XML::id_permitted(this->repr)) {
885             /* If we are not cloned, and not seeking, force unique id */
886             gchar const *id = this->repr->attribute("id");
887             if (!document->isSeeking()) {
888                 {
889                     gchar *realid = sp_object_get_unique_id(this, id);
890                     g_assert(realid != NULL);
892                     this->document->bindObjectToId(realid, this);
893                     SPObjectImpl::setId(this, realid);
894                     g_free(realid);
895                 }
897                 /* Redefine ID, if required */
898                 if ((id == NULL) || (strcmp(id, this->getId()) != 0)) {
899                     this->repr->setAttribute("id", this->getId());
900                 }
901             } else if (id) {
902                 // bind if id, but no conflict -- otherwise, we can expect
903                 // a subsequent setting of the id attribute
904                 if (!this->document->getObjectById(id)) {
905                     this->document->bindObjectToId(id, this);
906                     SPObjectImpl::setId(this, id);
907                 }
908             }
909         }
910     } else {
911         g_assert(this->getId() == NULL);
912     }
914     /* Invoke derived methods, if any */
915     if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->build) {
916         (*((SPObjectClass *) G_OBJECT_GET_CLASS(this))->build)(this, document, repr);
917     }
919     /* Signalling (should be connected AFTER processing derived methods */
920     sp_repr_add_listener(repr, &object_event_vector, this);
923 long long int SPObject::getIntAttribute(char const *key, long long int def)
925         return sp_repr_get_int_attribute(getRepr(),key,def);
928 unsigned SPObject::getPosition(){
929         g_assert(this->repr);
931         return repr->position();
934 void SPObject::appendChild(Inkscape::XML::Node *child) {
935         g_assert(this->repr);
937         repr->appendChild(child);
940 void SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node * prev)
942         g_assert(this->repr);
944         repr->addChild(child,prev);
947 void SPObject::releaseReferences() {
948     g_assert(this->document);
949     g_assert(this->repr);
951     sp_repr_remove_listener_by_data(this->repr, this);
953     this->_release_signal.emit(this);
954     SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
955     if (klass->release) {
956         klass->release(this);
957     }
959     /* all hrefs should be released by the "release" handlers */
960     g_assert(this->hrefcount == 0);
962     if (!SP_OBJECT_IS_CLONED(this)) {
963         if (this->id) {
964             this->document->bindObjectToId(this->id, NULL);
965         }
966         g_free(this->id);
967         this->id = NULL;
969         g_free(this->_default_label);
970         this->_default_label = NULL;
972         this->document->bindObjectToRepr(this->repr, NULL);
974         Inkscape::GC::release(this->repr);
975     } else {
976         g_assert(!this->id);
977     }
979     if (this->style) {
980         this->style = sp_style_unref(this->style);
981     }
983     this->document = NULL;
984     this->repr = NULL;
988 SPObject *SPObject::getNext()
990     return next;
993 SPObject *SPObject::getPrev()
995     return this->prev();
998 /**
999  * Callback for child_added node event.
1000  */
1001 void
1002 SPObject::sp_object_repr_child_added(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data)
1004     SPObject *object = SP_OBJECT(data);
1006     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->child_added)
1007         (*((SPObjectClass *)G_OBJECT_GET_CLASS(object))->child_added)(object, child, ref);
1010 /**
1011  * Callback for remove_child node event.
1012  */
1013 void
1014 SPObject::sp_object_repr_child_removed(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))->remove_child) {
1019         (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->remove_child)(object, child);
1020     }
1023 /**
1024  * Callback for order_changed node event.
1025  *
1026  * \todo fixme:
1027  */
1028 void
1029 SPObject::sp_object_repr_order_changed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data)
1031     SPObject *object = SP_OBJECT(data);
1033     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->order_changed) {
1034         (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->order_changed)(object, child, old, newer);
1035     }
1038 /**
1039  * Callback for set event.
1040  */
1041 void
1042 SPObject::sp_object_private_set(SPObject *object, unsigned int key, gchar const *value)
1044     g_assert(key != SP_ATTR_INVALID);
1046     switch (key) {
1047         case SP_ATTR_ID:
1049                         //XML Tree being used here.
1050             if ( !SP_OBJECT_IS_CLONED(object) && object->getRepr()->type() == Inkscape::XML::ELEMENT_NODE ) {
1051                 SPDocument *document=object->document;
1052                 SPObject *conflict=NULL;
1054                 gchar const *new_id = value;
1056                 if (new_id) {
1057                     conflict = document->getObjectById((char const *)new_id);
1058                 }
1060                 if ( conflict && conflict != object ) {
1061                     if (!document->isSeeking()) {
1062                         sp_object_ref(conflict, NULL);
1063                         // give the conflicting object a new ID
1064                         gchar *new_conflict_id = sp_object_get_unique_id(conflict, NULL);
1065                         SP_OBJECT_REPR(conflict)->setAttribute("id", new_conflict_id);
1066                         g_free(new_conflict_id);
1067                         sp_object_unref(conflict, NULL);
1068                     } else {
1069                         new_id = NULL;
1070                     }
1071                 }
1073                 if (object->getId()) {
1074                     document->bindObjectToId(object->getId(), NULL);
1075                     SPObjectImpl::setId(object, 0);
1076                 }
1078                 if (new_id) {
1079                     SPObjectImpl::setId(object, new_id);
1080                     document->bindObjectToId(object->getId(), object);
1081                 }
1083                 g_free(object->_default_label);
1084                 object->_default_label = NULL;
1085             }
1086             break;
1087         case SP_ATTR_INKSCAPE_LABEL:
1088             g_free(object->_label);
1089             if (value) {
1090                 object->_label = g_strdup(value);
1091             } else {
1092                 object->_label = NULL;
1093             }
1094             g_free(object->_default_label);
1095             object->_default_label = NULL;
1096             break;
1097         case SP_ATTR_INKSCAPE_COLLECT:
1098             if ( value && !strcmp(value, "always") ) {
1099                 object->setCollectionPolicy(SPObject::ALWAYS_COLLECT);
1100             } else {
1101                 object->setCollectionPolicy(SPObject::COLLECT_WITH_PARENT);
1102             }
1103             break;
1104         case SP_ATTR_XML_SPACE:
1105             if (value && !strcmp(value, "preserve")) {
1106                 object->xml_space.value = SP_XML_SPACE_PRESERVE;
1107                 object->xml_space.set = TRUE;
1108             } else if (value && !strcmp(value, "default")) {
1109                 object->xml_space.value = SP_XML_SPACE_DEFAULT;
1110                 object->xml_space.set = TRUE;
1111             } else if (object->parent) {
1112                 SPObject *parent;
1113                 parent = object->parent;
1114                 object->xml_space.value = parent->xml_space.value;
1115             }
1116             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1117             break;
1118         case SP_ATTR_STYLE:
1119             sp_style_read_from_object(object->style, object);
1120             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1121             break;
1122         default:
1123             break;
1124     }
1127 /**
1128  * Call virtual set() function of object.
1129  */
1130 void
1131 SPObject::setKeyValue(unsigned int key, gchar const *value)
1133     //g_assert(object != NULL);
1134     //g_assert(SP_IS_OBJECT(object));
1136     if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->set) {
1137         ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->set(this, key, value);
1138     }
1141 /**
1142  * Read value of key attribute from XML node into object.
1143  */
1144 void
1145 SPObject::readAttr(gchar const *key)
1147     //g_assert(object != NULL);
1148     //g_assert(SP_IS_OBJECT(object));
1149     g_assert(key != NULL);
1151         //XML Tree being used here.
1152     g_assert(this->getRepr() != NULL);
1154     unsigned int keyid = sp_attribute_lookup(key);
1155     if (keyid != SP_ATTR_INVALID) {
1156         /* Retrieve the 'key' attribute from the object's XML representation */
1157         gchar const *value = getRepr()->attribute(key);
1159         setKeyValue(keyid, value);
1160     }
1163 /**
1164  * Callback for attr_changed node event.
1165  */
1166 void
1167 SPObject::sp_object_repr_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *key, gchar const */*oldval*/, gchar const */*newval*/, bool is_interactive, gpointer data)
1169     SPObject *object = SP_OBJECT(data);
1171     object->readAttr(key);
1173     // manual changes to extension attributes require the normal
1174     // attributes, which depend on them, to be updated immediately
1175     if (is_interactive) {
1176         object->updateRepr(0);
1177     }
1180 /**
1181  * Callback for content_changed node event.
1182  */
1183 void
1184 SPObject::sp_object_repr_content_changed(Inkscape::XML::Node */*repr*/, gchar const */*oldcontent*/, gchar const */*newcontent*/, gpointer data)
1186     SPObject *object = SP_OBJECT(data);
1188     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)
1189         (*((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)(object);
1192 /**
1193  * Return string representation of space value.
1194  */
1195 static gchar const*
1196 sp_xml_get_space_string(unsigned int space)
1198     switch (space) {
1199         case SP_XML_SPACE_DEFAULT:
1200             return "default";
1201         case SP_XML_SPACE_PRESERVE:
1202             return "preserve";
1203         default:
1204             return NULL;
1205     }
1208 /**
1209  * Callback for write event.
1210  */
1211 Inkscape::XML::Node *
1212 SPObject::sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
1214     if (!repr && (flags & SP_OBJECT_WRITE_BUILD)) {
1215         repr = SP_OBJECT_REPR(object)->duplicate(doc);
1216         if (!( flags & SP_OBJECT_WRITE_EXT )) {
1217             repr->setAttribute("inkscape:collect", NULL);
1218         }
1219     } else {
1220         repr->setAttribute("id", object->getId());
1222         if (object->xml_space.set) {
1223             char const *xml_space;
1224             xml_space = sp_xml_get_space_string(object->xml_space.value);
1225             repr->setAttribute("xml:space", xml_space);
1226         }
1228         if ( flags & SP_OBJECT_WRITE_EXT &&
1229              object->collectionPolicy() == SPObject::ALWAYS_COLLECT )
1230         {
1231             repr->setAttribute("inkscape:collect", "always");
1232         } else {
1233             repr->setAttribute("inkscape:collect", NULL);
1234         }
1236         SPStyle const *const obj_style = SP_OBJECT_STYLE(object);
1237         if (obj_style) {
1238             gchar *s = sp_style_write_string(obj_style, SP_STYLE_FLAG_IFSET);
1239             repr->setAttribute("style", ( *s ? s : NULL ));
1240             g_free(s);
1241         } else {
1242             /** \todo I'm not sure what to do in this case.  Bug #1165868
1243              * suggests that it can arise, but the submitter doesn't know
1244              * how to do so reliably.  The main two options are either
1245              * leave repr's style attribute unchanged, or explicitly clear it.
1246              * Must also consider what to do with property attributes for
1247              * the element; see below.
1248              */
1249             char const *style_str = repr->attribute("style");
1250             if (!style_str) {
1251                 style_str = "NULL";
1252             }
1253             g_warning("Item's style is NULL; repr style attribute is %s", style_str);
1254         }
1256         /** \note We treat object->style as authoritative.  Its effects have
1257          * been written to the style attribute above; any properties that are
1258          * unset we take to be deliberately unset (e.g. so that clones can
1259          * override the property).
1260          *
1261          * Note that the below has an undesirable consequence of changing the
1262          * appearance on renderers that lack CSS support (e.g. SVG tiny);
1263          * possibly we should write property attributes instead of a style
1264          * attribute.
1265          */
1266         sp_style_unset_property_attrs (object);
1267     }
1269     return repr;
1272 /**
1273  * Update this object's XML node with flags value.
1274  */
1275 Inkscape::XML::Node *
1276 SPObject::updateRepr(unsigned int flags) {
1277     if (!SP_OBJECT_IS_CLONED(this)) {
1278         Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
1279         if (repr) {
1280             return updateRepr(repr->document(), repr, flags);
1281         } else {
1282             g_critical("Attempt to update non-existent repr");
1283             return NULL;
1284         }
1285     } else {
1286         /* cloned objects have no repr */
1287         return NULL;
1288     }
1291 /** Used both to create reprs in the original document, and to create
1292  *  reprs in another document (e.g. a temporary document used when
1293  *  saving as "Plain SVG"
1294  */
1295 Inkscape::XML::Node *
1296 SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags) {
1297     g_assert(doc != NULL);
1299     if (SP_OBJECT_IS_CLONED(this)) {
1300         /* cloned objects have no repr */
1301         return NULL;
1302     }
1303     if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write) {
1304         if (!(flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1305             repr = SP_OBJECT_REPR(this);
1306         }
1307         return ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write(this, doc, repr, flags);
1308     } else {
1309         g_warning("Class %s does not implement ::write", G_OBJECT_TYPE_NAME(this));
1310         if (!repr) {
1311             if (flags & SP_OBJECT_WRITE_BUILD) {
1312                 repr = SP_OBJECT_REPR(this)->duplicate(doc);
1313             }
1314             /// \todo FIXME: else probably error (Lauris) */
1315         } else {
1316             repr->mergeFrom(SP_OBJECT_REPR(this), "id");
1317         }
1318         return repr;
1319     }
1322 /* Modification */
1324 /**
1325  * Add \a flags to \a object's as dirtiness flags, and
1326  * recursively add CHILD_MODIFIED flag to
1327  * parent and ancestors (as far up as necessary).
1328  */
1329 void
1330 SPObject::requestDisplayUpdate(unsigned int flags)
1332     g_return_if_fail( this->document != NULL );
1334     if (update_in_progress) {
1335         g_print("WARNING: Requested update while update in progress, counter = %d\n", update_in_progress);
1336     }
1338     /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1339      * SP_OBJECT_CHILD_MODIFIED_FLAG */
1340     g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1341     g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1342     g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1344     bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1346     this->uflags |= flags;
1348     /* If requestModified has already been called on this object or one of its children, then we
1349      * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1350      */
1351     if (already_propagated) {
1352         SPObject *parent = SP_OBJECT_PARENT(this);
1353         if (parent) {
1354             parent->requestDisplayUpdate(SP_OBJECT_CHILD_MODIFIED_FLAG);
1355         } else {
1356             SP_OBJECT_DOCUMENT(this)->request_modified();
1357         }
1358     }
1361 /**
1362  * Update views
1363  */
1364 void
1365 SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
1367     g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1369     update_in_progress ++;
1371 #ifdef SP_OBJECT_DEBUG_CASCADE
1372     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);
1373 #endif
1375     /* Get this flags */
1376     flags |= this->uflags;
1377     /* Copy flags to modified cascade for later processing */
1378     this->mflags |= this->uflags;
1379     /* We have to clear flags here to allow rescheduling update */
1380     this->uflags = 0;
1382     // Merge style if we have good reasons to think that parent style is changed */
1383     /** \todo
1384      * I am not sure whether we should check only propagated
1385      * flag. We are currently assuming that style parsing is
1386      * done immediately. I think this is correct (Lauris).
1387      */
1388     if ((flags & SP_OBJECT_STYLE_MODIFIED_FLAG) && (flags & SP_OBJECT_PARENT_MODIFIED_FLAG)) {
1389         if (this->style && this->parent) {
1390             sp_style_merge_from_parent(this->style, this->parent->style);
1391         }
1392     }
1394     try
1395     {
1396         if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update)
1397             ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);
1398     }
1399     catch(...)
1400     {
1401         /** \todo
1402         * in case of catching an exception we need to inform the user somehow that the document is corrupted
1403         * maybe by implementing an document flag documentOk
1404         * or by a modal error dialog
1405         */
1406         g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
1407     }
1409     update_in_progress --;
1412 /**
1413  * Request modified always bubbles *up* the tree, as opposed to
1414  * request display update, which trickles down and relies on the
1415  * flags set during this pass...
1416  */
1417 void
1418 SPObject::requestModified(unsigned int flags)
1420     g_return_if_fail( this->document != NULL );
1422     /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1423      * SP_OBJECT_CHILD_MODIFIED_FLAG */
1424     g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1425     g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1426     g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1428     bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1430     this->mflags |= flags;
1432     /* If requestModified has already been called on this object or one of its children, then we
1433      * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1434      */
1435     if (already_propagated) {
1436         SPObject *parent=SP_OBJECT_PARENT(this);
1437         if (parent) {
1438             parent->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
1439         } else {
1440             SP_OBJECT_DOCUMENT(this)->request_modified();
1441         }
1442     }
1445 /**
1446  *  Emits the MODIFIED signal with the object's flags.
1447  *  The object's mflags are the original set aside during the update pass for
1448  *  later delivery here.  Once emitModified() is called, those flags don't
1449  *  need to be stored any longer.
1450  */
1451 void
1452 SPObject::emitModified(unsigned int flags)
1454     /* only the MODIFIED_CASCADE flag is legal here */
1455     g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1457 #ifdef SP_OBJECT_DEBUG_CASCADE
1458     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);
1459 #endif
1461     flags |= this->mflags;
1462     /* We have to clear mflags beforehand, as signal handlers may
1463      * make changes and therefore queue new modification notifications
1464      * themselves. */
1465     this->mflags = 0;
1467     g_object_ref(G_OBJECT(this));
1468     SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
1469     if (klass->modified) {
1470         klass->modified(this, flags);
1471     }
1472     _modified_signal.emit(this, flags);
1473     g_object_unref(G_OBJECT(this));
1476 gchar const *
1477 SPObject::getTagName(SPException *ex) const
1479         g_assert(repr != NULL);
1480     /* If exception is not clear, return */
1481     if (!SP_EXCEPTION_IS_OK(ex)) {
1482         return NULL;
1483     }
1485     /// \todo fixme: Exception if object is NULL? */
1486         //XML Tree being used here.
1487     return getRepr()->name();
1490 gchar const *
1491 SPObject::getAttribute(gchar const *key, SPException *ex) const
1493         g_assert(this->repr != NULL);
1494     /* If exception is not clear, return */
1495     if (!SP_EXCEPTION_IS_OK(ex)) {
1496         return NULL;
1497     }
1499     /// \todo fixme: Exception if object is NULL? */
1500         //XML Tree being used here.
1501     return (gchar const *) getRepr()->attribute(key);
1504 void
1505 SPObject::setAttribute(gchar const *key, gchar const *value, SPException *ex)
1507         g_assert(this->repr != NULL);
1508     /* If exception is not clear, return */
1509     g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1511     /// \todo fixme: Exception if object is NULL? */
1512         //XML Tree being used here.
1513     getRepr()->setAttribute(key, value, false);
1516 void
1517 SPObject::removeAttribute(gchar const *key, SPException *ex)
1519     /* If exception is not clear, return */
1520     g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1522     /// \todo fixme: Exception if object is NULL? */
1523         //XML Tree being used here.
1524     getRepr()->setAttribute(key, NULL, false);
1527 /* Helper */
1529 gchar *
1530 SPObject::sp_object_get_unique_id(SPObject *object, gchar const *id)
1532     static unsigned long count = 0;
1534     g_assert(SP_IS_OBJECT(object));
1536     count++;
1538         //XML Tree being used here.
1539     gchar const *name = object->getRepr()->name();
1540     g_assert(name != NULL);
1542     gchar const *local = strchr(name, ':');
1543     if (local) {
1544         name = local + 1;
1545     }
1547     if (id != NULL) {
1548         if (object->document->getObjectById(id) == NULL) {
1549             return g_strdup(id);
1550         }
1551     }
1553     size_t const name_len = strlen(name);
1554     size_t const buflen = name_len + (sizeof(count) * 10 / 4) + 1;
1555     gchar *const buf = (gchar *) g_malloc(buflen);
1556     memcpy(buf, name, name_len);
1557     gchar *const count_buf = buf + name_len;
1558     size_t const count_buflen = buflen - name_len;
1559     do {
1560         ++count;
1561         g_snprintf(count_buf, count_buflen, "%lu", count);
1562     } while ( object->document->getObjectById(buf) != NULL );
1563     return buf;
1566 /* Style */
1568 /**
1569  * Returns an object style property.
1570  *
1571  * \todo
1572  * fixme: Use proper CSS parsing.  The current version is buggy
1573  * in a number of situations where key is a substring of the
1574  * style string other than as a property name (including
1575  * where key is a substring of a property name), and is also
1576  * buggy in its handling of inheritance for properties that
1577  * aren't inherited by default.  It also doesn't allow for
1578  * the case where the property is specified but with an invalid
1579  * value (in which case I believe the CSS2 error-handling
1580  * behaviour applies, viz. behave as if the property hadn't
1581  * been specified).  Also, the current code doesn't use CRSelEng
1582  * stuff to take a value from stylesheets.  Also, we aren't
1583  * setting any hooks to force an update for changes in any of
1584  * the inputs (i.e., in any of the elements that this function
1585  * queries).
1586  *
1587  * \par
1588  * Given that the default value for a property depends on what
1589  * property it is (e.g., whether to inherit or not), and given
1590  * the above comment about ignoring invalid values, and that the
1591  * repr parent isn't necessarily the right element to inherit
1592  * from (e.g., maybe we need to inherit from the referencing
1593  * <use> element instead), we should probably make the caller
1594  * responsible for ascending the repr tree as necessary.
1595  */
1596 gchar const *
1597 SPObject::getStyleProperty(gchar const *key, gchar const *def) const
1599     //g_return_val_if_fail(object != NULL, NULL);
1600     //g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
1601     g_return_val_if_fail(key != NULL, NULL);
1603         //XML Tree being used here.
1604     gchar const *style = getRepr()->attribute("style");
1605     if (style) {
1606         size_t const len = strlen(key);
1607         char const *p;
1608         while ( (p = strstr(style, key))
1609                 != NULL )
1610         {
1611             p += len;
1612             while ((*p <= ' ') && *p) p++;
1613             if (*p++ != ':') break;
1614             while ((*p <= ' ') && *p) p++;
1615             size_t const inherit_len = sizeof("inherit") - 1;
1616             if (*p
1617                 && !(strneq(p, "inherit", inherit_len)
1618                      && (p[inherit_len] == '\0'
1619                          || p[inherit_len] == ';'
1620                          || g_ascii_isspace(p[inherit_len])))) {
1621                 return p;
1622             }
1623         }
1624     }
1626         //XML Tree being used here.
1627     gchar const *val = getRepr()->attribute(key);
1628     if (val && !streq(val, "inherit")) {
1629         return val;
1630     }
1631     if (this->parent) {
1632         return (this->parent)->getStyleProperty(key, def);
1633     }
1635     return def;
1638 /**
1639  * Lifts SVG version of all root objects to version.
1640  */
1641 void
1642 SPObject::_requireSVGVersion(Inkscape::Version version) {
1643     for ( SPObject::ParentIterator iter=this ; iter ; ++iter ) {
1644         SPObject *object=iter;
1645         if (SP_IS_ROOT(object)) {
1646             SPRoot *root=SP_ROOT(object);
1647             if ( root->version.svg < version ) {
1648                 root->version.svg = version;
1649             }
1650         }
1651     }
1654 /**
1655  * Returns previous object in sibling list or NULL.
1656  */
1657 SPObject *
1658 SPObject::prev()
1660     SPObject *parent = SP_OBJECT_PARENT(this);
1661     for ( SPObject *i = parent->first_child(); i; i = SP_OBJECT_NEXT(i) ) {
1662         if (SP_OBJECT_NEXT(i) == this)
1663             return i;
1664     }
1665     return NULL;
1668 /* Titles and descriptions */
1670 /* Note:
1671    Titles and descriptions are stored in 'title' and 'desc' child elements
1672    (see section 5.4 of the SVG 1.0 and 1.1 specifications).  The spec allows
1673    an element to have more than one 'title' child element, but strongly
1674    recommends against this and requires using the first one if a choice must
1675    be made.  The same applies to 'desc' elements.  Therefore, these functions
1676    ignore all but the first 'title' child element and first 'desc' child
1677    element, except when deleting a title or description.
1678 */
1680 /**
1681  * Returns the title of this object, or NULL if there is none.
1682  * The caller must free the returned string using g_free() - see comment
1683  * for getTitleOrDesc() below.
1684  */
1685 gchar *
1686 SPObject::title() const
1688     return getTitleOrDesc("svg:title");
1691 /**
1692  * Sets the title of this object
1693  * A NULL first argument is interpreted as meaning that the existing title
1694  * (if any) should be deleted.
1695  * The second argument is optional - see setTitleOrDesc() below for details.
1696  */
1697 bool
1698 SPObject::setTitle(gchar const *title, bool verbatim)
1700     return setTitleOrDesc(title, "svg:title", verbatim);
1703 /**
1704  * Returns the description of this object, or NULL if there is none.
1705  * The caller must free the returned string using g_free() - see comment
1706  * for getTitleOrDesc() below.
1707  */
1708 gchar *
1709 SPObject::desc() const
1711     return getTitleOrDesc("svg:desc");
1714 /**
1715  * Sets the description of this object.
1716  * A NULL first argument is interpreted as meaning that the existing
1717  * description (if any) should be deleted.
1718  * The second argument is optional - see setTitleOrDesc() below for details.
1719  */
1720 bool
1721 SPObject::setDesc(gchar const *desc, bool verbatim)
1723     return setTitleOrDesc(desc, "svg:desc", verbatim);
1726 /**
1727  * Returns the title or description of this object, or NULL if there is none.
1728  *
1729  * The SVG spec allows 'title' and 'desc' elements to contain text marked up
1730  * using elements from other namespaces.  Therefore, this function cannot
1731  * in general just return a pointer to an existing string - it must instead
1732  * construct a string containing the title or description without the mark-up.
1733  * Consequently, the return value is a newly allocated string (or NULL), and
1734  * must be freed (using g_free()) by the caller.
1735  */
1736 gchar *
1737 SPObject::getTitleOrDesc(gchar const *svg_tagname) const
1739     SPObject *elem = findFirstChild(svg_tagname);
1740     if (elem == NULL) return NULL;
1741     return g_string_free(elem->textualContent(), FALSE);
1744 /**
1745  * Sets or deletes the title or description of this object.
1746  * A NULL 'value' argument causes the title or description to be deleted.
1747  *
1748  * 'verbatim' parameter:
1749  * If verbatim==true, then the title or description is set to exactly the
1750  * specified value.  If verbatim==false then two exceptions are made:
1751  *   (1) If the specified value is just whitespace, then the title/description
1752  *       is deleted.
1753  *   (2) If the specified value is the same as the current value except for
1754  *       mark-up, then the current value is left unchanged.
1755  * This is usually the desired behaviour, so 'verbatim' defaults to false for
1756  * setTitle() and setDesc().
1757  *
1758  * The return value is true if a change was made to the title/description,
1759  * and usually false otherwise.
1760  */
1761 bool
1762 SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim)
1764     if (!verbatim) {
1765         // If the new title/description is just whitespace,
1766         // treat it as though it were NULL.
1767         if (value) {
1768             bool just_whitespace = true;
1769             for (const gchar *cp = value; *cp; ++cp) {
1770                 if (!std::strchr("\r\n \t", *cp)) {
1771                     just_whitespace = false;
1772                     break;
1773                 }
1774             }
1775             if (just_whitespace) value = NULL;
1776         }
1777         // Don't stomp on mark-up if there is no real change.
1778         if (value) {
1779             gchar *current_value = getTitleOrDesc(svg_tagname);
1780             if (current_value) {
1781                 bool different = std::strcmp(current_value, value);
1782                 g_free(current_value);
1783                 if (!different) return false;
1784             }
1785         }
1786     }
1788     SPObject *elem = findFirstChild(svg_tagname);
1790     if (value == NULL) {
1791         if (elem == NULL) return false;
1792         // delete the title/description(s)
1793         while (elem) {
1794             elem->deleteObject();
1795             elem = findFirstChild(svg_tagname);
1796         }
1797         return true;
1798     }
1800     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
1802     if (elem == NULL) {
1803         // create a new 'title' or 'desc' element, putting it at the
1804         // beginning (in accordance with the spec's recommendations)
1805         Inkscape::XML::Node *xml_elem = xml_doc->createElement(svg_tagname);
1806         repr->addChild(xml_elem, NULL);
1807         elem = document->getObjectByRepr(xml_elem);
1808         Inkscape::GC::release(xml_elem);
1809     }
1810     else {
1811         // remove the current content of the 'text' or 'desc' element
1812         SPObject *child;
1813         while (NULL != (child = elem->firstChild())) child->deleteObject();
1814     }
1816     // add the new content
1817     elem->appendChildRepr(xml_doc->createTextNode(value));
1818     return true;
1821 /**
1822  * Find the first child of this object with a given tag name,
1823  * and return it.  Returns NULL if there is no matching child.
1824  */
1825 SPObject *
1826 SPObject::findFirstChild(gchar const *tagname) const
1828     for (SPObject *child = children; child; child = child->next)
1829     {
1830         if (child->repr->type() == Inkscape::XML::ELEMENT_NODE &&
1831             !strcmp(child->repr->name(), tagname)) return child;
1832     }
1833     return NULL;
1836 /**
1837  * Return the full textual content of an element (typically all the
1838  * content except the tags).
1839  * Must not be used on anything except elements.
1840  */
1841 GString*
1842 SPObject::textualContent() const
1844     GString* text = g_string_new("");
1846     for (const SPObject *child = firstChild(); child; child = child->next)
1847     {
1848         Inkscape::XML::NodeType child_type = child->repr->type();
1850         if (child_type == Inkscape::XML::ELEMENT_NODE) {
1851             GString * new_text = child->textualContent();
1852             g_string_append(text, new_text->str);
1853             g_string_free(new_text, TRUE);
1854         }
1855         else if (child_type == Inkscape::XML::TEXT_NODE) {
1856             g_string_append(text, child->repr->content());
1857         }
1858     }
1859     return text;
1862 /*
1863   Local Variables:
1864   mode:c++
1865   c-file-style:"stroustrup"
1866   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1867   indent-tabs-mode:nil
1868   fill-column:99
1869   End:
1870 */
1871 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :