Code

SPObject c++ification finalized along with the beginning of XML Privatisation tweaks
[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::releaseReferences() {
941     g_assert(this->document);
942     g_assert(this->repr);
944     sp_repr_remove_listener_by_data(this->repr, this);
946     this->_release_signal.emit(this);
947     SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
948     if (klass->release) {
949         klass->release(this);
950     }
952     /* all hrefs should be released by the "release" handlers */
953     g_assert(this->hrefcount == 0);
955     if (!SP_OBJECT_IS_CLONED(this)) {
956         if (this->id) {
957             this->document->bindObjectToId(this->id, NULL);
958         }
959         g_free(this->id);
960         this->id = NULL;
962         g_free(this->_default_label);
963         this->_default_label = NULL;
965         this->document->bindObjectToRepr(this->repr, NULL);
967         Inkscape::GC::release(this->repr);
968     } else {
969         g_assert(!this->id);
970     }
972     if (this->style) {
973         this->style = sp_style_unref(this->style);
974     }
976     this->document = NULL;
977     this->repr = NULL;
981 SPObject *SPObject::getNext()
983     return next;
986 SPObject *SPObject::getPrev()
988     return this->prev();
991 /**
992  * Callback for child_added node event.
993  */
994 void
995 SPObject::sp_object_repr_child_added(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data)
997     SPObject *object = SP_OBJECT(data);
999     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->child_added)
1000         (*((SPObjectClass *)G_OBJECT_GET_CLASS(object))->child_added)(object, child, ref);
1003 /**
1004  * Callback for remove_child node event.
1005  */
1006 void
1007 SPObject::sp_object_repr_child_removed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node */*ref*/, gpointer data)
1009     SPObject *object = SP_OBJECT(data);
1011     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->remove_child) {
1012         (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->remove_child)(object, child);
1013     }
1016 /**
1017  * Callback for order_changed node event.
1018  *
1019  * \todo fixme:
1020  */
1021 void
1022 SPObject::sp_object_repr_order_changed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data)
1024     SPObject *object = SP_OBJECT(data);
1026     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->order_changed) {
1027         (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->order_changed)(object, child, old, newer);
1028     }
1031 /**
1032  * Callback for set event.
1033  */
1034 void
1035 SPObject::sp_object_private_set(SPObject *object, unsigned int key, gchar const *value)
1037     g_assert(key != SP_ATTR_INVALID);
1039     switch (key) {
1040         case SP_ATTR_ID:
1042                         //XML Tree being used here.
1043             if ( !SP_OBJECT_IS_CLONED(object) && object->getRepr()->type() == Inkscape::XML::ELEMENT_NODE ) {
1044                 SPDocument *document=object->document;
1045                 SPObject *conflict=NULL;
1047                 gchar const *new_id = value;
1049                 if (new_id) {
1050                     conflict = document->getObjectById((char const *)new_id);
1051                 }
1053                 if ( conflict && conflict != object ) {
1054                     if (!document->isSeeking()) {
1055                         sp_object_ref(conflict, NULL);
1056                         // give the conflicting object a new ID
1057                         gchar *new_conflict_id = sp_object_get_unique_id(conflict, NULL);
1058                         SP_OBJECT_REPR(conflict)->setAttribute("id", new_conflict_id);
1059                         g_free(new_conflict_id);
1060                         sp_object_unref(conflict, NULL);
1061                     } else {
1062                         new_id = NULL;
1063                     }
1064                 }
1066                 if (object->getId()) {
1067                     document->bindObjectToId(object->getId(), NULL);
1068                     SPObjectImpl::setId(object, 0);
1069                 }
1071                 if (new_id) {
1072                     SPObjectImpl::setId(object, new_id);
1073                     document->bindObjectToId(object->getId(), object);
1074                 }
1076                 g_free(object->_default_label);
1077                 object->_default_label = NULL;
1078             }
1079             break;
1080         case SP_ATTR_INKSCAPE_LABEL:
1081             g_free(object->_label);
1082             if (value) {
1083                 object->_label = g_strdup(value);
1084             } else {
1085                 object->_label = NULL;
1086             }
1087             g_free(object->_default_label);
1088             object->_default_label = NULL;
1089             break;
1090         case SP_ATTR_INKSCAPE_COLLECT:
1091             if ( value && !strcmp(value, "always") ) {
1092                 object->setCollectionPolicy(SPObject::ALWAYS_COLLECT);
1093             } else {
1094                 object->setCollectionPolicy(SPObject::COLLECT_WITH_PARENT);
1095             }
1096             break;
1097         case SP_ATTR_XML_SPACE:
1098             if (value && !strcmp(value, "preserve")) {
1099                 object->xml_space.value = SP_XML_SPACE_PRESERVE;
1100                 object->xml_space.set = TRUE;
1101             } else if (value && !strcmp(value, "default")) {
1102                 object->xml_space.value = SP_XML_SPACE_DEFAULT;
1103                 object->xml_space.set = TRUE;
1104             } else if (object->parent) {
1105                 SPObject *parent;
1106                 parent = object->parent;
1107                 object->xml_space.value = parent->xml_space.value;
1108             }
1109             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1110             break;
1111         case SP_ATTR_STYLE:
1112             sp_style_read_from_object(object->style, object);
1113             object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1114             break;
1115         default:
1116             break;
1117     }
1120 /**
1121  * Call virtual set() function of object.
1122  */
1123 void
1124 SPObject::setKeyValue(unsigned int key, gchar const *value)
1126     //g_assert(object != NULL);
1127     //g_assert(SP_IS_OBJECT(object));
1129     if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->set) {
1130         ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->set(this, key, value);
1131     }
1134 /**
1135  * Read value of key attribute from XML node into object.
1136  */
1137 void
1138 SPObject::readAttr(gchar const *key)
1140     //g_assert(object != NULL);
1141     //g_assert(SP_IS_OBJECT(object));
1142     g_assert(key != NULL);
1144         //XML Tree being used here.
1145     g_assert(this->getRepr() != NULL);
1147     unsigned int keyid = sp_attribute_lookup(key);
1148     if (keyid != SP_ATTR_INVALID) {
1149         /* Retrieve the 'key' attribute from the object's XML representation */
1150         gchar const *value = getRepr()->attribute(key);
1152         setKeyValue(keyid, value);
1153     }
1156 /**
1157  * Callback for attr_changed node event.
1158  */
1159 void
1160 SPObject::sp_object_repr_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *key, gchar const */*oldval*/, gchar const */*newval*/, bool is_interactive, gpointer data)
1162     SPObject *object = SP_OBJECT(data);
1164     object->readAttr(key);
1166     // manual changes to extension attributes require the normal
1167     // attributes, which depend on them, to be updated immediately
1168     if (is_interactive) {
1169         object->updateRepr(0);
1170     }
1173 /**
1174  * Callback for content_changed node event.
1175  */
1176 void
1177 SPObject::sp_object_repr_content_changed(Inkscape::XML::Node */*repr*/, gchar const */*oldcontent*/, gchar const */*newcontent*/, gpointer data)
1179     SPObject *object = SP_OBJECT(data);
1181     if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)
1182         (*((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)(object);
1185 /**
1186  * Return string representation of space value.
1187  */
1188 static gchar const*
1189 sp_xml_get_space_string(unsigned int space)
1191     switch (space) {
1192         case SP_XML_SPACE_DEFAULT:
1193             return "default";
1194         case SP_XML_SPACE_PRESERVE:
1195             return "preserve";
1196         default:
1197             return NULL;
1198     }
1201 /**
1202  * Callback for write event.
1203  */
1204 Inkscape::XML::Node *
1205 SPObject::sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
1207     if (!repr && (flags & SP_OBJECT_WRITE_BUILD)) {
1208         repr = SP_OBJECT_REPR(object)->duplicate(doc);
1209         if (!( flags & SP_OBJECT_WRITE_EXT )) {
1210             repr->setAttribute("inkscape:collect", NULL);
1211         }
1212     } else {
1213         repr->setAttribute("id", object->getId());
1215         if (object->xml_space.set) {
1216             char const *xml_space;
1217             xml_space = sp_xml_get_space_string(object->xml_space.value);
1218             repr->setAttribute("xml:space", xml_space);
1219         }
1221         if ( flags & SP_OBJECT_WRITE_EXT &&
1222              object->collectionPolicy() == SPObject::ALWAYS_COLLECT )
1223         {
1224             repr->setAttribute("inkscape:collect", "always");
1225         } else {
1226             repr->setAttribute("inkscape:collect", NULL);
1227         }
1229         SPStyle const *const obj_style = SP_OBJECT_STYLE(object);
1230         if (obj_style) {
1231             gchar *s = sp_style_write_string(obj_style, SP_STYLE_FLAG_IFSET);
1232             repr->setAttribute("style", ( *s ? s : NULL ));
1233             g_free(s);
1234         } else {
1235             /** \todo I'm not sure what to do in this case.  Bug #1165868
1236              * suggests that it can arise, but the submitter doesn't know
1237              * how to do so reliably.  The main two options are either
1238              * leave repr's style attribute unchanged, or explicitly clear it.
1239              * Must also consider what to do with property attributes for
1240              * the element; see below.
1241              */
1242             char const *style_str = repr->attribute("style");
1243             if (!style_str) {
1244                 style_str = "NULL";
1245             }
1246             g_warning("Item's style is NULL; repr style attribute is %s", style_str);
1247         }
1249         /** \note We treat object->style as authoritative.  Its effects have
1250          * been written to the style attribute above; any properties that are
1251          * unset we take to be deliberately unset (e.g. so that clones can
1252          * override the property).
1253          *
1254          * Note that the below has an undesirable consequence of changing the
1255          * appearance on renderers that lack CSS support (e.g. SVG tiny);
1256          * possibly we should write property attributes instead of a style
1257          * attribute.
1258          */
1259         sp_style_unset_property_attrs (object);
1260     }
1262     return repr;
1265 /**
1266  * Update this object's XML node with flags value.
1267  */
1268 Inkscape::XML::Node *
1269 SPObject::updateRepr(unsigned int flags) {
1270     if (!SP_OBJECT_IS_CLONED(this)) {
1271         Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
1272         if (repr) {
1273             return updateRepr(repr->document(), repr, flags);
1274         } else {
1275             g_critical("Attempt to update non-existent repr");
1276             return NULL;
1277         }
1278     } else {
1279         /* cloned objects have no repr */
1280         return NULL;
1281     }
1284 /** Used both to create reprs in the original document, and to create
1285  *  reprs in another document (e.g. a temporary document used when
1286  *  saving as "Plain SVG"
1287  */
1288 Inkscape::XML::Node *
1289 SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags) {
1290     g_assert(doc != NULL);
1292     if (SP_OBJECT_IS_CLONED(this)) {
1293         /* cloned objects have no repr */
1294         return NULL;
1295     }
1296     if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write) {
1297         if (!(flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1298             repr = SP_OBJECT_REPR(this);
1299         }
1300         return ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write(this, doc, repr, flags);
1301     } else {
1302         g_warning("Class %s does not implement ::write", G_OBJECT_TYPE_NAME(this));
1303         if (!repr) {
1304             if (flags & SP_OBJECT_WRITE_BUILD) {
1305                 repr = SP_OBJECT_REPR(this)->duplicate(doc);
1306             }
1307             /// \todo FIXME: else probably error (Lauris) */
1308         } else {
1309             repr->mergeFrom(SP_OBJECT_REPR(this), "id");
1310         }
1311         return repr;
1312     }
1315 /* Modification */
1317 /**
1318  * Add \a flags to \a object's as dirtiness flags, and
1319  * recursively add CHILD_MODIFIED flag to
1320  * parent and ancestors (as far up as necessary).
1321  */
1322 void
1323 SPObject::requestDisplayUpdate(unsigned int flags)
1325     g_return_if_fail( this->document != NULL );
1327     if (update_in_progress) {
1328         g_print("WARNING: Requested update while update in progress, counter = %d\n", update_in_progress);
1329     }
1331     /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1332      * SP_OBJECT_CHILD_MODIFIED_FLAG */
1333     g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1334     g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1335     g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1337     bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1339     this->uflags |= flags;
1341     /* If requestModified has already been called on this object or one of its children, then we
1342      * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1343      */
1344     if (already_propagated) {
1345         SPObject *parent = SP_OBJECT_PARENT(this);
1346         if (parent) {
1347             parent->requestDisplayUpdate(SP_OBJECT_CHILD_MODIFIED_FLAG);
1348         } else {
1349             SP_OBJECT_DOCUMENT(this)->request_modified();
1350         }
1351     }
1354 /**
1355  * Update views
1356  */
1357 void
1358 SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
1360     g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1362     update_in_progress ++;
1364 #ifdef SP_OBJECT_DEBUG_CASCADE
1365     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);
1366 #endif
1368     /* Get this flags */
1369     flags |= this->uflags;
1370     /* Copy flags to modified cascade for later processing */
1371     this->mflags |= this->uflags;
1372     /* We have to clear flags here to allow rescheduling update */
1373     this->uflags = 0;
1375     // Merge style if we have good reasons to think that parent style is changed */
1376     /** \todo
1377      * I am not sure whether we should check only propagated
1378      * flag. We are currently assuming that style parsing is
1379      * done immediately. I think this is correct (Lauris).
1380      */
1381     if ((flags & SP_OBJECT_STYLE_MODIFIED_FLAG) && (flags & SP_OBJECT_PARENT_MODIFIED_FLAG)) {
1382         if (this->style && this->parent) {
1383             sp_style_merge_from_parent(this->style, this->parent->style);
1384         }
1385     }
1387     try
1388     {
1389         if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update)
1390             ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);
1391     }
1392     catch(...)
1393     {
1394         /** \todo
1395         * in case of catching an exception we need to inform the user somehow that the document is corrupted
1396         * maybe by implementing an document flag documentOk
1397         * or by a modal error dialog
1398         */
1399         g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
1400     }
1402     update_in_progress --;
1405 /**
1406  * Request modified always bubbles *up* the tree, as opposed to
1407  * request display update, which trickles down and relies on the
1408  * flags set during this pass...
1409  */
1410 void
1411 SPObject::requestModified(unsigned int flags)
1413     g_return_if_fail( this->document != NULL );
1415     /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1416      * SP_OBJECT_CHILD_MODIFIED_FLAG */
1417     g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1418     g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1419     g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1421     bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1423     this->mflags |= flags;
1425     /* If requestModified has already been called on this object or one of its children, then we
1426      * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1427      */
1428     if (already_propagated) {
1429         SPObject *parent=SP_OBJECT_PARENT(this);
1430         if (parent) {
1431             parent->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
1432         } else {
1433             SP_OBJECT_DOCUMENT(this)->request_modified();
1434         }
1435     }
1438 /**
1439  *  Emits the MODIFIED signal with the object's flags.
1440  *  The object's mflags are the original set aside during the update pass for
1441  *  later delivery here.  Once emitModified() is called, those flags don't
1442  *  need to be stored any longer.
1443  */
1444 void
1445 SPObject::emitModified(unsigned int flags)
1447     /* only the MODIFIED_CASCADE flag is legal here */
1448     g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1450 #ifdef SP_OBJECT_DEBUG_CASCADE
1451     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);
1452 #endif
1454     flags |= this->mflags;
1455     /* We have to clear mflags beforehand, as signal handlers may
1456      * make changes and therefore queue new modification notifications
1457      * themselves. */
1458     this->mflags = 0;
1460     g_object_ref(G_OBJECT(this));
1461     SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
1462     if (klass->modified) {
1463         klass->modified(this, flags);
1464     }
1465     _modified_signal.emit(this, flags);
1466     g_object_unref(G_OBJECT(this));
1469 gchar const *
1470 SPObject::getTagName(SPException *ex) const
1472         g_assert(repr != NULL);
1473     /* If exception is not clear, return */
1474     if (!SP_EXCEPTION_IS_OK(ex)) {
1475         return NULL;
1476     }
1478     /// \todo fixme: Exception if object is NULL? */
1479         //XML Tree being used here.
1480     return getRepr()->name();
1483 gchar const *
1484 SPObject::getAttribute(gchar const *key, SPException *ex) const
1486         g_assert(this->repr != NULL);
1487     /* If exception is not clear, return */
1488     if (!SP_EXCEPTION_IS_OK(ex)) {
1489         return NULL;
1490     }
1492     /// \todo fixme: Exception if object is NULL? */
1493         //XML Tree being used here.
1494     return (gchar const *) getRepr()->attribute(key);
1497 void
1498 SPObject::setAttribute(gchar const *key, gchar const *value, SPException *ex)
1500         g_assert(this->repr != NULL);
1501     /* If exception is not clear, return */
1502     g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1504     /// \todo fixme: Exception if object is NULL? */
1505         //XML Tree being used here.
1506     getRepr()->setAttribute(key, value, false);
1509 void
1510 SPObject::removeAttribute(gchar const *key, SPException *ex)
1512     /* If exception is not clear, return */
1513     g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1515     /// \todo fixme: Exception if object is NULL? */
1516         //XML Tree being used here.
1517     getRepr()->setAttribute(key, NULL, false);
1520 /* Helper */
1522 gchar *
1523 SPObject::sp_object_get_unique_id(SPObject *object, gchar const *id)
1525     static unsigned long count = 0;
1527     g_assert(SP_IS_OBJECT(object));
1529     count++;
1531         //XML Tree being used here.
1532     gchar const *name = object->getRepr()->name();
1533     g_assert(name != NULL);
1535     gchar const *local = strchr(name, ':');
1536     if (local) {
1537         name = local + 1;
1538     }
1540     if (id != NULL) {
1541         if (object->document->getObjectById(id) == NULL) {
1542             return g_strdup(id);
1543         }
1544     }
1546     size_t const name_len = strlen(name);
1547     size_t const buflen = name_len + (sizeof(count) * 10 / 4) + 1;
1548     gchar *const buf = (gchar *) g_malloc(buflen);
1549     memcpy(buf, name, name_len);
1550     gchar *const count_buf = buf + name_len;
1551     size_t const count_buflen = buflen - name_len;
1552     do {
1553         ++count;
1554         g_snprintf(count_buf, count_buflen, "%lu", count);
1555     } while ( object->document->getObjectById(buf) != NULL );
1556     return buf;
1559 /* Style */
1561 /**
1562  * Returns an object style property.
1563  *
1564  * \todo
1565  * fixme: Use proper CSS parsing.  The current version is buggy
1566  * in a number of situations where key is a substring of the
1567  * style string other than as a property name (including
1568  * where key is a substring of a property name), and is also
1569  * buggy in its handling of inheritance for properties that
1570  * aren't inherited by default.  It also doesn't allow for
1571  * the case where the property is specified but with an invalid
1572  * value (in which case I believe the CSS2 error-handling
1573  * behaviour applies, viz. behave as if the property hadn't
1574  * been specified).  Also, the current code doesn't use CRSelEng
1575  * stuff to take a value from stylesheets.  Also, we aren't
1576  * setting any hooks to force an update for changes in any of
1577  * the inputs (i.e., in any of the elements that this function
1578  * queries).
1579  *
1580  * \par
1581  * Given that the default value for a property depends on what
1582  * property it is (e.g., whether to inherit or not), and given
1583  * the above comment about ignoring invalid values, and that the
1584  * repr parent isn't necessarily the right element to inherit
1585  * from (e.g., maybe we need to inherit from the referencing
1586  * <use> element instead), we should probably make the caller
1587  * responsible for ascending the repr tree as necessary.
1588  */
1589 gchar const *
1590 SPObject::getStyleProperty(gchar const *key, gchar const *def) const
1592     //g_return_val_if_fail(object != NULL, NULL);
1593     //g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
1594     g_return_val_if_fail(key != NULL, NULL);
1596         //XML Tree being used here.
1597     gchar const *style = getRepr()->attribute("style");
1598     if (style) {
1599         size_t const len = strlen(key);
1600         char const *p;
1601         while ( (p = strstr(style, key))
1602                 != NULL )
1603         {
1604             p += len;
1605             while ((*p <= ' ') && *p) p++;
1606             if (*p++ != ':') break;
1607             while ((*p <= ' ') && *p) p++;
1608             size_t const inherit_len = sizeof("inherit") - 1;
1609             if (*p
1610                 && !(strneq(p, "inherit", inherit_len)
1611                      && (p[inherit_len] == '\0'
1612                          || p[inherit_len] == ';'
1613                          || g_ascii_isspace(p[inherit_len])))) {
1614                 return p;
1615             }
1616         }
1617     }
1619         //XML Tree being used here.
1620     gchar const *val = getRepr()->attribute(key);
1621     if (val && !streq(val, "inherit")) {
1622         return val;
1623     }
1624     if (this->parent) {
1625         return (this->parent)->getStyleProperty(key, def);
1626     }
1628     return def;
1631 /**
1632  * Lifts SVG version of all root objects to version.
1633  */
1634 void
1635 SPObject::_requireSVGVersion(Inkscape::Version version) {
1636     for ( SPObject::ParentIterator iter=this ; iter ; ++iter ) {
1637         SPObject *object=iter;
1638         if (SP_IS_ROOT(object)) {
1639             SPRoot *root=SP_ROOT(object);
1640             if ( root->version.svg < version ) {
1641                 root->version.svg = version;
1642             }
1643         }
1644     }
1647 /**
1648  * Returns previous object in sibling list or NULL.
1649  */
1650 SPObject *
1651 SPObject::prev()
1653     SPObject *parent = SP_OBJECT_PARENT(this);
1654     for ( SPObject *i = parent->first_child(); i; i = SP_OBJECT_NEXT(i) ) {
1655         if (SP_OBJECT_NEXT(i) == this)
1656             return i;
1657     }
1658     return NULL;
1661 /* Titles and descriptions */
1663 /* Note:
1664    Titles and descriptions are stored in 'title' and 'desc' child elements
1665    (see section 5.4 of the SVG 1.0 and 1.1 specifications).  The spec allows
1666    an element to have more than one 'title' child element, but strongly
1667    recommends against this and requires using the first one if a choice must
1668    be made.  The same applies to 'desc' elements.  Therefore, these functions
1669    ignore all but the first 'title' child element and first 'desc' child
1670    element, except when deleting a title or description.
1671 */
1673 /**
1674  * Returns the title of this object, or NULL if there is none.
1675  * The caller must free the returned string using g_free() - see comment
1676  * for getTitleOrDesc() below.
1677  */
1678 gchar *
1679 SPObject::title() const
1681     return getTitleOrDesc("svg:title");
1684 /**
1685  * Sets the title of this object
1686  * A NULL first argument is interpreted as meaning that the existing title
1687  * (if any) should be deleted.
1688  * The second argument is optional - see setTitleOrDesc() below for details.
1689  */
1690 bool
1691 SPObject::setTitle(gchar const *title, bool verbatim)
1693     return setTitleOrDesc(title, "svg:title", verbatim);
1696 /**
1697  * Returns the description of this object, or NULL if there is none.
1698  * The caller must free the returned string using g_free() - see comment
1699  * for getTitleOrDesc() below.
1700  */
1701 gchar *
1702 SPObject::desc() const
1704     return getTitleOrDesc("svg:desc");
1707 /**
1708  * Sets the description of this object.
1709  * A NULL first argument is interpreted as meaning that the existing
1710  * description (if any) should be deleted.
1711  * The second argument is optional - see setTitleOrDesc() below for details.
1712  */
1713 bool
1714 SPObject::setDesc(gchar const *desc, bool verbatim)
1716     return setTitleOrDesc(desc, "svg:desc", verbatim);
1719 /**
1720  * Returns the title or description of this object, or NULL if there is none.
1721  *
1722  * The SVG spec allows 'title' and 'desc' elements to contain text marked up
1723  * using elements from other namespaces.  Therefore, this function cannot
1724  * in general just return a pointer to an existing string - it must instead
1725  * construct a string containing the title or description without the mark-up.
1726  * Consequently, the return value is a newly allocated string (or NULL), and
1727  * must be freed (using g_free()) by the caller.
1728  */
1729 gchar *
1730 SPObject::getTitleOrDesc(gchar const *svg_tagname) const
1732     SPObject *elem = findFirstChild(svg_tagname);
1733     if (elem == NULL) return NULL;
1734     return g_string_free(elem->textualContent(), FALSE);
1737 /**
1738  * Sets or deletes the title or description of this object.
1739  * A NULL 'value' argument causes the title or description to be deleted.
1740  *
1741  * 'verbatim' parameter:
1742  * If verbatim==true, then the title or description is set to exactly the
1743  * specified value.  If verbatim==false then two exceptions are made:
1744  *   (1) If the specified value is just whitespace, then the title/description
1745  *       is deleted.
1746  *   (2) If the specified value is the same as the current value except for
1747  *       mark-up, then the current value is left unchanged.
1748  * This is usually the desired behaviour, so 'verbatim' defaults to false for
1749  * setTitle() and setDesc().
1750  *
1751  * The return value is true if a change was made to the title/description,
1752  * and usually false otherwise.
1753  */
1754 bool
1755 SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim)
1757     if (!verbatim) {
1758         // If the new title/description is just whitespace,
1759         // treat it as though it were NULL.
1760         if (value) {
1761             bool just_whitespace = true;
1762             for (const gchar *cp = value; *cp; ++cp) {
1763                 if (!std::strchr("\r\n \t", *cp)) {
1764                     just_whitespace = false;
1765                     break;
1766                 }
1767             }
1768             if (just_whitespace) value = NULL;
1769         }
1770         // Don't stomp on mark-up if there is no real change.
1771         if (value) {
1772             gchar *current_value = getTitleOrDesc(svg_tagname);
1773             if (current_value) {
1774                 bool different = std::strcmp(current_value, value);
1775                 g_free(current_value);
1776                 if (!different) return false;
1777             }
1778         }
1779     }
1781     SPObject *elem = findFirstChild(svg_tagname);
1783     if (value == NULL) {
1784         if (elem == NULL) return false;
1785         // delete the title/description(s)
1786         while (elem) {
1787             elem->deleteObject();
1788             elem = findFirstChild(svg_tagname);
1789         }
1790         return true;
1791     }
1793     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
1795     if (elem == NULL) {
1796         // create a new 'title' or 'desc' element, putting it at the
1797         // beginning (in accordance with the spec's recommendations)
1798         Inkscape::XML::Node *xml_elem = xml_doc->createElement(svg_tagname);
1799         repr->addChild(xml_elem, NULL);
1800         elem = document->getObjectByRepr(xml_elem);
1801         Inkscape::GC::release(xml_elem);
1802     }
1803     else {
1804         // remove the current content of the 'text' or 'desc' element
1805         SPObject *child;
1806         while (NULL != (child = elem->firstChild())) child->deleteObject();
1807     }
1809     // add the new content
1810     elem->appendChildRepr(xml_doc->createTextNode(value));
1811     return true;
1814 /**
1815  * Find the first child of this object with a given tag name,
1816  * and return it.  Returns NULL if there is no matching child.
1817  */
1818 SPObject *
1819 SPObject::findFirstChild(gchar const *tagname) const
1821     for (SPObject *child = children; child; child = child->next)
1822     {
1823         if (child->repr->type() == Inkscape::XML::ELEMENT_NODE &&
1824             !strcmp(child->repr->name(), tagname)) return child;
1825     }
1826     return NULL;
1829 /**
1830  * Return the full textual content of an element (typically all the
1831  * content except the tags).
1832  * Must not be used on anything except elements.
1833  */
1834 GString*
1835 SPObject::textualContent() const
1837     GString* text = g_string_new("");
1839     for (const SPObject *child = firstChild(); child; child = child->next)
1840     {
1841         Inkscape::XML::NodeType child_type = child->repr->type();
1843         if (child_type == Inkscape::XML::ELEMENT_NODE) {
1844             GString * new_text = child->textualContent();
1845             g_string_append(text, new_text->str);
1846             g_string_free(new_text, TRUE);
1847         }
1848         else if (child_type == Inkscape::XML::TEXT_NODE) {
1849             g_string_append(text, child->repr->content());
1850         }
1851     }
1852     return text;
1855 /*
1856   Local Variables:
1857   mode:c++
1858   c-file-style:"stroustrup"
1859   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1860   indent-tabs-mode:nil
1861   fill-column:99
1862   End:
1863 */
1864 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :