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"
57 #include "algorithms/longest-common-suffix.h"
58 using std::memcpy;
59 using std::strchr;
60 using std::strcmp;
61 using std::strlen;
62 using std::strstr;
64 #define noSP_OBJECT_DEBUG_CASCADE
66 #define noSP_OBJECT_DEBUG
68 #ifdef SP_OBJECT_DEBUG
69 # define debug(f, a...) { g_print("%s(%d) %s:", \
70 __FILE__,__LINE__,__FUNCTION__); \
71 g_print(f, ## a); \
72 g_print("\n"); \
73 }
74 #else
75 # define debug(f, a...) /**/
76 #endif
78 static void sp_object_class_init(SPObjectClass *klass);
79 static void sp_object_init(SPObject *object);
80 static void sp_object_finalize(GObject *object);
82 static void sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref);
83 static void sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child);
84 static void sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *old_ref, Inkscape::XML::Node *new_ref);
86 static void sp_object_release(SPObject *object);
87 static void sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
89 static void sp_object_private_set(SPObject *object, unsigned int key, gchar const *value);
90 static Inkscape::XML::Node *sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
92 /* Real handlers of repr signals */
94 static void sp_object_repr_attr_changed(Inkscape::XML::Node *repr, gchar const *key, gchar const *oldval, gchar const *newval, bool is_interactive, gpointer data);
96 static void sp_object_repr_content_changed(Inkscape::XML::Node *repr, gchar const *oldcontent, gchar const *newcontent, gpointer data);
98 static void sp_object_repr_child_added(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data);
99 static void sp_object_repr_child_removed(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, void *data);
101 static void sp_object_repr_order_changed(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data);
103 static gchar *sp_object_get_unique_id(SPObject *object, gchar const *defid);
105 guint update_in_progress = 0; // guard against update-during-update
107 Inkscape::XML::NodeEventVector object_event_vector = {
108 sp_object_repr_child_added,
109 sp_object_repr_child_removed,
110 sp_object_repr_attr_changed,
111 sp_object_repr_content_changed,
112 sp_object_repr_order_changed
113 };
115 // A friend class used to set internal members on SPObject so as to not expose settors in SPObject's public API
116 class SPObjectImpl
117 {
118 public:
120 /**
121 * Null's the id member of an SPObject without attempting to free prior contents.
122 */
123 static void setIdNull( SPObject* obj ) {
124 if (obj) {
125 obj->id = 0;
126 }
127 }
129 /**
130 * Sets the id member of an object, freeing any prior content.
131 */
132 static void setId( SPObject* obj, gchar const* id ) {
133 if (obj && (id != obj->id) ) {
134 if (obj->id) {
135 g_free(obj->id);
136 obj->id = 0;
137 }
138 if (id) {
139 obj->id = g_strdup(id);
140 }
141 }
142 }
143 };
146 static GObjectClass *parent_class;
148 /**
149 * Registers the SPObject class with Gdk and returns its type number.
150 */
151 GType
152 sp_object_get_type(void)
153 {
154 static GType type = 0;
155 if (!type) {
156 GTypeInfo info = {
157 sizeof(SPObjectClass),
158 NULL, NULL,
159 (GClassInitFunc) sp_object_class_init,
160 NULL, NULL,
161 sizeof(SPObject),
162 16,
163 (GInstanceInitFunc) sp_object_init,
164 NULL
165 };
166 type = g_type_register_static(G_TYPE_OBJECT, "SPObject", &info, (GTypeFlags)0);
167 }
168 return type;
169 }
171 /**
172 * Initializes the SPObject vtable.
173 */
174 static void
175 sp_object_class_init(SPObjectClass *klass)
176 {
177 GObjectClass *object_class;
179 object_class = (GObjectClass *) klass;
181 parent_class = (GObjectClass *) g_type_class_ref(G_TYPE_OBJECT);
183 object_class->finalize = sp_object_finalize;
185 klass->child_added = sp_object_child_added;
186 klass->remove_child = sp_object_remove_child;
187 klass->order_changed = sp_object_order_changed;
189 klass->release = sp_object_release;
191 klass->build = sp_object_build;
193 klass->set = sp_object_private_set;
194 klass->write = sp_object_private_write;
195 }
197 /**
198 * Callback to initialize the SPObject object.
199 */
200 static void
201 sp_object_init(SPObject *object)
202 {
203 debug("id=%x, typename=%s",object, g_type_name_from_instance((GTypeInstance*)object));
205 object->hrefcount = 0;
206 object->_total_hrefcount = 0;
207 object->document = NULL;
208 object->children = object->_last_child = NULL;
209 object->parent = object->next = NULL;
210 object->repr = NULL;
211 SPObjectImpl::setIdNull(object);
213 object->_collection_policy = SPObject::COLLECT_WITH_PARENT;
215 new (&object->_release_signal) sigc::signal<void, SPObject *>();
216 new (&object->_modified_signal) sigc::signal<void, SPObject *, unsigned int>();
217 new (&object->_delete_signal) sigc::signal<void, SPObject *>();
218 new (&object->_position_changed_signal) sigc::signal<void, SPObject *>();
219 object->_successor = NULL;
221 // FIXME: now we create style for all objects, but per SVG, only the following can have style attribute:
222 // vg, g, defs, desc, title, symbol, use, image, switch, path, rect, circle, ellipse, line, polyline,
223 // polygon, text, tspan, tref, textPath, altGlyph, glyphRef, marker, linearGradient, radialGradient,
224 // stop, pattern, clipPath, mask, filter, feImage, a, font, glyph, missing-glyph, foreignObject
225 object->style = sp_style_new_from_object(object);
227 object->_label = NULL;
228 object->_default_label = NULL;
229 }
231 /**
232 * Callback to destroy all members and connections of object and itself.
233 */
234 static void
235 sp_object_finalize(GObject *object)
236 {
237 SPObject *spobject = (SPObject *)object;
239 g_free(spobject->_label);
240 g_free(spobject->_default_label);
241 spobject->_label = NULL;
242 spobject->_default_label = NULL;
244 if (spobject->_successor) {
245 sp_object_unref(spobject->_successor, NULL);
246 spobject->_successor = NULL;
247 }
249 if (((GObjectClass *) (parent_class))->finalize) {
250 (* ((GObjectClass *) (parent_class))->finalize)(object);
251 }
253 spobject->_release_signal.~signal();
254 spobject->_modified_signal.~signal();
255 spobject->_delete_signal.~signal();
256 spobject->_position_changed_signal.~signal();
257 }
259 namespace {
261 namespace Debug = Inkscape::Debug;
262 namespace Util = Inkscape::Util;
264 typedef Debug::SimpleEvent<Debug::Event::REFCOUNT> BaseRefCountEvent;
266 class RefCountEvent : public BaseRefCountEvent {
267 public:
268 RefCountEvent(SPObject *object, int bias, Util::ptr_shared<char> name)
269 : BaseRefCountEvent(name)
270 {
271 _addProperty("object", Util::format("%p", object));
272 _addProperty("class", Debug::demangle(g_type_name(G_TYPE_FROM_INSTANCE(object))));
273 _addProperty("new-refcount", Util::format("%d", G_OBJECT(object)->ref_count + bias));
274 }
275 };
277 class RefEvent : public RefCountEvent {
278 public:
279 RefEvent(SPObject *object)
280 : RefCountEvent(object, 1, Util::share_static_string("sp-object-ref"))
281 {}
282 };
284 class UnrefEvent : public RefCountEvent {
285 public:
286 UnrefEvent(SPObject *object)
287 : RefCountEvent(object, -1, Util::share_static_string("sp-object-unref"))
288 {}
289 };
291 }
293 gchar const* SPObject::getId() const {
294 return id;
295 }
297 /**
298 * Increase reference count of object, with possible debugging.
299 *
300 * \param owner If non-NULL, make debug log entry.
301 * \return object, NULL is error.
302 * \pre object points to real object
303 */
304 SPObject *
305 sp_object_ref(SPObject *object, SPObject *owner)
306 {
307 g_return_val_if_fail(object != NULL, NULL);
308 g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
309 g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
311 Inkscape::Debug::EventTracker<RefEvent> tracker(object);
312 g_object_ref(G_OBJECT(object));
313 return object;
314 }
316 /**
317 * Decrease reference count of object, with possible debugging and
318 * finalization.
319 *
320 * \param owner If non-NULL, make debug log entry.
321 * \return always NULL
322 * \pre object points to real object
323 */
324 SPObject *
325 sp_object_unref(SPObject *object, SPObject *owner)
326 {
327 g_return_val_if_fail(object != NULL, NULL);
328 g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
329 g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
331 Inkscape::Debug::EventTracker<UnrefEvent> tracker(object);
332 g_object_unref(G_OBJECT(object));
333 return NULL;
334 }
336 /**
337 * Increase weak refcount.
338 *
339 * Hrefcount is used for weak references, for example, to
340 * determine whether any graphical element references a certain gradient
341 * node.
342 * \param owner Ignored.
343 * \return object, NULL is error
344 * \pre object points to real object
345 */
346 SPObject *
347 sp_object_href(SPObject *object, gpointer /*owner*/)
348 {
349 g_return_val_if_fail(object != NULL, NULL);
350 g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
352 object->hrefcount++;
353 object->_updateTotalHRefCount(1);
355 return object;
356 }
358 /**
359 * Decrease weak refcount.
360 *
361 * Hrefcount is used for weak references, for example, to determine whether
362 * any graphical element references a certain gradient node.
363 * \param owner Ignored.
364 * \return always NULL
365 * \pre object points to real object and hrefcount>0
366 */
367 SPObject *
368 sp_object_hunref(SPObject *object, gpointer /*owner*/)
369 {
370 g_return_val_if_fail(object != NULL, NULL);
371 g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
372 g_return_val_if_fail(object->hrefcount > 0, NULL);
374 object->hrefcount--;
375 object->_updateTotalHRefCount(-1);
377 return NULL;
378 }
380 /**
381 * Adds increment to _total_hrefcount of object and its parents.
382 */
383 void
384 SPObject::_updateTotalHRefCount(int increment) {
385 SPObject *topmost_collectable = NULL;
386 for ( SPObject *iter = this ; iter ; iter = SP_OBJECT_PARENT(iter) ) {
387 iter->_total_hrefcount += increment;
388 if ( iter->_total_hrefcount < iter->hrefcount ) {
389 g_critical("HRefs overcounted");
390 }
391 if ( iter->_total_hrefcount == 0 &&
392 iter->_collection_policy != COLLECT_WITH_PARENT )
393 {
394 topmost_collectable = iter;
395 }
396 }
397 if (topmost_collectable) {
398 topmost_collectable->requestOrphanCollection();
399 }
400 }
402 /**
403 * True if object is non-NULL and this is some in/direct parent of object.
404 */
405 bool
406 SPObject::isAncestorOf(SPObject const *object) const {
407 g_return_val_if_fail(object != NULL, false);
408 object = SP_OBJECT_PARENT(object);
409 while (object) {
410 if ( object == this ) {
411 return true;
412 }
413 object = SP_OBJECT_PARENT(object);
414 }
415 return false;
416 }
418 namespace {
420 bool same_objects(SPObject const &a, SPObject const &b) {
421 return &a == &b;
422 }
424 }
426 /**
427 * Returns youngest object being parent to this and object.
428 */
429 SPObject const *
430 SPObject::nearestCommonAncestor(SPObject const *object) const {
431 g_return_val_if_fail(object != NULL, NULL);
433 using Inkscape::Algorithms::longest_common_suffix;
434 return longest_common_suffix<SPObject::ConstParentIterator>(this, object, NULL, &same_objects);
435 }
437 SPObject const *AncestorSon(SPObject const *obj, SPObject const *ancestor) {
438 if (obj == NULL || ancestor == NULL)
439 return NULL;
440 if (SP_OBJECT_PARENT(obj) == ancestor)
441 return obj;
442 return AncestorSon(SP_OBJECT_PARENT(obj), ancestor);
443 }
445 /**
446 * Compares height of objects in tree.
447 *
448 * Works for different-parent objects, so long as they have a common ancestor.
449 * \return \verbatim
450 * 0 positions are equivalent
451 * 1 first object's position is greater than the second
452 * -1 first object's position is less than the second \endverbatim
453 */
454 int
455 sp_object_compare_position(SPObject const *first, SPObject const *second)
456 {
457 if (first == second) return 0;
459 SPObject const *ancestor = first->nearestCommonAncestor(second);
460 if (ancestor == NULL) return 0; // cannot compare, no common ancestor!
462 // we have an object and its ancestor (should not happen when sorting selection)
463 if (ancestor == first)
464 return 1;
465 if (ancestor == second)
466 return -1;
468 SPObject const *to_first = AncestorSon(first, ancestor);
469 SPObject const *to_second = AncestorSon(second, ancestor);
471 g_assert(SP_OBJECT_PARENT(to_second) == SP_OBJECT_PARENT(to_first));
473 return sp_repr_compare_position(SP_OBJECT_REPR(to_first), SP_OBJECT_REPR(to_second));
474 }
477 /**
478 * Append repr as child of this object.
479 * \pre this is not a cloned object
480 */
481 SPObject *
482 SPObject::appendChildRepr(Inkscape::XML::Node *repr) {
483 if (!SP_OBJECT_IS_CLONED(this)) {
484 SP_OBJECT_REPR(this)->appendChild(repr);
485 return SP_OBJECT_DOCUMENT(this)->getObjectByRepr(repr);
486 } else {
487 g_critical("Attempt to append repr as child of cloned object");
488 return NULL;
489 }
490 }
492 /**
493 * Retrieves the children as a GSList object, optionally ref'ing the children
494 * in the process, if add_ref is specified.
495 */
496 GSList *SPObject::childList(bool add_ref, Action) {
497 GSList *l = NULL;
498 for (SPObject *child = sp_object_first_child(this) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
499 if (add_ref)
500 g_object_ref (G_OBJECT (child));
502 l = g_slist_prepend (l, child);
503 }
504 return l;
506 }
508 /** Gets the label property for the object or a default if no label
509 * is defined.
510 */
511 gchar const *
512 SPObject::label() const {
513 return _label;
514 }
516 /** Returns a default label property for the object. */
517 gchar const *
518 SPObject::defaultLabel() const {
519 if (_label) {
520 return _label;
521 } else {
522 if (!_default_label) {
523 gchar const *id=SP_OBJECT_ID(this);
524 if (id) {
525 _default_label = g_strdup_printf("#%s", id);
526 } else {
527 _default_label = g_strdup_printf("<%s>", SP_OBJECT_REPR(this)->name());
528 }
529 }
530 return _default_label;
531 }
532 }
534 /** Sets the label property for the object */
535 void
536 SPObject::setLabel(gchar const *label) {
537 SP_OBJECT_REPR(this)->setAttribute("inkscape:label", label, false);
538 }
541 /** Queues the object for orphan collection */
542 void
543 SPObject::requestOrphanCollection() {
544 g_return_if_fail(document != NULL);
546 // do not remove style or script elements (Bug #276244)
547 if (SP_IS_STYLE_ELEM(this))
548 return;
549 if (SP_IS_SCRIPT(this))
550 return;
552 document->queueForOrphanCollection(this);
554 /** \todo
555 * This is a temporary hack added to make fill&stroke rebuild its
556 * gradient list when the defs are vacuumed. gradient-vector.cpp
557 * listens to the modified signal on defs, and now we give it that
558 * signal. Mental says that this should be made automatic by
559 * merging SPObjectGroup with SPObject; SPObjectGroup would issue
560 * this signal automatically. Or maybe just derive SPDefs from
561 * SPObjectGroup?
562 */
564 this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
565 }
567 /** Sends the delete signal to all children of this object recursively */
568 void
569 SPObject::_sendDeleteSignalRecursive() {
570 for (SPObject *child = sp_object_first_child(this); child; child = SP_OBJECT_NEXT(child)) {
571 child->_delete_signal.emit(child);
572 child->_sendDeleteSignalRecursive();
573 }
574 }
576 /**
577 * Deletes the object reference, unparenting it from its parent.
578 *
579 * If the \a propagate parameter is set to true, it emits a delete
580 * signal. If the \a propagate_descendants parameter is true, it
581 * recursively sends the delete signal to children.
582 */
583 void
584 SPObject::deleteObject(bool propagate, bool propagate_descendants)
585 {
586 sp_object_ref(this, NULL);
587 if (propagate) {
588 _delete_signal.emit(this);
589 }
590 if (propagate_descendants) {
591 this->_sendDeleteSignalRecursive();
592 }
594 Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
595 if (repr && sp_repr_parent(repr)) {
596 sp_repr_unparent(repr);
597 }
599 if (_successor) {
600 _successor->deleteObject(propagate, propagate_descendants);
601 }
602 sp_object_unref(this, NULL);
603 }
605 /**
606 * Put object into object tree, under parent, and behind prev;
607 * also update object's XML space.
608 */
609 void
610 sp_object_attach(SPObject *parent, SPObject *object, SPObject *prev)
611 {
612 g_return_if_fail(parent != NULL);
613 g_return_if_fail(SP_IS_OBJECT(parent));
614 g_return_if_fail(object != NULL);
615 g_return_if_fail(SP_IS_OBJECT(object));
616 g_return_if_fail(!prev || SP_IS_OBJECT(prev));
617 g_return_if_fail(!prev || prev->parent == parent);
618 g_return_if_fail(!object->parent);
620 sp_object_ref(object, parent);
621 object->parent = parent;
622 parent->_updateTotalHRefCount(object->_total_hrefcount);
624 SPObject *next;
625 if (prev) {
626 next = prev->next;
627 prev->next = object;
628 } else {
629 next = parent->children;
630 parent->children = object;
631 }
632 object->next = next;
633 if (!next) {
634 parent->_last_child = object;
635 }
636 if (!object->xml_space.set)
637 object->xml_space.value = parent->xml_space.value;
638 }
640 /**
641 * In list of object's siblings, move object behind prev.
642 */
643 void
644 sp_object_reorder(SPObject *object, SPObject *prev) {
645 g_return_if_fail(object != NULL);
646 g_return_if_fail(SP_IS_OBJECT(object));
647 g_return_if_fail(object->parent != NULL);
648 g_return_if_fail(object != prev);
649 g_return_if_fail(!prev || SP_IS_OBJECT(prev));
650 g_return_if_fail(!prev || prev->parent == object->parent);
652 SPObject *const parent=object->parent;
654 SPObject *old_prev=NULL;
655 for ( SPObject *child = parent->children ; child && child != object ;
656 child = child->next )
657 {
658 old_prev = child;
659 }
661 SPObject *next=object->next;
662 if (old_prev) {
663 old_prev->next = next;
664 } else {
665 parent->children = next;
666 }
667 if (!next) {
668 parent->_last_child = old_prev;
669 }
670 if (prev) {
671 next = prev->next;
672 prev->next = object;
673 } else {
674 next = parent->children;
675 parent->children = object;
676 }
677 object->next = next;
678 if (!next) {
679 parent->_last_child = object;
680 }
681 }
683 /**
684 * Remove object from parent's children, release and unref it.
685 */
686 void
687 sp_object_detach(SPObject *parent, SPObject *object) {
688 g_return_if_fail(parent != NULL);
689 g_return_if_fail(SP_IS_OBJECT(parent));
690 g_return_if_fail(object != NULL);
691 g_return_if_fail(SP_IS_OBJECT(object));
692 g_return_if_fail(object->parent == parent);
694 object->releaseReferences();
696 SPObject *prev=NULL;
697 for ( SPObject *child = parent->children ; child && child != object ;
698 child = child->next )
699 {
700 prev = child;
701 }
703 SPObject *next=object->next;
704 if (prev) {
705 prev->next = next;
706 } else {
707 parent->children = next;
708 }
709 if (!next) {
710 parent->_last_child = prev;
711 }
713 object->next = NULL;
714 object->parent = NULL;
716 parent->_updateTotalHRefCount(-object->_total_hrefcount);
717 sp_object_unref(object, parent);
718 }
720 /**
721 * Return object's child whose node pointer equals repr.
722 */
723 SPObject *
724 sp_object_get_child_by_repr(SPObject *object, Inkscape::XML::Node *repr)
725 {
726 g_return_val_if_fail(object != NULL, NULL);
727 g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
728 g_return_val_if_fail(repr != NULL, NULL);
730 if (object->_last_child && SP_OBJECT_REPR(object->_last_child) == repr)
731 return object->_last_child; // optimization for common scenario
732 for ( SPObject *child = object->children ; child ; child = child->next ) {
733 if ( SP_OBJECT_REPR(child) == repr ) {
734 return child;
735 }
736 }
738 return NULL;
739 }
741 /**
742 * Callback for child_added event.
743 * Invoked whenever the given mutation event happens in the XML tree.
744 */
745 static void
746 sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
747 {
748 GType type = sp_repr_type_lookup(child);
749 if (!type) {
750 return;
751 }
752 SPObject *ochild = SP_OBJECT(g_object_new(type, 0));
753 SPObject *prev = ref ? sp_object_get_child_by_repr(object, ref) : NULL;
754 sp_object_attach(object, ochild, prev);
755 sp_object_unref(ochild, NULL);
757 sp_object_invoke_build(ochild, object->document, child, SP_OBJECT_IS_CLONED(object));
758 }
760 /**
761 * Removes, releases and unrefs all children of object.
762 *
763 * This is the opposite of build. It has to be invoked as soon as the
764 * object is removed from the tree, even if it is still alive according
765 * to reference count. The frontend unregisters the object from the
766 * document and releases the SPRepr bindings; implementations should free
767 * state data and release all child objects. Invoking release on
768 * SPRoot destroys the whole document tree.
769 * \see sp_object_build()
770 */
771 static void sp_object_release(SPObject *object)
772 {
773 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
774 while (object->children) {
775 sp_object_detach(object, object->children);
776 }
777 }
779 /**
780 * Remove object's child whose node equals repr, release and
781 * unref it.
782 *
783 * Invoked whenever the given mutation event happens in the XML
784 * tree, BEFORE removal from the XML tree happens, so grouping
785 * objects can safely release the child data.
786 */
787 static void
788 sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child)
789 {
790 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
791 SPObject *ochild = sp_object_get_child_by_repr(object, child);
792 g_return_if_fail (ochild != NULL || !strcmp("comment", child->name())); // comments have no objects
793 if (ochild)
794 sp_object_detach(object, ochild);
795 }
797 /**
798 * Move object corresponding to child after sibling object corresponding
799 * to new_ref.
800 * Invoked whenever the given mutation event happens in the XML tree.
801 * \param old_ref Ignored
802 */
803 static void sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node */*old_ref*/,
804 Inkscape::XML::Node *new_ref)
805 {
806 SPObject *ochild = sp_object_get_child_by_repr(object, child);
807 g_return_if_fail(ochild != NULL);
808 SPObject *prev = new_ref ? sp_object_get_child_by_repr(object, new_ref) : NULL;
809 sp_object_reorder(ochild, prev);
810 ochild->_position_changed_signal.emit(ochild);
811 }
813 /**
814 * Virtual build callback.
815 *
816 * This has to be invoked immediately after creation of an SPObject. The
817 * frontend method ensures that the new object is properly attached to
818 * the document and repr; implementation then will parse all of the attributes,
819 * generate the children objects and so on. Invoking build on the SPRoot
820 * object results in creation of the whole document tree (this is, what
821 * SPDocument does after the creation of the XML tree).
822 * \see sp_object_release()
823 */
824 static void
825 sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
826 {
827 /* Nothing specific here */
828 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
830 sp_object_read_attr(object, "xml:space");
831 sp_object_read_attr(object, "inkscape:label");
832 sp_object_read_attr(object, "inkscape:collect");
834 for (Inkscape::XML::Node *rchild = repr->firstChild() ; rchild != NULL; rchild = rchild->next()) {
835 GType type = sp_repr_type_lookup(rchild);
836 if (!type) {
837 continue;
838 }
839 SPObject *child = SP_OBJECT(g_object_new(type, 0));
840 sp_object_attach(object, child, object->lastChild());
841 sp_object_unref(child, NULL);
842 sp_object_invoke_build(child, document, rchild, SP_OBJECT_IS_CLONED(object));
843 }
844 }
846 void sp_object_invoke_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
847 {
848 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
850 g_assert(object != NULL);
851 g_assert(SP_IS_OBJECT(object));
852 g_assert(document != NULL);
853 g_assert(repr != NULL);
855 g_assert(object->document == NULL);
856 g_assert(object->repr == NULL);
857 g_assert(object->getId() == NULL);
859 /* Bookkeeping */
861 object->document = document;
862 object->repr = repr;
863 if (!cloned)
864 Inkscape::GC::anchor(repr);
865 object->cloned = cloned;
867 if (!SP_OBJECT_IS_CLONED(object)) {
868 object->document->bindObjectToRepr(object->repr, object);
870 if (Inkscape::XML::id_permitted(object->repr)) {
871 /* If we are not cloned, and not seeking, force unique id */
872 gchar const *id = object->repr->attribute("id");
873 if (!document->isSeeking()) {
874 {
875 gchar *realid = sp_object_get_unique_id(object, id);
876 g_assert(realid != NULL);
878 object->document->bindObjectToId(realid, object);
879 SPObjectImpl::setId(object, realid);
880 g_free(realid);
881 }
883 /* Redefine ID, if required */
884 if ((id == NULL) || (strcmp(id, object->getId()) != 0)) {
885 object->repr->setAttribute("id", object->getId());
886 }
887 } else if (id) {
888 // bind if id, but no conflict -- otherwise, we can expect
889 // a subsequent setting of the id attribute
890 if (!object->document->getObjectById(id)) {
891 object->document->bindObjectToId(id, object);
892 SPObjectImpl::setId(object, id);
893 }
894 }
895 }
896 } else {
897 g_assert(object->getId() == NULL);
898 }
900 /* Invoke derived methods, if any */
901 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->build) {
902 (*((SPObjectClass *) G_OBJECT_GET_CLASS(object))->build)(object, document, repr);
903 }
905 /* Signalling (should be connected AFTER processing derived methods */
906 sp_repr_add_listener(repr, &object_event_vector, object);
907 }
909 void SPObject::releaseReferences() {
910 g_assert(this->document);
911 g_assert(this->repr);
913 sp_repr_remove_listener_by_data(this->repr, this);
915 this->_release_signal.emit(this);
916 SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
917 if (klass->release) {
918 klass->release(this);
919 }
921 /* all hrefs should be released by the "release" handlers */
922 g_assert(this->hrefcount == 0);
924 if (!SP_OBJECT_IS_CLONED(this)) {
925 if (this->id) {
926 this->document->bindObjectToId(this->id, NULL);
927 }
928 g_free(this->id);
929 this->id = NULL;
931 g_free(this->_default_label);
932 this->_default_label = NULL;
934 this->document->bindObjectToRepr(this->repr, NULL);
936 Inkscape::GC::release(this->repr);
937 } else {
938 g_assert(!this->id);
939 }
941 if (this->style) {
942 this->style = sp_style_unref(this->style);
943 }
945 this->document = NULL;
946 this->repr = NULL;
947 }
949 /**
950 * Callback for child_added node event.
951 */
952 static void
953 sp_object_repr_child_added(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data)
954 {
955 SPObject *object = SP_OBJECT(data);
957 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->child_added)
958 (*((SPObjectClass *)G_OBJECT_GET_CLASS(object))->child_added)(object, child, ref);
959 }
961 /**
962 * Callback for remove_child node event.
963 */
964 static void
965 sp_object_repr_child_removed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node */*ref*/, gpointer data)
966 {
967 SPObject *object = SP_OBJECT(data);
969 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->remove_child) {
970 (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->remove_child)(object, child);
971 }
972 }
974 /**
975 * Callback for order_changed node event.
976 *
977 * \todo fixme:
978 */
979 static void
980 sp_object_repr_order_changed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data)
981 {
982 SPObject *object = SP_OBJECT(data);
984 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->order_changed) {
985 (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->order_changed)(object, child, old, newer);
986 }
987 }
989 /**
990 * Callback for set event.
991 */
992 static void
993 sp_object_private_set(SPObject *object, unsigned int key, gchar const *value)
994 {
995 g_assert(key != SP_ATTR_INVALID);
997 switch (key) {
998 case SP_ATTR_ID:
999 if ( !SP_OBJECT_IS_CLONED(object) && object->repr->type() == Inkscape::XML::ELEMENT_NODE ) {
1000 SPDocument *document=object->document;
1001 SPObject *conflict=NULL;
1003 gchar const *new_id = value;
1005 if (new_id) {
1006 conflict = document->getObjectById((char const *)new_id);
1007 }
1009 if ( conflict && conflict != object ) {
1010 if (!document->isSeeking()) {
1011 sp_object_ref(conflict, NULL);
1012 // give the conflicting object a new ID
1013 gchar *new_conflict_id = sp_object_get_unique_id(conflict, NULL);
1014 SP_OBJECT_REPR(conflict)->setAttribute("id", new_conflict_id);
1015 g_free(new_conflict_id);
1016 sp_object_unref(conflict, NULL);
1017 } else {
1018 new_id = NULL;
1019 }
1020 }
1022 if (object->getId()) {
1023 document->bindObjectToId(object->getId(), NULL);
1024 SPObjectImpl::setId(object, 0);
1025 }
1027 if (new_id) {
1028 SPObjectImpl::setId(object, new_id);
1029 document->bindObjectToId(object->getId(), object);
1030 }
1032 g_free(object->_default_label);
1033 object->_default_label = NULL;
1034 }
1035 break;
1036 case SP_ATTR_INKSCAPE_LABEL:
1037 g_free(object->_label);
1038 if (value) {
1039 object->_label = g_strdup(value);
1040 } else {
1041 object->_label = NULL;
1042 }
1043 g_free(object->_default_label);
1044 object->_default_label = NULL;
1045 break;
1046 case SP_ATTR_INKSCAPE_COLLECT:
1047 if ( value && !strcmp(value, "always") ) {
1048 object->setCollectionPolicy(SPObject::ALWAYS_COLLECT);
1049 } else {
1050 object->setCollectionPolicy(SPObject::COLLECT_WITH_PARENT);
1051 }
1052 break;
1053 case SP_ATTR_XML_SPACE:
1054 if (value && !strcmp(value, "preserve")) {
1055 object->xml_space.value = SP_XML_SPACE_PRESERVE;
1056 object->xml_space.set = TRUE;
1057 } else if (value && !strcmp(value, "default")) {
1058 object->xml_space.value = SP_XML_SPACE_DEFAULT;
1059 object->xml_space.set = TRUE;
1060 } else if (object->parent) {
1061 SPObject *parent;
1062 parent = object->parent;
1063 object->xml_space.value = parent->xml_space.value;
1064 }
1065 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1066 break;
1067 case SP_ATTR_STYLE:
1068 sp_style_read_from_object(object->style, object);
1069 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1070 break;
1071 default:
1072 break;
1073 }
1074 }
1076 /**
1077 * Call virtual set() function of object.
1078 */
1079 void
1080 sp_object_set(SPObject *object, unsigned int key, gchar const *value)
1081 {
1082 g_assert(object != NULL);
1083 g_assert(SP_IS_OBJECT(object));
1085 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->set) {
1086 ((SPObjectClass *) G_OBJECT_GET_CLASS(object))->set(object, key, value);
1087 }
1088 }
1090 /**
1091 * Read value of key attribute from XML node into object.
1092 */
1093 void
1094 sp_object_read_attr(SPObject *object, gchar const *key)
1095 {
1096 g_assert(object != NULL);
1097 g_assert(SP_IS_OBJECT(object));
1098 g_assert(key != NULL);
1100 g_assert(object->repr != NULL);
1102 unsigned int keyid = sp_attribute_lookup(key);
1103 if (keyid != SP_ATTR_INVALID) {
1104 /* Retrieve the 'key' attribute from the object's XML representation */
1105 gchar const *value = object->repr->attribute(key);
1107 sp_object_set(object, keyid, value);
1108 }
1109 }
1111 /**
1112 * Callback for attr_changed node event.
1113 */
1114 static void
1115 sp_object_repr_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *key, gchar const */*oldval*/, gchar const */*newval*/, bool is_interactive, gpointer data)
1116 {
1117 SPObject *object = SP_OBJECT(data);
1119 sp_object_read_attr(object, key);
1121 // manual changes to extension attributes require the normal
1122 // attributes, which depend on them, to be updated immediately
1123 if (is_interactive) {
1124 object->updateRepr(0);
1125 }
1126 }
1128 /**
1129 * Callback for content_changed node event.
1130 */
1131 static void
1132 sp_object_repr_content_changed(Inkscape::XML::Node */*repr*/, gchar const */*oldcontent*/, gchar const */*newcontent*/, gpointer data)
1133 {
1134 SPObject *object = SP_OBJECT(data);
1136 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)
1137 (*((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)(object);
1138 }
1140 /**
1141 * Return string representation of space value.
1142 */
1143 static gchar const*
1144 sp_xml_get_space_string(unsigned int space)
1145 {
1146 switch (space) {
1147 case SP_XML_SPACE_DEFAULT:
1148 return "default";
1149 case SP_XML_SPACE_PRESERVE:
1150 return "preserve";
1151 default:
1152 return NULL;
1153 }
1154 }
1156 /**
1157 * Callback for write event.
1158 */
1159 static Inkscape::XML::Node *
1160 sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
1161 {
1162 if (!repr && (flags & SP_OBJECT_WRITE_BUILD)) {
1163 repr = SP_OBJECT_REPR(object)->duplicate(doc);
1164 if (!( flags & SP_OBJECT_WRITE_EXT )) {
1165 repr->setAttribute("inkscape:collect", NULL);
1166 }
1167 } else {
1168 repr->setAttribute("id", object->getId());
1170 if (object->xml_space.set) {
1171 char const *xml_space;
1172 xml_space = sp_xml_get_space_string(object->xml_space.value);
1173 repr->setAttribute("xml:space", xml_space);
1174 }
1176 if ( flags & SP_OBJECT_WRITE_EXT &&
1177 object->collectionPolicy() == SPObject::ALWAYS_COLLECT )
1178 {
1179 repr->setAttribute("inkscape:collect", "always");
1180 } else {
1181 repr->setAttribute("inkscape:collect", NULL);
1182 }
1184 SPStyle const *const obj_style = SP_OBJECT_STYLE(object);
1185 if (obj_style) {
1186 gchar *s = sp_style_write_string(obj_style, SP_STYLE_FLAG_IFSET);
1187 repr->setAttribute("style", ( *s ? s : NULL ));
1188 g_free(s);
1189 } else {
1190 /** \todo I'm not sure what to do in this case. Bug #1165868
1191 * suggests that it can arise, but the submitter doesn't know
1192 * how to do so reliably. The main two options are either
1193 * leave repr's style attribute unchanged, or explicitly clear it.
1194 * Must also consider what to do with property attributes for
1195 * the element; see below.
1196 */
1197 char const *style_str = repr->attribute("style");
1198 if (!style_str) {
1199 style_str = "NULL";
1200 }
1201 g_warning("Item's style is NULL; repr style attribute is %s", style_str);
1202 }
1204 /** \note We treat object->style as authoritative. Its effects have
1205 * been written to the style attribute above; any properties that are
1206 * unset we take to be deliberately unset (e.g. so that clones can
1207 * override the property).
1208 *
1209 * Note that the below has an undesirable consequence of changing the
1210 * appearance on renderers that lack CSS support (e.g. SVG tiny);
1211 * possibly we should write property attributes instead of a style
1212 * attribute.
1213 */
1214 sp_style_unset_property_attrs (object);
1215 }
1217 return repr;
1218 }
1220 /**
1221 * Update this object's XML node with flags value.
1222 */
1223 Inkscape::XML::Node *
1224 SPObject::updateRepr(unsigned int flags) {
1225 if (!SP_OBJECT_IS_CLONED(this)) {
1226 Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
1227 if (repr) {
1228 return updateRepr(repr->document(), repr, flags);
1229 } else {
1230 g_critical("Attempt to update non-existent repr");
1231 return NULL;
1232 }
1233 } else {
1234 /* cloned objects have no repr */
1235 return NULL;
1236 }
1237 }
1239 /** Used both to create reprs in the original document, and to create
1240 * reprs in another document (e.g. a temporary document used when
1241 * saving as "Plain SVG"
1242 */
1243 Inkscape::XML::Node *
1244 SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags) {
1245 g_assert(doc != NULL);
1247 if (SP_OBJECT_IS_CLONED(this)) {
1248 /* cloned objects have no repr */
1249 return NULL;
1250 }
1251 if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write) {
1252 if (!(flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1253 repr = SP_OBJECT_REPR(this);
1254 }
1255 return ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write(this, doc, repr, flags);
1256 } else {
1257 g_warning("Class %s does not implement ::write", G_OBJECT_TYPE_NAME(this));
1258 if (!repr) {
1259 if (flags & SP_OBJECT_WRITE_BUILD) {
1260 repr = SP_OBJECT_REPR(this)->duplicate(doc);
1261 }
1262 /// \todo FIXME: else probably error (Lauris) */
1263 } else {
1264 repr->mergeFrom(SP_OBJECT_REPR(this), "id");
1265 }
1266 return repr;
1267 }
1268 }
1270 /* Modification */
1272 /**
1273 * Add \a flags to \a object's as dirtiness flags, and
1274 * recursively add CHILD_MODIFIED flag to
1275 * parent and ancestors (as far up as necessary).
1276 */
1277 void
1278 SPObject::requestDisplayUpdate(unsigned int flags)
1279 {
1280 g_return_if_fail( this->document != NULL );
1282 if (update_in_progress) {
1283 g_print("WARNING: Requested update while update in progress, counter = %d\n", update_in_progress);
1284 }
1286 /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1287 * SP_OBJECT_CHILD_MODIFIED_FLAG */
1288 g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1289 g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1290 g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1292 bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1294 this->uflags |= flags;
1296 /* If requestModified has already been called on this object or one of its children, then we
1297 * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1298 */
1299 if (already_propagated) {
1300 SPObject *parent = SP_OBJECT_PARENT(this);
1301 if (parent) {
1302 parent->requestDisplayUpdate(SP_OBJECT_CHILD_MODIFIED_FLAG);
1303 } else {
1304 sp_document_request_modified(SP_OBJECT_DOCUMENT(this));
1305 }
1306 }
1307 }
1309 /**
1310 * Update views
1311 */
1312 void
1313 SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
1314 {
1315 g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1317 update_in_progress ++;
1319 #ifdef SP_OBJECT_DEBUG_CASCADE
1320 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);
1321 #endif
1323 /* Get this flags */
1324 flags |= this->uflags;
1325 /* Copy flags to modified cascade for later processing */
1326 this->mflags |= this->uflags;
1327 /* We have to clear flags here to allow rescheduling update */
1328 this->uflags = 0;
1330 // Merge style if we have good reasons to think that parent style is changed */
1331 /** \todo
1332 * I am not sure whether we should check only propagated
1333 * flag. We are currently assuming that style parsing is
1334 * done immediately. I think this is correct (Lauris).
1335 */
1336 if ((flags & SP_OBJECT_STYLE_MODIFIED_FLAG) && (flags & SP_OBJECT_PARENT_MODIFIED_FLAG)) {
1337 if (this->style && this->parent) {
1338 sp_style_merge_from_parent(this->style, this->parent->style);
1339 }
1340 }
1342 try
1343 {
1344 if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update)
1345 ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);
1346 }
1347 catch(...)
1348 {
1349 /** \todo
1350 * in case of catching an exception we need to inform the user somehow that the document is corrupted
1351 * maybe by implementing an document flag documentOk
1352 * or by a modal error dialog
1353 */
1354 g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
1355 }
1357 update_in_progress --;
1358 }
1360 /**
1361 * Request modified always bubbles *up* the tree, as opposed to
1362 * request display update, which trickles down and relies on the
1363 * flags set during this pass...
1364 */
1365 void
1366 SPObject::requestModified(unsigned int flags)
1367 {
1368 g_return_if_fail( this->document != NULL );
1370 /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1371 * SP_OBJECT_CHILD_MODIFIED_FLAG */
1372 g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1373 g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1374 g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1376 bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1378 this->mflags |= flags;
1380 /* If requestModified has already been called on this object or one of its children, then we
1381 * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1382 */
1383 if (already_propagated) {
1384 SPObject *parent=SP_OBJECT_PARENT(this);
1385 if (parent) {
1386 parent->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
1387 } else {
1388 sp_document_request_modified(SP_OBJECT_DOCUMENT(this));
1389 }
1390 }
1391 }
1393 /**
1394 * Emits the MODIFIED signal with the object's flags.
1395 * The object's mflags are the original set aside during the update pass for
1396 * later delivery here. Once emitModified() is called, those flags don't
1397 * need to be stored any longer.
1398 */
1399 void
1400 SPObject::emitModified(unsigned int flags)
1401 {
1402 /* only the MODIFIED_CASCADE flag is legal here */
1403 g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1405 #ifdef SP_OBJECT_DEBUG_CASCADE
1406 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);
1407 #endif
1409 flags |= this->mflags;
1410 /* We have to clear mflags beforehand, as signal handlers may
1411 * make changes and therefore queue new modification notifications
1412 * themselves. */
1413 this->mflags = 0;
1415 g_object_ref(G_OBJECT(this));
1416 SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
1417 if (klass->modified) {
1418 klass->modified(this, flags);
1419 }
1420 _modified_signal.emit(this, flags);
1421 g_object_unref(G_OBJECT(this));
1422 }
1424 gchar const *
1425 sp_object_tagName_get(SPObject const *object, SPException *ex)
1426 {
1427 /* If exception is not clear, return */
1428 if (!SP_EXCEPTION_IS_OK(ex)) {
1429 return NULL;
1430 }
1432 /// \todo fixme: Exception if object is NULL? */
1433 return object->repr->name();
1434 }
1436 gchar const *
1437 sp_object_getAttribute(SPObject const *object, gchar const *key, SPException *ex)
1438 {
1439 /* If exception is not clear, return */
1440 if (!SP_EXCEPTION_IS_OK(ex)) {
1441 return NULL;
1442 }
1444 /// \todo fixme: Exception if object is NULL? */
1445 return (gchar const *) object->repr->attribute(key);
1446 }
1448 void
1449 sp_object_setAttribute(SPObject *object, gchar const *key, gchar const *value, SPException *ex)
1450 {
1451 /* If exception is not clear, return */
1452 g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1454 /// \todo fixme: Exception if object is NULL? */
1455 object->repr->setAttribute(key, value, false);
1456 }
1458 void
1459 sp_object_removeAttribute(SPObject *object, gchar const *key, SPException *ex)
1460 {
1461 /* If exception is not clear, return */
1462 g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1464 /// \todo fixme: Exception if object is NULL? */
1465 object->repr->setAttribute(key, NULL, false);
1466 }
1468 /* Helper */
1470 static gchar *
1471 sp_object_get_unique_id(SPObject *object, gchar const *id)
1472 {
1473 static unsigned long count = 0;
1475 g_assert(SP_IS_OBJECT(object));
1477 count++;
1479 gchar const *name = object->repr->name();
1480 g_assert(name != NULL);
1482 gchar const *local = strchr(name, ':');
1483 if (local) {
1484 name = local + 1;
1485 }
1487 if (id != NULL) {
1488 if (object->document->getObjectById(id) == NULL) {
1489 return g_strdup(id);
1490 }
1491 }
1493 size_t const name_len = strlen(name);
1494 size_t const buflen = name_len + (sizeof(count) * 10 / 4) + 1;
1495 gchar *const buf = (gchar *) g_malloc(buflen);
1496 memcpy(buf, name, name_len);
1497 gchar *const count_buf = buf + name_len;
1498 size_t const count_buflen = buflen - name_len;
1499 do {
1500 ++count;
1501 g_snprintf(count_buf, count_buflen, "%lu", count);
1502 } while ( object->document->getObjectById(buf) != NULL );
1503 return buf;
1504 }
1506 /* Style */
1508 /**
1509 * Returns an object style property.
1510 *
1511 * \todo
1512 * fixme: Use proper CSS parsing. The current version is buggy
1513 * in a number of situations where key is a substring of the
1514 * style string other than as a property name (including
1515 * where key is a substring of a property name), and is also
1516 * buggy in its handling of inheritance for properties that
1517 * aren't inherited by default. It also doesn't allow for
1518 * the case where the property is specified but with an invalid
1519 * value (in which case I believe the CSS2 error-handling
1520 * behaviour applies, viz. behave as if the property hadn't
1521 * been specified). Also, the current code doesn't use CRSelEng
1522 * stuff to take a value from stylesheets. Also, we aren't
1523 * setting any hooks to force an update for changes in any of
1524 * the inputs (i.e., in any of the elements that this function
1525 * queries).
1526 *
1527 * \par
1528 * Given that the default value for a property depends on what
1529 * property it is (e.g., whether to inherit or not), and given
1530 * the above comment about ignoring invalid values, and that the
1531 * repr parent isn't necessarily the right element to inherit
1532 * from (e.g., maybe we need to inherit from the referencing
1533 * <use> element instead), we should probably make the caller
1534 * responsible for ascending the repr tree as necessary.
1535 */
1536 gchar const *
1537 sp_object_get_style_property(SPObject const *object, gchar const *key, gchar const *def)
1538 {
1539 g_return_val_if_fail(object != NULL, NULL);
1540 g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
1541 g_return_val_if_fail(key != NULL, NULL);
1543 gchar const *style = object->repr->attribute("style");
1544 if (style) {
1545 size_t const len = strlen(key);
1546 char const *p;
1547 while ( (p = strstr(style, key))
1548 != NULL )
1549 {
1550 p += len;
1551 while ((*p <= ' ') && *p) p++;
1552 if (*p++ != ':') break;
1553 while ((*p <= ' ') && *p) p++;
1554 size_t const inherit_len = sizeof("inherit") - 1;
1555 if (*p
1556 && !(strneq(p, "inherit", inherit_len)
1557 && (p[inherit_len] == '\0'
1558 || p[inherit_len] == ';'
1559 || g_ascii_isspace(p[inherit_len])))) {
1560 return p;
1561 }
1562 }
1563 }
1564 gchar const *val = object->repr->attribute(key);
1565 if (val && !streq(val, "inherit")) {
1566 return val;
1567 }
1568 if (object->parent) {
1569 return sp_object_get_style_property(object->parent, key, def);
1570 }
1572 return def;
1573 }
1575 /**
1576 * Lifts SVG version of all root objects to version.
1577 */
1578 void
1579 SPObject::_requireSVGVersion(Inkscape::Version version) {
1580 for ( SPObject::ParentIterator iter=this ; iter ; ++iter ) {
1581 SPObject *object=iter;
1582 if (SP_IS_ROOT(object)) {
1583 SPRoot *root=SP_ROOT(object);
1584 if ( root->version.svg < version ) {
1585 root->version.svg = version;
1586 }
1587 }
1588 }
1589 }
1591 /**
1592 * Returns previous object in sibling list or NULL.
1593 */
1594 SPObject *
1595 sp_object_prev(SPObject *child)
1596 {
1597 SPObject *parent = SP_OBJECT_PARENT(child);
1598 for ( SPObject *i = sp_object_first_child(parent); i; i = SP_OBJECT_NEXT(i) ) {
1599 if (SP_OBJECT_NEXT(i) == child)
1600 return i;
1601 }
1602 return NULL;
1603 }
1605 /* Titles and descriptions */
1607 /* Note:
1608 Titles and descriptions are stored in 'title' and 'desc' child elements
1609 (see section 5.4 of the SVG 1.0 and 1.1 specifications). The spec allows
1610 an element to have more than one 'title' child element, but strongly
1611 recommends against this and requires using the first one if a choice must
1612 be made. The same applies to 'desc' elements. Therefore, these functions
1613 ignore all but the first 'title' child element and first 'desc' child
1614 element, except when deleting a title or description.
1615 */
1617 /**
1618 * Returns the title of this object, or NULL if there is none.
1619 * The caller must free the returned string using g_free() - see comment
1620 * for getTitleOrDesc() below.
1621 */
1622 gchar *
1623 SPObject::title() const
1624 {
1625 return getTitleOrDesc("svg:title");
1626 }
1628 /**
1629 * Sets the title of this object
1630 * A NULL first argument is interpreted as meaning that the existing title
1631 * (if any) should be deleted.
1632 * The second argument is optional - see setTitleOrDesc() below for details.
1633 */
1634 bool
1635 SPObject::setTitle(gchar const *title, bool verbatim)
1636 {
1637 return setTitleOrDesc(title, "svg:title", verbatim);
1638 }
1640 /**
1641 * Returns the description of this object, or NULL if there is none.
1642 * The caller must free the returned string using g_free() - see comment
1643 * for getTitleOrDesc() below.
1644 */
1645 gchar *
1646 SPObject::desc() const
1647 {
1648 return getTitleOrDesc("svg:desc");
1649 }
1651 /**
1652 * Sets the description of this object.
1653 * A NULL first argument is interpreted as meaning that the existing
1654 * description (if any) should be deleted.
1655 * The second argument is optional - see setTitleOrDesc() below for details.
1656 */
1657 bool
1658 SPObject::setDesc(gchar const *desc, bool verbatim)
1659 {
1660 return setTitleOrDesc(desc, "svg:desc", verbatim);
1661 }
1663 /**
1664 * Returns the title or description of this object, or NULL if there is none.
1665 *
1666 * The SVG spec allows 'title' and 'desc' elements to contain text marked up
1667 * using elements from other namespaces. Therefore, this function cannot
1668 * in general just return a pointer to an existing string - it must instead
1669 * construct a string containing the title or description without the mark-up.
1670 * Consequently, the return value is a newly allocated string (or NULL), and
1671 * must be freed (using g_free()) by the caller.
1672 */
1673 gchar *
1674 SPObject::getTitleOrDesc(gchar const *svg_tagname) const
1675 {
1676 SPObject *elem = findFirstChild(svg_tagname);
1677 if (elem == NULL) return NULL;
1678 return g_string_free(elem->textualContent(), FALSE);
1679 }
1681 /**
1682 * Sets or deletes the title or description of this object.
1683 * A NULL 'value' argument causes the title or description to be deleted.
1684 *
1685 * 'verbatim' parameter:
1686 * If verbatim==true, then the title or description is set to exactly the
1687 * specified value. If verbatim==false then two exceptions are made:
1688 * (1) If the specified value is just whitespace, then the title/description
1689 * is deleted.
1690 * (2) If the specified value is the same as the current value except for
1691 * mark-up, then the current value is left unchanged.
1692 * This is usually the desired behaviour, so 'verbatim' defaults to false for
1693 * setTitle() and setDesc().
1694 *
1695 * The return value is true if a change was made to the title/description,
1696 * and usually false otherwise.
1697 */
1698 bool
1699 SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim)
1700 {
1701 if (!verbatim) {
1702 // If the new title/description is just whitespace,
1703 // treat it as though it were NULL.
1704 if (value) {
1705 bool just_whitespace = true;
1706 for (const gchar *cp = value; *cp; ++cp) {
1707 if (!std::strchr("\r\n \t", *cp)) {
1708 just_whitespace = false;
1709 break;
1710 }
1711 }
1712 if (just_whitespace) value = NULL;
1713 }
1714 // Don't stomp on mark-up if there is no real change.
1715 if (value) {
1716 gchar *current_value = getTitleOrDesc(svg_tagname);
1717 if (current_value) {
1718 bool different = std::strcmp(current_value, value);
1719 g_free(current_value);
1720 if (!different) return false;
1721 }
1722 }
1723 }
1725 SPObject *elem = findFirstChild(svg_tagname);
1727 if (value == NULL) {
1728 if (elem == NULL) return false;
1729 // delete the title/description(s)
1730 while (elem) {
1731 elem->deleteObject();
1732 elem = findFirstChild(svg_tagname);
1733 }
1734 return true;
1735 }
1737 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
1739 if (elem == NULL) {
1740 // create a new 'title' or 'desc' element, putting it at the
1741 // beginning (in accordance with the spec's recommendations)
1742 Inkscape::XML::Node *xml_elem = xml_doc->createElement(svg_tagname);
1743 repr->addChild(xml_elem, NULL);
1744 elem = document->getObjectByRepr(xml_elem);
1745 Inkscape::GC::release(xml_elem);
1746 }
1747 else {
1748 // remove the current content of the 'text' or 'desc' element
1749 SPObject *child;
1750 while (NULL != (child = elem->firstChild())) child->deleteObject();
1751 }
1753 // add the new content
1754 elem->appendChildRepr(xml_doc->createTextNode(value));
1755 return true;
1756 }
1758 /**
1759 * Find the first child of this object with a given tag name,
1760 * and return it. Returns NULL if there is no matching child.
1761 */
1762 SPObject *
1763 SPObject::findFirstChild(gchar const *tagname) const
1764 {
1765 for (SPObject *child = children; child; child = child->next)
1766 {
1767 if (child->repr->type() == Inkscape::XML::ELEMENT_NODE &&
1768 !strcmp(child->repr->name(), tagname)) return child;
1769 }
1770 return NULL;
1771 }
1773 /**
1774 * Return the full textual content of an element (typically all the
1775 * content except the tags).
1776 * Must not be used on anything except elements.
1777 */
1778 GString*
1779 SPObject::textualContent() const
1780 {
1781 GString* text = g_string_new("");
1783 for (const SPObject *child = firstChild(); child; child = child->next)
1784 {
1785 Inkscape::XML::NodeType child_type = child->repr->type();
1787 if (child_type == Inkscape::XML::ELEMENT_NODE) {
1788 GString * new_text = child->textualContent();
1789 g_string_append(text, new_text->str);
1790 g_string_free(new_text, TRUE);
1791 }
1792 else if (child_type == Inkscape::XML::TEXT_NODE) {
1793 g_string_append(text, child->repr->content());
1794 }
1795 }
1796 return text;
1797 }
1799 /*
1800 Local Variables:
1801 mode:c++
1802 c-file-style:"stroustrup"
1803 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1804 indent-tabs-mode:nil
1805 fill-column:99
1806 End:
1807 */
1808 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :