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