3b0056b41c9cdf7a6431dd3fca1b98d34923cf9d
1 #define __SP_OBJECT_C__
2 /** \file
3 * SPObject implementation.
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * bulia byak <buliabyak@users.sf.net>
8 * Stephen Silver <sasilver@users.sourceforge.net>
9 *
10 * Copyright (C) 1999-2008 authors
11 * Copyright (C) 2001-2002 Ximian, Inc.
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 /** \class SPObject
17 *
18 * SPObject is an abstract base class of all of the document nodes at the
19 * SVG document level. Each SPObject subclass implements a certain SVG
20 * element node type, or is an abstract base class for different node
21 * types. The SPObject layer is bound to the SPRepr layer, closely
22 * following the SPRepr mutations via callbacks. During creation,
23 * SPObject parses and interprets all textual attributes and CSS style
24 * strings of the SPRepr, and later updates the internal state whenever
25 * it receives a signal about a change. The opposite is not true - there
26 * are methods manipulating SPObjects directly and such changes do not
27 * propagate to the SPRepr layer. This is important for implementation of
28 * the undo stack, animations and other features.
29 *
30 * SPObjects are bound to the higher-level container SPDocument, which
31 * provides document level functionality such as the undo stack,
32 * dictionary and so on. Source: doc/architecture.txt
33 */
35 #include <cstring>
36 #include <string>
38 #include "helper/sp-marshal.h"
39 #include "xml/node-event-vector.h"
40 #include "attributes.h"
41 #include "document.h"
42 #include "style.h"
43 #include "sp-object-repr.h"
44 #include "sp-root.h"
45 #include "sp-style-elem.h"
46 #include "sp-script.h"
47 #include "streq.h"
48 #include "strneq.h"
49 #include "xml/repr.h"
50 #include "xml/node-fns.h"
51 #include "debug/event-tracker.h"
52 #include "debug/simple-event.h"
53 #include "debug/demangle.h"
54 #include "util/share.h"
55 #include "util/format.h"
56 #include "util/longest-common-suffix.h"
58 using std::memcpy;
59 using std::strchr;
60 using std::strcmp;
61 using std::strlen;
62 using std::strstr;
64 #define noSP_OBJECT_DEBUG_CASCADE
66 #define noSP_OBJECT_DEBUG
68 #ifdef SP_OBJECT_DEBUG
69 # define debug(f, a...) { g_print("%s(%d) %s:", \
70 __FILE__,__LINE__,__FUNCTION__); \
71 g_print(f, ## a); \
72 g_print("\n"); \
73 }
74 #else
75 # define debug(f, a...) /**/
76 #endif
78 static void sp_object_class_init(SPObjectClass *klass);
79 static void sp_object_init(SPObject *object);
80 static void sp_object_finalize(GObject *object);
82 static void sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref);
83 static void sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child);
84 static void sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *old_ref, Inkscape::XML::Node *new_ref);
86 static void sp_object_release(SPObject *object);
87 static void sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
89 static void sp_object_private_set(SPObject *object, unsigned int key, gchar const *value);
90 static Inkscape::XML::Node *sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
92 /* Real handlers of repr signals */
94 static void sp_object_repr_attr_changed(Inkscape::XML::Node *repr, gchar const *key, gchar const *oldval, gchar const *newval, bool is_interactive, gpointer data);
96 static void sp_object_repr_content_changed(Inkscape::XML::Node *repr, gchar const *oldcontent, gchar const *newcontent, gpointer data);
98 static void sp_object_repr_child_added(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data);
99 static void sp_object_repr_child_removed(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, void *data);
101 static void sp_object_repr_order_changed(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data);
103 static gchar *sp_object_get_unique_id(SPObject *object, gchar const *defid);
105 guint update_in_progress = 0; // guard against update-during-update
107 Inkscape::XML::NodeEventVector object_event_vector = {
108 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 SPObject::requestOrphanCollection() {
543 g_return_if_fail(document != NULL);
545 // do not remove style or script elements (Bug #276244)
546 if (SP_IS_STYLE_ELEM(this)) {
547 // leave it
548 } else if (SP_IS_SCRIPT(this)) {
549 // leave it
550 } else if (SP_IS_PAINT_SERVER(this) && static_cast<SPPaintServer*>(this)->isSwatch() ) {
551 // leave it
552 } else {
553 document->queueForOrphanCollection(this);
555 /** \todo
556 * This is a temporary hack added to make fill&stroke rebuild its
557 * gradient list when the defs are vacuumed. gradient-vector.cpp
558 * listens to the modified signal on defs, and now we give it that
559 * signal. Mental says that this should be made automatic by
560 * merging SPObjectGroup with SPObject; SPObjectGroup would issue
561 * this signal automatically. Or maybe just derive SPDefs from
562 * SPObjectGroup?
563 */
565 this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
566 }
567 }
569 /** Sends the delete signal to all children of this object recursively */
570 void
571 SPObject::_sendDeleteSignalRecursive() {
572 for (SPObject *child = sp_object_first_child(this); child; child = SP_OBJECT_NEXT(child)) {
573 child->_delete_signal.emit(child);
574 child->_sendDeleteSignalRecursive();
575 }
576 }
578 /**
579 * Deletes the object reference, unparenting it from its parent.
580 *
581 * If the \a propagate parameter is set to true, it emits a delete
582 * signal. If the \a propagate_descendants parameter is true, it
583 * recursively sends the delete signal to children.
584 */
585 void
586 SPObject::deleteObject(bool propagate, bool propagate_descendants)
587 {
588 sp_object_ref(this, NULL);
589 if (propagate) {
590 _delete_signal.emit(this);
591 }
592 if (propagate_descendants) {
593 this->_sendDeleteSignalRecursive();
594 }
596 Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
597 if (repr && sp_repr_parent(repr)) {
598 sp_repr_unparent(repr);
599 }
601 if (_successor) {
602 _successor->deleteObject(propagate, propagate_descendants);
603 }
604 sp_object_unref(this, NULL);
605 }
607 /**
608 * Put object into object tree, under parent, and behind prev;
609 * also update object's XML space.
610 */
611 void
612 sp_object_attach(SPObject *parent, SPObject *object, SPObject *prev)
613 {
614 g_return_if_fail(parent != NULL);
615 g_return_if_fail(SP_IS_OBJECT(parent));
616 g_return_if_fail(object != NULL);
617 g_return_if_fail(SP_IS_OBJECT(object));
618 g_return_if_fail(!prev || SP_IS_OBJECT(prev));
619 g_return_if_fail(!prev || prev->parent == parent);
620 g_return_if_fail(!object->parent);
622 sp_object_ref(object, parent);
623 object->parent = parent;
624 parent->_updateTotalHRefCount(object->_total_hrefcount);
626 SPObject *next;
627 if (prev) {
628 next = prev->next;
629 prev->next = object;
630 } else {
631 next = parent->children;
632 parent->children = object;
633 }
634 object->next = next;
635 if (!next) {
636 parent->_last_child = object;
637 }
638 if (!object->xml_space.set)
639 object->xml_space.value = parent->xml_space.value;
640 }
642 /**
643 * In list of object's siblings, move object behind prev.
644 */
645 void
646 sp_object_reorder(SPObject *object, SPObject *prev) {
647 g_return_if_fail(object != NULL);
648 g_return_if_fail(SP_IS_OBJECT(object));
649 g_return_if_fail(object->parent != NULL);
650 g_return_if_fail(object != prev);
651 g_return_if_fail(!prev || SP_IS_OBJECT(prev));
652 g_return_if_fail(!prev || prev->parent == object->parent);
654 SPObject *const parent=object->parent;
656 SPObject *old_prev=NULL;
657 for ( SPObject *child = parent->children ; child && child != object ;
658 child = child->next )
659 {
660 old_prev = child;
661 }
663 SPObject *next=object->next;
664 if (old_prev) {
665 old_prev->next = next;
666 } else {
667 parent->children = next;
668 }
669 if (!next) {
670 parent->_last_child = old_prev;
671 }
672 if (prev) {
673 next = prev->next;
674 prev->next = object;
675 } else {
676 next = parent->children;
677 parent->children = object;
678 }
679 object->next = next;
680 if (!next) {
681 parent->_last_child = object;
682 }
683 }
685 /**
686 * Remove object from parent's children, release and unref it.
687 */
688 void
689 sp_object_detach(SPObject *parent, SPObject *object) {
690 g_return_if_fail(parent != NULL);
691 g_return_if_fail(SP_IS_OBJECT(parent));
692 g_return_if_fail(object != NULL);
693 g_return_if_fail(SP_IS_OBJECT(object));
694 g_return_if_fail(object->parent == parent);
696 object->releaseReferences();
698 SPObject *prev=NULL;
699 for ( SPObject *child = parent->children ; child && child != object ;
700 child = child->next )
701 {
702 prev = child;
703 }
705 SPObject *next=object->next;
706 if (prev) {
707 prev->next = next;
708 } else {
709 parent->children = next;
710 }
711 if (!next) {
712 parent->_last_child = prev;
713 }
715 object->next = NULL;
716 object->parent = NULL;
718 parent->_updateTotalHRefCount(-object->_total_hrefcount);
719 sp_object_unref(object, parent);
720 }
722 /**
723 * Return object's child whose node pointer equals repr.
724 */
725 SPObject *
726 sp_object_get_child_by_repr(SPObject *object, Inkscape::XML::Node *repr)
727 {
728 g_return_val_if_fail(object != NULL, NULL);
729 g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
730 g_return_val_if_fail(repr != NULL, NULL);
732 if (object->_last_child && SP_OBJECT_REPR(object->_last_child) == repr)
733 return object->_last_child; // optimization for common scenario
734 for ( SPObject *child = object->children ; child ; child = child->next ) {
735 if ( SP_OBJECT_REPR(child) == repr ) {
736 return child;
737 }
738 }
740 return NULL;
741 }
743 /**
744 * Callback for child_added event.
745 * Invoked whenever the given mutation event happens in the XML tree.
746 */
747 static void
748 sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
749 {
750 GType type = sp_repr_type_lookup(child);
751 if (!type) {
752 return;
753 }
754 SPObject *ochild = SP_OBJECT(g_object_new(type, 0));
755 SPObject *prev = ref ? sp_object_get_child_by_repr(object, ref) : NULL;
756 sp_object_attach(object, ochild, prev);
757 sp_object_unref(ochild, NULL);
759 sp_object_invoke_build(ochild, object->document, child, SP_OBJECT_IS_CLONED(object));
760 }
762 /**
763 * Removes, releases and unrefs all children of object.
764 *
765 * This is the opposite of build. It has to be invoked as soon as the
766 * object is removed from the tree, even if it is still alive according
767 * to reference count. The frontend unregisters the object from the
768 * document and releases the SPRepr bindings; implementations should free
769 * state data and release all child objects. Invoking release on
770 * SPRoot destroys the whole document tree.
771 * \see sp_object_build()
772 */
773 static void sp_object_release(SPObject *object)
774 {
775 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
776 while (object->children) {
777 sp_object_detach(object, object->children);
778 }
779 }
781 /**
782 * Remove object's child whose node equals repr, release and
783 * unref it.
784 *
785 * Invoked whenever the given mutation event happens in the XML
786 * tree, BEFORE removal from the XML tree happens, so grouping
787 * objects can safely release the child data.
788 */
789 static void
790 sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child)
791 {
792 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
793 SPObject *ochild = sp_object_get_child_by_repr(object, child);
794 g_return_if_fail (ochild != NULL || !strcmp("comment", child->name())); // comments have no objects
795 if (ochild)
796 sp_object_detach(object, ochild);
797 }
799 /**
800 * Move object corresponding to child after sibling object corresponding
801 * to new_ref.
802 * Invoked whenever the given mutation event happens in the XML tree.
803 * \param old_ref Ignored
804 */
805 static void sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node */*old_ref*/,
806 Inkscape::XML::Node *new_ref)
807 {
808 SPObject *ochild = sp_object_get_child_by_repr(object, child);
809 g_return_if_fail(ochild != NULL);
810 SPObject *prev = new_ref ? sp_object_get_child_by_repr(object, new_ref) : NULL;
811 sp_object_reorder(ochild, prev);
812 ochild->_position_changed_signal.emit(ochild);
813 }
815 /**
816 * Virtual build callback.
817 *
818 * This has to be invoked immediately after creation of an SPObject. The
819 * frontend method ensures that the new object is properly attached to
820 * the document and repr; implementation then will parse all of the attributes,
821 * generate the children objects and so on. Invoking build on the SPRoot
822 * object results in creation of the whole document tree (this is, what
823 * SPDocument does after the creation of the XML tree).
824 * \see sp_object_release()
825 */
826 static void
827 sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
828 {
829 /* Nothing specific here */
830 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
832 sp_object_read_attr(object, "xml:space");
833 sp_object_read_attr(object, "inkscape:label");
834 sp_object_read_attr(object, "inkscape:collect");
836 for (Inkscape::XML::Node *rchild = repr->firstChild() ; rchild != NULL; rchild = rchild->next()) {
837 GType type = sp_repr_type_lookup(rchild);
838 if (!type) {
839 continue;
840 }
841 SPObject *child = SP_OBJECT(g_object_new(type, 0));
842 sp_object_attach(object, child, object->lastChild());
843 sp_object_unref(child, NULL);
844 sp_object_invoke_build(child, document, rchild, SP_OBJECT_IS_CLONED(object));
845 }
846 }
848 void sp_object_invoke_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
849 {
850 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
852 g_assert(object != NULL);
853 g_assert(SP_IS_OBJECT(object));
854 g_assert(document != NULL);
855 g_assert(repr != NULL);
857 g_assert(object->document == NULL);
858 g_assert(object->repr == NULL);
859 g_assert(object->getId() == NULL);
861 /* Bookkeeping */
863 object->document = document;
864 object->repr = repr;
865 if (!cloned)
866 Inkscape::GC::anchor(repr);
867 object->cloned = cloned;
869 if (!SP_OBJECT_IS_CLONED(object)) {
870 object->document->bindObjectToRepr(object->repr, object);
872 if (Inkscape::XML::id_permitted(object->repr)) {
873 /* If we are not cloned, and not seeking, force unique id */
874 gchar const *id = object->repr->attribute("id");
875 if (!document->isSeeking()) {
876 {
877 gchar *realid = sp_object_get_unique_id(object, id);
878 g_assert(realid != NULL);
880 object->document->bindObjectToId(realid, object);
881 SPObjectImpl::setId(object, realid);
882 g_free(realid);
883 }
885 /* Redefine ID, if required */
886 if ((id == NULL) || (strcmp(id, object->getId()) != 0)) {
887 object->repr->setAttribute("id", object->getId());
888 }
889 } else if (id) {
890 // bind if id, but no conflict -- otherwise, we can expect
891 // a subsequent setting of the id attribute
892 if (!object->document->getObjectById(id)) {
893 object->document->bindObjectToId(id, object);
894 SPObjectImpl::setId(object, id);
895 }
896 }
897 }
898 } else {
899 g_assert(object->getId() == NULL);
900 }
902 /* Invoke derived methods, if any */
903 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->build) {
904 (*((SPObjectClass *) G_OBJECT_GET_CLASS(object))->build)(object, document, repr);
905 }
907 /* Signalling (should be connected AFTER processing derived methods */
908 sp_repr_add_listener(repr, &object_event_vector, object);
909 }
911 void SPObject::releaseReferences() {
912 g_assert(this->document);
913 g_assert(this->repr);
915 sp_repr_remove_listener_by_data(this->repr, this);
917 this->_release_signal.emit(this);
918 SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
919 if (klass->release) {
920 klass->release(this);
921 }
923 /* all hrefs should be released by the "release" handlers */
924 g_assert(this->hrefcount == 0);
926 if (!SP_OBJECT_IS_CLONED(this)) {
927 if (this->id) {
928 this->document->bindObjectToId(this->id, NULL);
929 }
930 g_free(this->id);
931 this->id = NULL;
933 g_free(this->_default_label);
934 this->_default_label = NULL;
936 this->document->bindObjectToRepr(this->repr, NULL);
938 Inkscape::GC::release(this->repr);
939 } else {
940 g_assert(!this->id);
941 }
943 if (this->style) {
944 this->style = sp_style_unref(this->style);
945 }
947 this->document = NULL;
948 this->repr = NULL;
949 }
952 SPObject *SPObject::getNext()
953 {
954 return next;
955 }
957 SPObject *SPObject::getPrev()
958 {
959 return sp_object_prev(this);
960 }
962 /**
963 * Callback for child_added node event.
964 */
965 static void
966 sp_object_repr_child_added(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data)
967 {
968 SPObject *object = SP_OBJECT(data);
970 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->child_added)
971 (*((SPObjectClass *)G_OBJECT_GET_CLASS(object))->child_added)(object, child, ref);
972 }
974 /**
975 * Callback for remove_child node event.
976 */
977 static void
978 sp_object_repr_child_removed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node */*ref*/, gpointer data)
979 {
980 SPObject *object = SP_OBJECT(data);
982 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->remove_child) {
983 (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->remove_child)(object, child);
984 }
985 }
987 /**
988 * Callback for order_changed node event.
989 *
990 * \todo fixme:
991 */
992 static void
993 sp_object_repr_order_changed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data)
994 {
995 SPObject *object = SP_OBJECT(data);
997 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->order_changed) {
998 (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->order_changed)(object, child, old, newer);
999 }
1000 }
1002 /**
1003 * Callback for set event.
1004 */
1005 static void
1006 sp_object_private_set(SPObject *object, unsigned int key, gchar const *value)
1007 {
1008 g_assert(key != SP_ATTR_INVALID);
1010 switch (key) {
1011 case SP_ATTR_ID:
1012 if ( !SP_OBJECT_IS_CLONED(object) && object->repr->type() == Inkscape::XML::ELEMENT_NODE ) {
1013 SPDocument *document=object->document;
1014 SPObject *conflict=NULL;
1016 gchar const *new_id = value;
1018 if (new_id) {
1019 conflict = document->getObjectById((char const *)new_id);
1020 }
1022 if ( conflict && conflict != object ) {
1023 if (!document->isSeeking()) {
1024 sp_object_ref(conflict, NULL);
1025 // give the conflicting object a new ID
1026 gchar *new_conflict_id = sp_object_get_unique_id(conflict, NULL);
1027 SP_OBJECT_REPR(conflict)->setAttribute("id", new_conflict_id);
1028 g_free(new_conflict_id);
1029 sp_object_unref(conflict, NULL);
1030 } else {
1031 new_id = NULL;
1032 }
1033 }
1035 if (object->getId()) {
1036 document->bindObjectToId(object->getId(), NULL);
1037 SPObjectImpl::setId(object, 0);
1038 }
1040 if (new_id) {
1041 SPObjectImpl::setId(object, new_id);
1042 document->bindObjectToId(object->getId(), object);
1043 }
1045 g_free(object->_default_label);
1046 object->_default_label = NULL;
1047 }
1048 break;
1049 case SP_ATTR_INKSCAPE_LABEL:
1050 g_free(object->_label);
1051 if (value) {
1052 object->_label = g_strdup(value);
1053 } else {
1054 object->_label = NULL;
1055 }
1056 g_free(object->_default_label);
1057 object->_default_label = NULL;
1058 break;
1059 case SP_ATTR_INKSCAPE_COLLECT:
1060 if ( value && !strcmp(value, "always") ) {
1061 object->setCollectionPolicy(SPObject::ALWAYS_COLLECT);
1062 } else {
1063 object->setCollectionPolicy(SPObject::COLLECT_WITH_PARENT);
1064 }
1065 break;
1066 case SP_ATTR_XML_SPACE:
1067 if (value && !strcmp(value, "preserve")) {
1068 object->xml_space.value = SP_XML_SPACE_PRESERVE;
1069 object->xml_space.set = TRUE;
1070 } else if (value && !strcmp(value, "default")) {
1071 object->xml_space.value = SP_XML_SPACE_DEFAULT;
1072 object->xml_space.set = TRUE;
1073 } else if (object->parent) {
1074 SPObject *parent;
1075 parent = object->parent;
1076 object->xml_space.value = parent->xml_space.value;
1077 }
1078 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1079 break;
1080 case SP_ATTR_STYLE:
1081 sp_style_read_from_object(object->style, object);
1082 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1083 break;
1084 default:
1085 break;
1086 }
1087 }
1089 /**
1090 * Call virtual set() function of object.
1091 */
1092 void
1093 sp_object_set(SPObject *object, unsigned int key, gchar const *value)
1094 {
1095 g_assert(object != NULL);
1096 g_assert(SP_IS_OBJECT(object));
1098 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->set) {
1099 ((SPObjectClass *) G_OBJECT_GET_CLASS(object))->set(object, key, value);
1100 }
1101 }
1103 /**
1104 * Read value of key attribute from XML node into object.
1105 */
1106 void
1107 sp_object_read_attr(SPObject *object, gchar const *key)
1108 {
1109 g_assert(object != NULL);
1110 g_assert(SP_IS_OBJECT(object));
1111 g_assert(key != NULL);
1113 g_assert(object->repr != NULL);
1115 unsigned int keyid = sp_attribute_lookup(key);
1116 if (keyid != SP_ATTR_INVALID) {
1117 /* Retrieve the 'key' attribute from the object's XML representation */
1118 gchar const *value = object->repr->attribute(key);
1120 sp_object_set(object, keyid, value);
1121 }
1122 }
1124 /**
1125 * Callback for attr_changed node event.
1126 */
1127 static void
1128 sp_object_repr_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *key, gchar const */*oldval*/, gchar const */*newval*/, bool is_interactive, gpointer data)
1129 {
1130 SPObject *object = SP_OBJECT(data);
1132 sp_object_read_attr(object, key);
1134 // manual changes to extension attributes require the normal
1135 // attributes, which depend on them, to be updated immediately
1136 if (is_interactive) {
1137 object->updateRepr(0);
1138 }
1139 }
1141 /**
1142 * Callback for content_changed node event.
1143 */
1144 static void
1145 sp_object_repr_content_changed(Inkscape::XML::Node */*repr*/, gchar const */*oldcontent*/, gchar const */*newcontent*/, gpointer data)
1146 {
1147 SPObject *object = SP_OBJECT(data);
1149 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)
1150 (*((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)(object);
1151 }
1153 /**
1154 * Return string representation of space value.
1155 */
1156 static gchar const*
1157 sp_xml_get_space_string(unsigned int space)
1158 {
1159 switch (space) {
1160 case SP_XML_SPACE_DEFAULT:
1161 return "default";
1162 case SP_XML_SPACE_PRESERVE:
1163 return "preserve";
1164 default:
1165 return NULL;
1166 }
1167 }
1169 /**
1170 * Callback for write event.
1171 */
1172 static Inkscape::XML::Node *
1173 sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
1174 {
1175 if (!repr && (flags & SP_OBJECT_WRITE_BUILD)) {
1176 repr = SP_OBJECT_REPR(object)->duplicate(doc);
1177 if (!( flags & SP_OBJECT_WRITE_EXT )) {
1178 repr->setAttribute("inkscape:collect", NULL);
1179 }
1180 } else {
1181 repr->setAttribute("id", object->getId());
1183 if (object->xml_space.set) {
1184 char const *xml_space;
1185 xml_space = sp_xml_get_space_string(object->xml_space.value);
1186 repr->setAttribute("xml:space", xml_space);
1187 }
1189 if ( flags & SP_OBJECT_WRITE_EXT &&
1190 object->collectionPolicy() == SPObject::ALWAYS_COLLECT )
1191 {
1192 repr->setAttribute("inkscape:collect", "always");
1193 } else {
1194 repr->setAttribute("inkscape:collect", NULL);
1195 }
1197 SPStyle const *const obj_style = SP_OBJECT_STYLE(object);
1198 if (obj_style) {
1199 gchar *s = sp_style_write_string(obj_style, SP_STYLE_FLAG_IFSET);
1200 repr->setAttribute("style", ( *s ? s : NULL ));
1201 g_free(s);
1202 } else {
1203 /** \todo I'm not sure what to do in this case. Bug #1165868
1204 * suggests that it can arise, but the submitter doesn't know
1205 * how to do so reliably. The main two options are either
1206 * leave repr's style attribute unchanged, or explicitly clear it.
1207 * Must also consider what to do with property attributes for
1208 * the element; see below.
1209 */
1210 char const *style_str = repr->attribute("style");
1211 if (!style_str) {
1212 style_str = "NULL";
1213 }
1214 g_warning("Item's style is NULL; repr style attribute is %s", style_str);
1215 }
1217 /** \note We treat object->style as authoritative. Its effects have
1218 * been written to the style attribute above; any properties that are
1219 * unset we take to be deliberately unset (e.g. so that clones can
1220 * override the property).
1221 *
1222 * Note that the below has an undesirable consequence of changing the
1223 * appearance on renderers that lack CSS support (e.g. SVG tiny);
1224 * possibly we should write property attributes instead of a style
1225 * attribute.
1226 */
1227 sp_style_unset_property_attrs (object);
1228 }
1230 return repr;
1231 }
1233 /**
1234 * Update this object's XML node with flags value.
1235 */
1236 Inkscape::XML::Node *
1237 SPObject::updateRepr(unsigned int flags) {
1238 if (!SP_OBJECT_IS_CLONED(this)) {
1239 Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
1240 if (repr) {
1241 return updateRepr(repr->document(), repr, flags);
1242 } else {
1243 g_critical("Attempt to update non-existent repr");
1244 return NULL;
1245 }
1246 } else {
1247 /* cloned objects have no repr */
1248 return NULL;
1249 }
1250 }
1252 /** Used both to create reprs in the original document, and to create
1253 * reprs in another document (e.g. a temporary document used when
1254 * saving as "Plain SVG"
1255 */
1256 Inkscape::XML::Node *
1257 SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags) {
1258 g_assert(doc != NULL);
1260 if (SP_OBJECT_IS_CLONED(this)) {
1261 /* cloned objects have no repr */
1262 return NULL;
1263 }
1264 if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write) {
1265 if (!(flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1266 repr = SP_OBJECT_REPR(this);
1267 }
1268 return ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write(this, doc, repr, flags);
1269 } else {
1270 g_warning("Class %s does not implement ::write", G_OBJECT_TYPE_NAME(this));
1271 if (!repr) {
1272 if (flags & SP_OBJECT_WRITE_BUILD) {
1273 repr = SP_OBJECT_REPR(this)->duplicate(doc);
1274 }
1275 /// \todo FIXME: else probably error (Lauris) */
1276 } else {
1277 repr->mergeFrom(SP_OBJECT_REPR(this), "id");
1278 }
1279 return repr;
1280 }
1281 }
1283 /* Modification */
1285 /**
1286 * Add \a flags to \a object's as dirtiness flags, and
1287 * recursively add CHILD_MODIFIED flag to
1288 * parent and ancestors (as far up as necessary).
1289 */
1290 void
1291 SPObject::requestDisplayUpdate(unsigned int flags)
1292 {
1293 g_return_if_fail( this->document != NULL );
1295 if (update_in_progress) {
1296 g_print("WARNING: Requested update while update in progress, counter = %d\n", update_in_progress);
1297 }
1299 /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1300 * SP_OBJECT_CHILD_MODIFIED_FLAG */
1301 g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1302 g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1303 g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1305 bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1307 this->uflags |= flags;
1309 /* If requestModified has already been called on this object or one of its children, then we
1310 * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1311 */
1312 if (already_propagated) {
1313 SPObject *parent = SP_OBJECT_PARENT(this);
1314 if (parent) {
1315 parent->requestDisplayUpdate(SP_OBJECT_CHILD_MODIFIED_FLAG);
1316 } else {
1317 sp_document_request_modified(SP_OBJECT_DOCUMENT(this));
1318 }
1319 }
1320 }
1322 /**
1323 * Update views
1324 */
1325 void
1326 SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
1327 {
1328 g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1330 update_in_progress ++;
1332 #ifdef SP_OBJECT_DEBUG_CASCADE
1333 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);
1334 #endif
1336 /* Get this flags */
1337 flags |= this->uflags;
1338 /* Copy flags to modified cascade for later processing */
1339 this->mflags |= this->uflags;
1340 /* We have to clear flags here to allow rescheduling update */
1341 this->uflags = 0;
1343 // Merge style if we have good reasons to think that parent style is changed */
1344 /** \todo
1345 * I am not sure whether we should check only propagated
1346 * flag. We are currently assuming that style parsing is
1347 * done immediately. I think this is correct (Lauris).
1348 */
1349 if ((flags & SP_OBJECT_STYLE_MODIFIED_FLAG) && (flags & SP_OBJECT_PARENT_MODIFIED_FLAG)) {
1350 if (this->style && this->parent) {
1351 sp_style_merge_from_parent(this->style, this->parent->style);
1352 }
1353 }
1355 try
1356 {
1357 if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update)
1358 ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);
1359 }
1360 catch(...)
1361 {
1362 /** \todo
1363 * in case of catching an exception we need to inform the user somehow that the document is corrupted
1364 * maybe by implementing an document flag documentOk
1365 * or by a modal error dialog
1366 */
1367 g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
1368 }
1370 update_in_progress --;
1371 }
1373 /**
1374 * Request modified always bubbles *up* the tree, as opposed to
1375 * request display update, which trickles down and relies on the
1376 * flags set during this pass...
1377 */
1378 void
1379 SPObject::requestModified(unsigned int flags)
1380 {
1381 g_return_if_fail( this->document != NULL );
1383 /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1384 * SP_OBJECT_CHILD_MODIFIED_FLAG */
1385 g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1386 g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1387 g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1389 bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1391 this->mflags |= flags;
1393 /* If requestModified has already been called on this object or one of its children, then we
1394 * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1395 */
1396 if (already_propagated) {
1397 SPObject *parent=SP_OBJECT_PARENT(this);
1398 if (parent) {
1399 parent->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
1400 } else {
1401 sp_document_request_modified(SP_OBJECT_DOCUMENT(this));
1402 }
1403 }
1404 }
1406 /**
1407 * Emits the MODIFIED signal with the object's flags.
1408 * The object's mflags are the original set aside during the update pass for
1409 * later delivery here. Once emitModified() is called, those flags don't
1410 * need to be stored any longer.
1411 */
1412 void
1413 SPObject::emitModified(unsigned int flags)
1414 {
1415 /* only the MODIFIED_CASCADE flag is legal here */
1416 g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1418 #ifdef SP_OBJECT_DEBUG_CASCADE
1419 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);
1420 #endif
1422 flags |= this->mflags;
1423 /* We have to clear mflags beforehand, as signal handlers may
1424 * make changes and therefore queue new modification notifications
1425 * themselves. */
1426 this->mflags = 0;
1428 g_object_ref(G_OBJECT(this));
1429 SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
1430 if (klass->modified) {
1431 klass->modified(this, flags);
1432 }
1433 _modified_signal.emit(this, flags);
1434 g_object_unref(G_OBJECT(this));
1435 }
1437 gchar const *
1438 sp_object_tagName_get(SPObject const *object, SPException *ex)
1439 {
1440 /* If exception is not clear, return */
1441 if (!SP_EXCEPTION_IS_OK(ex)) {
1442 return NULL;
1443 }
1445 /// \todo fixme: Exception if object is NULL? */
1446 return object->repr->name();
1447 }
1449 gchar const *
1450 sp_object_getAttribute(SPObject const *object, gchar const *key, SPException *ex)
1451 {
1452 /* If exception is not clear, return */
1453 if (!SP_EXCEPTION_IS_OK(ex)) {
1454 return NULL;
1455 }
1457 /// \todo fixme: Exception if object is NULL? */
1458 return (gchar const *) object->repr->attribute(key);
1459 }
1461 void
1462 sp_object_setAttribute(SPObject *object, gchar const *key, gchar const *value, SPException *ex)
1463 {
1464 /* If exception is not clear, return */
1465 g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1467 /// \todo fixme: Exception if object is NULL? */
1468 object->repr->setAttribute(key, value, false);
1469 }
1471 void
1472 sp_object_removeAttribute(SPObject *object, gchar const *key, SPException *ex)
1473 {
1474 /* If exception is not clear, return */
1475 g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1477 /// \todo fixme: Exception if object is NULL? */
1478 object->repr->setAttribute(key, NULL, false);
1479 }
1481 /* Helper */
1483 static gchar *
1484 sp_object_get_unique_id(SPObject *object, gchar const *id)
1485 {
1486 static unsigned long count = 0;
1488 g_assert(SP_IS_OBJECT(object));
1490 count++;
1492 gchar const *name = object->repr->name();
1493 g_assert(name != NULL);
1495 gchar const *local = strchr(name, ':');
1496 if (local) {
1497 name = local + 1;
1498 }
1500 if (id != NULL) {
1501 if (object->document->getObjectById(id) == NULL) {
1502 return g_strdup(id);
1503 }
1504 }
1506 size_t const name_len = strlen(name);
1507 size_t const buflen = name_len + (sizeof(count) * 10 / 4) + 1;
1508 gchar *const buf = (gchar *) g_malloc(buflen);
1509 memcpy(buf, name, name_len);
1510 gchar *const count_buf = buf + name_len;
1511 size_t const count_buflen = buflen - name_len;
1512 do {
1513 ++count;
1514 g_snprintf(count_buf, count_buflen, "%lu", count);
1515 } while ( object->document->getObjectById(buf) != NULL );
1516 return buf;
1517 }
1519 /* Style */
1521 /**
1522 * Returns an object style property.
1523 *
1524 * \todo
1525 * fixme: Use proper CSS parsing. The current version is buggy
1526 * in a number of situations where key is a substring of the
1527 * style string other than as a property name (including
1528 * where key is a substring of a property name), and is also
1529 * buggy in its handling of inheritance for properties that
1530 * aren't inherited by default. It also doesn't allow for
1531 * the case where the property is specified but with an invalid
1532 * value (in which case I believe the CSS2 error-handling
1533 * behaviour applies, viz. behave as if the property hadn't
1534 * been specified). Also, the current code doesn't use CRSelEng
1535 * stuff to take a value from stylesheets. Also, we aren't
1536 * setting any hooks to force an update for changes in any of
1537 * the inputs (i.e., in any of the elements that this function
1538 * queries).
1539 *
1540 * \par
1541 * Given that the default value for a property depends on what
1542 * property it is (e.g., whether to inherit or not), and given
1543 * the above comment about ignoring invalid values, and that the
1544 * repr parent isn't necessarily the right element to inherit
1545 * from (e.g., maybe we need to inherit from the referencing
1546 * <use> element instead), we should probably make the caller
1547 * responsible for ascending the repr tree as necessary.
1548 */
1549 gchar const *
1550 sp_object_get_style_property(SPObject const *object, gchar const *key, gchar const *def)
1551 {
1552 g_return_val_if_fail(object != NULL, NULL);
1553 g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
1554 g_return_val_if_fail(key != NULL, NULL);
1556 gchar const *style = object->repr->attribute("style");
1557 if (style) {
1558 size_t const len = strlen(key);
1559 char const *p;
1560 while ( (p = strstr(style, key))
1561 != NULL )
1562 {
1563 p += len;
1564 while ((*p <= ' ') && *p) p++;
1565 if (*p++ != ':') break;
1566 while ((*p <= ' ') && *p) p++;
1567 size_t const inherit_len = sizeof("inherit") - 1;
1568 if (*p
1569 && !(strneq(p, "inherit", inherit_len)
1570 && (p[inherit_len] == '\0'
1571 || p[inherit_len] == ';'
1572 || g_ascii_isspace(p[inherit_len])))) {
1573 return p;
1574 }
1575 }
1576 }
1577 gchar const *val = object->repr->attribute(key);
1578 if (val && !streq(val, "inherit")) {
1579 return val;
1580 }
1581 if (object->parent) {
1582 return sp_object_get_style_property(object->parent, key, def);
1583 }
1585 return def;
1586 }
1588 /**
1589 * Lifts SVG version of all root objects to version.
1590 */
1591 void
1592 SPObject::_requireSVGVersion(Inkscape::Version version) {
1593 for ( SPObject::ParentIterator iter=this ; iter ; ++iter ) {
1594 SPObject *object=iter;
1595 if (SP_IS_ROOT(object)) {
1596 SPRoot *root=SP_ROOT(object);
1597 if ( root->version.svg < version ) {
1598 root->version.svg = version;
1599 }
1600 }
1601 }
1602 }
1604 /**
1605 * Returns previous object in sibling list or NULL.
1606 */
1607 SPObject *
1608 sp_object_prev(SPObject *child)
1609 {
1610 SPObject *parent = SP_OBJECT_PARENT(child);
1611 for ( SPObject *i = sp_object_first_child(parent); i; i = SP_OBJECT_NEXT(i) ) {
1612 if (SP_OBJECT_NEXT(i) == child)
1613 return i;
1614 }
1615 return NULL;
1616 }
1618 /* Titles and descriptions */
1620 /* Note:
1621 Titles and descriptions are stored in 'title' and 'desc' child elements
1622 (see section 5.4 of the SVG 1.0 and 1.1 specifications). The spec allows
1623 an element to have more than one 'title' child element, but strongly
1624 recommends against this and requires using the first one if a choice must
1625 be made. The same applies to 'desc' elements. Therefore, these functions
1626 ignore all but the first 'title' child element and first 'desc' child
1627 element, except when deleting a title or description.
1628 */
1630 /**
1631 * Returns the title of this object, or NULL if there is none.
1632 * The caller must free the returned string using g_free() - see comment
1633 * for getTitleOrDesc() below.
1634 */
1635 gchar *
1636 SPObject::title() const
1637 {
1638 return getTitleOrDesc("svg:title");
1639 }
1641 /**
1642 * Sets the title of this object
1643 * A NULL first argument is interpreted as meaning that the existing title
1644 * (if any) should be deleted.
1645 * The second argument is optional - see setTitleOrDesc() below for details.
1646 */
1647 bool
1648 SPObject::setTitle(gchar const *title, bool verbatim)
1649 {
1650 return setTitleOrDesc(title, "svg:title", verbatim);
1651 }
1653 /**
1654 * Returns the description of this object, or NULL if there is none.
1655 * The caller must free the returned string using g_free() - see comment
1656 * for getTitleOrDesc() below.
1657 */
1658 gchar *
1659 SPObject::desc() const
1660 {
1661 return getTitleOrDesc("svg:desc");
1662 }
1664 /**
1665 * Sets the description of this object.
1666 * A NULL first argument is interpreted as meaning that the existing
1667 * description (if any) should be deleted.
1668 * The second argument is optional - see setTitleOrDesc() below for details.
1669 */
1670 bool
1671 SPObject::setDesc(gchar const *desc, bool verbatim)
1672 {
1673 return setTitleOrDesc(desc, "svg:desc", verbatim);
1674 }
1676 /**
1677 * Returns the title or description of this object, or NULL if there is none.
1678 *
1679 * The SVG spec allows 'title' and 'desc' elements to contain text marked up
1680 * using elements from other namespaces. Therefore, this function cannot
1681 * in general just return a pointer to an existing string - it must instead
1682 * construct a string containing the title or description without the mark-up.
1683 * Consequently, the return value is a newly allocated string (or NULL), and
1684 * must be freed (using g_free()) by the caller.
1685 */
1686 gchar *
1687 SPObject::getTitleOrDesc(gchar const *svg_tagname) const
1688 {
1689 SPObject *elem = findFirstChild(svg_tagname);
1690 if (elem == NULL) return NULL;
1691 return g_string_free(elem->textualContent(), FALSE);
1692 }
1694 /**
1695 * Sets or deletes the title or description of this object.
1696 * A NULL 'value' argument causes the title or description to be deleted.
1697 *
1698 * 'verbatim' parameter:
1699 * If verbatim==true, then the title or description is set to exactly the
1700 * specified value. If verbatim==false then two exceptions are made:
1701 * (1) If the specified value is just whitespace, then the title/description
1702 * is deleted.
1703 * (2) If the specified value is the same as the current value except for
1704 * mark-up, then the current value is left unchanged.
1705 * This is usually the desired behaviour, so 'verbatim' defaults to false for
1706 * setTitle() and setDesc().
1707 *
1708 * The return value is true if a change was made to the title/description,
1709 * and usually false otherwise.
1710 */
1711 bool
1712 SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim)
1713 {
1714 if (!verbatim) {
1715 // If the new title/description is just whitespace,
1716 // treat it as though it were NULL.
1717 if (value) {
1718 bool just_whitespace = true;
1719 for (const gchar *cp = value; *cp; ++cp) {
1720 if (!std::strchr("\r\n \t", *cp)) {
1721 just_whitespace = false;
1722 break;
1723 }
1724 }
1725 if (just_whitespace) value = NULL;
1726 }
1727 // Don't stomp on mark-up if there is no real change.
1728 if (value) {
1729 gchar *current_value = getTitleOrDesc(svg_tagname);
1730 if (current_value) {
1731 bool different = std::strcmp(current_value, value);
1732 g_free(current_value);
1733 if (!different) return false;
1734 }
1735 }
1736 }
1738 SPObject *elem = findFirstChild(svg_tagname);
1740 if (value == NULL) {
1741 if (elem == NULL) return false;
1742 // delete the title/description(s)
1743 while (elem) {
1744 elem->deleteObject();
1745 elem = findFirstChild(svg_tagname);
1746 }
1747 return true;
1748 }
1750 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
1752 if (elem == NULL) {
1753 // create a new 'title' or 'desc' element, putting it at the
1754 // beginning (in accordance with the spec's recommendations)
1755 Inkscape::XML::Node *xml_elem = xml_doc->createElement(svg_tagname);
1756 repr->addChild(xml_elem, NULL);
1757 elem = document->getObjectByRepr(xml_elem);
1758 Inkscape::GC::release(xml_elem);
1759 }
1760 else {
1761 // remove the current content of the 'text' or 'desc' element
1762 SPObject *child;
1763 while (NULL != (child = elem->firstChild())) child->deleteObject();
1764 }
1766 // add the new content
1767 elem->appendChildRepr(xml_doc->createTextNode(value));
1768 return true;
1769 }
1771 /**
1772 * Find the first child of this object with a given tag name,
1773 * and return it. Returns NULL if there is no matching child.
1774 */
1775 SPObject *
1776 SPObject::findFirstChild(gchar const *tagname) const
1777 {
1778 for (SPObject *child = children; child; child = child->next)
1779 {
1780 if (child->repr->type() == Inkscape::XML::ELEMENT_NODE &&
1781 !strcmp(child->repr->name(), tagname)) return child;
1782 }
1783 return NULL;
1784 }
1786 /**
1787 * Return the full textual content of an element (typically all the
1788 * content except the tags).
1789 * Must not be used on anything except elements.
1790 */
1791 GString*
1792 SPObject::textualContent() const
1793 {
1794 GString* text = g_string_new("");
1796 for (const SPObject *child = firstChild(); child; child = child->next)
1797 {
1798 Inkscape::XML::NodeType child_type = child->repr->type();
1800 if (child_type == Inkscape::XML::ELEMENT_NODE) {
1801 GString * new_text = child->textualContent();
1802 g_string_append(text, new_text->str);
1803 g_string_free(new_text, TRUE);
1804 }
1805 else if (child_type == Inkscape::XML::TEXT_NODE) {
1806 g_string_append(text, child->repr->content());
1807 }
1808 }
1809 return text;
1810 }
1812 /*
1813 Local Variables:
1814 mode:c++
1815 c-file-style:"stroustrup"
1816 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1817 indent-tabs-mode:nil
1818 fill-column:99
1819 End:
1820 */
1821 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :