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