1 #define __SP_OBJECT_C__
2 /** \file
3 * SPObject implementation.
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * bulia byak <buliabyak@users.sf.net>
8 * Stephen Silver <sasilver@users.sourceforge.net>
9 *
10 * Copyright (C) 1999-2008 authors
11 * Copyright (C) 2001-2002 Ximian, Inc.
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 /** \class SPObject
17 *
18 * SPObject is an abstract base class of all of the document nodes at the
19 * SVG document level. Each SPObject subclass implements a certain SVG
20 * element node type, or is an abstract base class for different node
21 * types. The SPObject layer is bound to the SPRepr layer, closely
22 * following the SPRepr mutations via callbacks. During creation,
23 * SPObject parses and interprets all textual attributes and CSS style
24 * strings of the SPRepr, and later updates the internal state whenever
25 * it receives a signal about a change. The opposite is not true - there
26 * are methods manipulating SPObjects directly and such changes do not
27 * propagate to the SPRepr layer. This is important for implementation of
28 * the undo stack, animations and other features.
29 *
30 * SPObjects are bound to the higher-level container SPDocument, which
31 * provides document level functionality such as the undo stack,
32 * dictionary and so on. Source: doc/architecture.txt
33 */
35 #include <cstring>
36 #include <string>
38 #include "helper/sp-marshal.h"
39 #include "xml/node-event-vector.h"
40 #include "attributes.h"
41 #include "document.h"
42 #include "style.h"
43 #include "sp-object-repr.h"
44 #include "sp-root.h"
45 #include "sp-style-elem.h"
46 #include "sp-script.h"
47 #include "streq.h"
48 #include "strneq.h"
49 #include "xml/repr.h"
50 #include "xml/node-fns.h"
51 #include "debug/event-tracker.h"
52 #include "debug/simple-event.h"
53 #include "debug/demangle.h"
54 #include "util/share.h"
55 #include "util/format.h"
56 #include "util/longest-common-suffix.h"
58 using std::memcpy;
59 using std::strchr;
60 using std::strcmp;
61 using std::strlen;
62 using std::strstr;
64 #define noSP_OBJECT_DEBUG_CASCADE
66 #define noSP_OBJECT_DEBUG
68 #ifdef SP_OBJECT_DEBUG
69 # define debug(f, a...) { g_print("%s(%d) %s:", \
70 __FILE__,__LINE__,__FUNCTION__); \
71 g_print(f, ## a); \
72 g_print("\n"); \
73 }
74 #else
75 # define debug(f, a...) /**/
76 #endif
78 /*static void sp_object_class_init(SPObjectClass *klass);
79 static void sp_object_init(SPObject *object);
80 static void sp_object_finalize(GObject *object);
82 static void sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref);
83 static void sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child);
84 static void sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *old_ref, Inkscape::XML::Node *new_ref);
86 static void sp_object_release(SPObject *object);
87 static void sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
89 static void sp_object_private_set(SPObject *object, unsigned int key, gchar const *value);
90 static Inkscape::XML::Node *sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags);
92 * Real handlers of repr signals */
94 /*static void sp_object_repr_attr_changed(Inkscape::XML::Node *repr, gchar const *key, gchar const *oldval, gchar const *newval, bool is_interactive, gpointer data);
96 static void sp_object_repr_content_changed(Inkscape::XML::Node *repr, gchar const *oldcontent, gchar const *newcontent, gpointer data);
98 static void sp_object_repr_child_added(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data);
99 static void sp_object_repr_child_removed(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, void *data);
101 static void sp_object_repr_order_changed(Inkscape::XML::Node *repr, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data);
103 static gchar *sp_object_get_unique_id(SPObject *object, gchar const *defid);*/
105 guint update_in_progress = 0; // guard against update-during-update
107 Inkscape::XML::NodeEventVector object_event_vector = {
108 SPObject::sp_object_repr_child_added,
109 SPObject::sp_object_repr_child_removed,
110 SPObject::sp_object_repr_attr_changed,
111 SPObject::sp_object_repr_content_changed,
112 SPObject::sp_object_repr_order_changed
113 };
115 // A friend class used to set internal members on SPObject so as to not expose settors in SPObject's public API
116 class SPObjectImpl
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 GObjectClass * SPObjectClass::static_parent_class = 0;
150 /**
151 * Registers the SPObject class with Gdk and returns its type number.
152 */
153 GType
154 SPObject::sp_object_get_type()
155 {
156 static GType type = 0;
157 if (!type) {
158 GTypeInfo info = {
159 sizeof(SPObjectClass),
160 NULL, NULL,
161 (GClassInitFunc) SPObjectClass::sp_object_class_init,
162 NULL, NULL,
163 sizeof(SPObject),
164 16,
165 (GInstanceInitFunc) sp_object_init,
166 NULL
167 };
168 type = g_type_register_static(G_TYPE_OBJECT, "SPObject", &info, (GTypeFlags)0);
169 }
170 return type;
171 }
173 /**
174 * Initializes the SPObject vtable.
175 */
176 void
177 SPObjectClass::sp_object_class_init(SPObjectClass *klass)
178 {
179 GObjectClass *object_class;
181 object_class = (GObjectClass *) klass;
183 static_parent_class = (GObjectClass *) g_type_class_ref(G_TYPE_OBJECT);
185 object_class->finalize = SPObject::sp_object_finalize;
187 klass->child_added = SPObject::sp_object_child_added;
188 klass->remove_child = SPObject::sp_object_remove_child;
189 klass->order_changed = SPObject::sp_object_order_changed;
191 klass->release = SPObject::sp_object_release;
193 klass->build = SPObject::sp_object_build;
195 klass->set = SPObject::sp_object_private_set;
196 klass->write = SPObject::sp_object_private_write;
197 }
199 /**
200 * Callback to initialize the SPObject object.
201 */
202 void
203 SPObject::sp_object_init(SPObject *object)
204 {
205 debug("id=%x, typename=%s",object, g_type_name_from_instance((GTypeInstance*)object));
207 object->hrefcount = 0;
208 object->_total_hrefcount = 0;
209 object->document = NULL;
210 object->children = object->_last_child = NULL;
211 object->parent = object->next = NULL;
213 //used XML Tree here.
214 Inkscape::XML::Node *repr = object->getRepr();
215 repr = NULL;
216 SPObjectImpl::setIdNull(object);
218 object->_collection_policy = SPObject::COLLECT_WITH_PARENT;
220 new (&object->_release_signal) sigc::signal<void, SPObject *>();
221 new (&object->_modified_signal) sigc::signal<void, SPObject *, unsigned int>();
222 new (&object->_delete_signal) sigc::signal<void, SPObject *>();
223 new (&object->_position_changed_signal) sigc::signal<void, SPObject *>();
224 object->_successor = NULL;
226 // FIXME: now we create style for all objects, but per SVG, only the following can have style attribute:
227 // vg, g, defs, desc, title, symbol, use, image, switch, path, rect, circle, ellipse, line, polyline,
228 // polygon, text, tspan, tref, textPath, altGlyph, glyphRef, marker, linearGradient, radialGradient,
229 // stop, pattern, clipPath, mask, filter, feImage, a, font, glyph, missing-glyph, foreignObject
230 object->style = sp_style_new_from_object(object);
232 object->_label = NULL;
233 object->_default_label = NULL;
234 }
236 /**
237 * Callback to destroy all members and connections of object and itself.
238 */
239 void
240 SPObject::sp_object_finalize(GObject *object)
241 {
242 SPObject *spobject = (SPObject *)object;
244 g_free(spobject->_label);
245 g_free(spobject->_default_label);
246 spobject->_label = NULL;
247 spobject->_default_label = NULL;
249 if (spobject->_successor) {
250 sp_object_unref(spobject->_successor, NULL);
251 spobject->_successor = NULL;
252 }
254 if (((GObjectClass *) (SPObjectClass::static_parent_class))->finalize) {
255 (* ((GObjectClass *) (SPObjectClass::static_parent_class))->finalize)(object);
256 }
258 spobject->_release_signal.~signal();
259 spobject->_modified_signal.~signal();
260 spobject->_delete_signal.~signal();
261 spobject->_position_changed_signal.~signal();
262 }
264 namespace {
266 namespace Debug = Inkscape::Debug;
267 namespace Util = Inkscape::Util;
269 typedef Debug::SimpleEvent<Debug::Event::REFCOUNT> BaseRefCountEvent;
271 class RefCountEvent : public BaseRefCountEvent {
272 public:
273 RefCountEvent(SPObject *object, int bias, Util::ptr_shared<char> name)
274 : BaseRefCountEvent(name)
275 {
276 _addProperty("object", Util::format("%p", object));
277 _addProperty("class", Debug::demangle(g_type_name(G_TYPE_FROM_INSTANCE(object))));
278 _addProperty("new-refcount", Util::format("%d", G_OBJECT(object)->ref_count + bias));
279 }
280 };
282 class RefEvent : public RefCountEvent {
283 public:
284 RefEvent(SPObject *object)
285 : RefCountEvent(object, 1, Util::share_static_string("sp-object-ref"))
286 {}
287 };
289 class UnrefEvent : public RefCountEvent {
290 public:
291 UnrefEvent(SPObject *object)
292 : RefCountEvent(object, -1, Util::share_static_string("sp-object-unref"))
293 {}
294 };
296 }
298 gchar const* SPObject::getId() const {
299 return id;
300 }
302 Inkscape::XML::Node * SPObject::getRepr() {
303 return repr;
304 }
306 Inkscape::XML::Node const* SPObject::getRepr() const{
307 return repr;
308 }
311 /**
312 * Increase reference count of object, with possible debugging.
313 *
314 * \param owner If non-NULL, make debug log entry.
315 * \return object, NULL is error.
316 * \pre object points to real object
317 */
318 SPObject *
319 sp_object_ref(SPObject *object, SPObject *owner)
320 {
321 g_return_val_if_fail(object != NULL, NULL);
322 g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
323 g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
325 Inkscape::Debug::EventTracker<RefEvent> tracker(object);
326 g_object_ref(G_OBJECT(object));
327 return object;
328 }
330 /**
331 * Decrease reference count of object, with possible debugging and
332 * finalization.
333 *
334 * \param owner If non-NULL, make debug log entry.
335 * \return always NULL
336 * \pre object points to real object
337 */
338 SPObject *
339 sp_object_unref(SPObject *object, SPObject *owner)
340 {
341 g_return_val_if_fail(object != NULL, NULL);
342 g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
343 g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
345 Inkscape::Debug::EventTracker<UnrefEvent> tracker(object);
346 g_object_unref(G_OBJECT(object));
347 return NULL;
348 }
350 /**
351 * Increase weak refcount.
352 *
353 * Hrefcount is used for weak references, for example, to
354 * determine whether any graphical element references a certain gradient
355 * node.
356 * \param owner Ignored.
357 * \return object, NULL is error
358 * \pre object points to real object
359 */
360 SPObject *
361 sp_object_href(SPObject *object, gpointer /*owner*/)
362 {
363 g_return_val_if_fail(object != NULL, NULL);
364 g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
366 object->hrefcount++;
367 object->_updateTotalHRefCount(1);
369 return object;
370 }
372 /**
373 * Decrease weak refcount.
374 *
375 * Hrefcount is used for weak references, for example, to determine whether
376 * any graphical element references a certain gradient node.
377 * \param owner Ignored.
378 * \return always NULL
379 * \pre object points to real object and hrefcount>0
380 */
381 SPObject *
382 sp_object_hunref(SPObject *object, gpointer /*owner*/)
383 {
384 g_return_val_if_fail(object != NULL, NULL);
385 g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
386 g_return_val_if_fail(object->hrefcount > 0, NULL);
388 object->hrefcount--;
389 object->_updateTotalHRefCount(-1);
391 return NULL;
392 }
394 /**
395 * Adds increment to _total_hrefcount of object and its parents.
396 */
397 void
398 SPObject::_updateTotalHRefCount(int increment) {
399 SPObject *topmost_collectable = NULL;
400 for ( SPObject *iter = this ; iter ; iter = SP_OBJECT_PARENT(iter) ) {
401 iter->_total_hrefcount += increment;
402 if ( iter->_total_hrefcount < iter->hrefcount ) {
403 g_critical("HRefs overcounted");
404 }
405 if ( iter->_total_hrefcount == 0 &&
406 iter->_collection_policy != COLLECT_WITH_PARENT )
407 {
408 topmost_collectable = iter;
409 }
410 }
411 if (topmost_collectable) {
412 topmost_collectable->requestOrphanCollection();
413 }
414 }
416 /**
417 * True if object is non-NULL and this is some in/direct parent of object.
418 */
419 bool
420 SPObject::isAncestorOf(SPObject const *object) const {
421 g_return_val_if_fail(object != NULL, false);
422 object = SP_OBJECT_PARENT(object);
423 while (object) {
424 if ( object == this ) {
425 return true;
426 }
427 object = SP_OBJECT_PARENT(object);
428 }
429 return false;
430 }
432 namespace {
434 bool same_objects(SPObject const &a, SPObject const &b) {
435 return &a == &b;
436 }
438 }
440 /**
441 * Returns youngest object being parent to this and object.
442 */
443 SPObject const *
444 SPObject::nearestCommonAncestor(SPObject const *object) const {
445 g_return_val_if_fail(object != NULL, NULL);
447 using Inkscape::Algorithms::longest_common_suffix;
448 return longest_common_suffix<SPObject::ConstParentIterator>(this, object, NULL, &same_objects);
449 }
451 SPObject const *AncestorSon(SPObject const *obj, SPObject const *ancestor) {
452 if (obj == NULL || ancestor == NULL)
453 return NULL;
454 if (SP_OBJECT_PARENT(obj) == ancestor)
455 return obj;
456 return AncestorSon(SP_OBJECT_PARENT(obj), ancestor);
457 }
459 /**
460 * Compares height of objects in tree.
461 *
462 * Works for different-parent objects, so long as they have a common ancestor.
463 * \return \verbatim
464 * 0 positions are equivalent
465 * 1 first object's position is greater than the second
466 * -1 first object's position is less than the second \endverbatim
467 */
468 int
469 sp_object_compare_position(SPObject const *first, SPObject const *second)
470 {
471 if (first == second) return 0;
473 SPObject const *ancestor = first->nearestCommonAncestor(second);
474 if (ancestor == NULL) return 0; // cannot compare, no common ancestor!
476 // we have an object and its ancestor (should not happen when sorting selection)
477 if (ancestor == first)
478 return 1;
479 if (ancestor == second)
480 return -1;
482 SPObject const *to_first = AncestorSon(first, ancestor);
483 SPObject const *to_second = AncestorSon(second, ancestor);
485 g_assert(SP_OBJECT_PARENT(to_second) == SP_OBJECT_PARENT(to_first));
487 return sp_repr_compare_position(SP_OBJECT_REPR(to_first), SP_OBJECT_REPR(to_second));
488 }
491 /**
492 * Append repr as child of this object.
493 * \pre this is not a cloned object
494 */
495 SPObject *
496 SPObject::appendChildRepr(Inkscape::XML::Node *repr) {
497 if (!SP_OBJECT_IS_CLONED(this)) {
498 SP_OBJECT_REPR(this)->appendChild(repr);
499 return SP_OBJECT_DOCUMENT(this)->getObjectByRepr(repr);
500 } else {
501 g_critical("Attempt to append repr as child of cloned object");
502 return NULL;
503 }
504 }
506 /**
507 * Retrieves the children as a GSList object, optionally ref'ing the children
508 * in the process, if add_ref is specified.
509 */
510 GSList *SPObject::childList(bool add_ref, Action) {
511 GSList *l = NULL;
512 for (SPObject *child = this->first_child() ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
513 if (add_ref)
514 g_object_ref (G_OBJECT (child));
516 l = g_slist_prepend (l, child);
517 }
518 return l;
520 }
522 /** Gets the label property for the object or a default if no label
523 * is defined.
524 */
525 gchar const *
526 SPObject::label() const {
527 return _label;
528 }
530 /** Returns a default label property for the object. */
531 gchar const *
532 SPObject::defaultLabel() const {
533 if (_label) {
534 return _label;
535 } else {
536 if (!_default_label) {
537 gchar const *id=SP_OBJECT_ID(this);
538 if (id) {
539 _default_label = g_strdup_printf("#%s", id);
540 } else {
541 _default_label = g_strdup_printf("<%s>", SP_OBJECT_REPR(this)->name());
542 }
543 }
544 return _default_label;
545 }
546 }
548 /** Sets the label property for the object */
549 void
550 SPObject::setLabel(gchar const *label) {
551 SP_OBJECT_REPR(this)->setAttribute("inkscape:label", label, false);
552 }
555 /** Queues the object for orphan collection */
556 void
557 SPObject::requestOrphanCollection() {
558 g_return_if_fail(document != NULL);
560 // do not remove style or script elements (Bug #276244)
561 if (SP_IS_STYLE_ELEM(this))
562 return;
563 if (SP_IS_SCRIPT(this))
564 return;
566 document->queueForOrphanCollection(this);
568 /** \todo
569 * This is a temporary hack added to make fill&stroke rebuild its
570 * gradient list when the defs are vacuumed. gradient-vector.cpp
571 * listens to the modified signal on defs, and now we give it that
572 * signal. Mental says that this should be made automatic by
573 * merging SPObjectGroup with SPObject; SPObjectGroup would issue
574 * this signal automatically. Or maybe just derive SPDefs from
575 * SPObjectGroup?
576 */
578 this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
579 }
581 /** Sends the delete signal to all children of this object recursively */
582 void
583 SPObject::_sendDeleteSignalRecursive() {
584 for (SPObject *child = this->first_child(); child; child = SP_OBJECT_NEXT(child)) {
585 child->_delete_signal.emit(child);
586 child->_sendDeleteSignalRecursive();
587 }
588 }
590 /**
591 * Deletes the object reference, unparenting it from its parent.
592 *
593 * If the \a propagate parameter is set to true, it emits a delete
594 * signal. If the \a propagate_descendants parameter is true, it
595 * recursively sends the delete signal to children.
596 */
597 void
598 SPObject::deleteObject(bool propagate, bool propagate_descendants)
599 {
600 sp_object_ref(this, NULL);
601 if (propagate) {
602 _delete_signal.emit(this);
603 }
604 if (propagate_descendants) {
605 this->_sendDeleteSignalRecursive();
606 }
608 Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
609 if (repr && sp_repr_parent(repr)) {
610 sp_repr_unparent(repr);
611 }
613 if (_successor) {
614 _successor->deleteObject(propagate, propagate_descendants);
615 }
616 sp_object_unref(this, NULL);
617 }
619 /**
620 * Put object into object tree, under parent, and behind prev;
621 * also update object's XML space.
622 */
623 void
624 SPObject::attach(SPObject *object, SPObject *prev)
625 {
626 //g_return_if_fail(parent != NULL);
627 //g_return_if_fail(SP_IS_OBJECT(parent));
628 g_return_if_fail(object != NULL);
629 g_return_if_fail(SP_IS_OBJECT(object));
630 g_return_if_fail(!prev || SP_IS_OBJECT(prev));
631 g_return_if_fail(!prev || prev->parent == this);
632 g_return_if_fail(!object->parent);
634 sp_object_ref(object, this);
635 object->parent = this;
636 this->_updateTotalHRefCount(object->_total_hrefcount);
638 SPObject *next;
639 if (prev) {
640 next = prev->next;
641 prev->next = object;
642 } else {
643 next = this->children;
644 this->children = object;
645 }
646 object->next = next;
647 if (!next) {
648 this->_last_child = object;
649 }
650 if (!object->xml_space.set)
651 object->xml_space.value = this->xml_space.value;
652 }
654 /**
655 * In list of object's siblings, move object behind prev.
656 */
657 void
658 SPObject::reorder(SPObject *prev) {
659 //g_return_if_fail(object != NULL);
660 //g_return_if_fail(SP_IS_OBJECT(object));
661 g_return_if_fail(this->parent != NULL);
662 g_return_if_fail(this != prev);
663 g_return_if_fail(!prev || SP_IS_OBJECT(prev));
664 g_return_if_fail(!prev || prev->parent == this->parent);
666 SPObject *const parent=this->parent;
668 SPObject *old_prev=NULL;
669 for ( SPObject *child = parent->children ; child && child != this ;
670 child = child->next )
671 {
672 old_prev = child;
673 }
675 SPObject *next=this->next;
676 if (old_prev) {
677 old_prev->next = next;
678 } else {
679 parent->children = next;
680 }
681 if (!next) {
682 parent->_last_child = old_prev;
683 }
684 if (prev) {
685 next = prev->next;
686 prev->next = this;
687 } else {
688 next = parent->children;
689 parent->children = this;
690 }
691 this->next = next;
692 if (!next) {
693 parent->_last_child = this;
694 }
695 }
697 /**
698 * Remove object from parent's children, release and unref it.
699 */
700 void
701 SPObject::detach(SPObject *object) {
702 //g_return_if_fail(parent != NULL);
703 //g_return_if_fail(SP_IS_OBJECT(parent));
704 g_return_if_fail(object != NULL);
705 g_return_if_fail(SP_IS_OBJECT(object));
706 g_return_if_fail(object->parent == this);
708 object->releaseReferences();
710 SPObject *prev=NULL;
711 for ( SPObject *child = this->children ; child && child != object ;
712 child = child->next )
713 {
714 prev = child;
715 }
717 SPObject *next=object->next;
718 if (prev) {
719 prev->next = next;
720 } else {
721 this->children = next;
722 }
723 if (!next) {
724 this->_last_child = prev;
725 }
727 object->next = NULL;
728 object->parent = NULL;
730 this->_updateTotalHRefCount(-object->_total_hrefcount);
731 sp_object_unref(object, this);
732 }
734 /**
735 * Return object's child whose node pointer equals repr.
736 */
737 SPObject *
738 SPObject::get_child_by_repr(Inkscape::XML::Node *repr)
739 {
740 //g_return_val_if_fail(object != NULL, NULL);
741 //g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
742 g_return_val_if_fail(repr != NULL, NULL);
744 if (this->_last_child && SP_OBJECT_REPR(this->_last_child) == repr)
745 return this->_last_child; // optimization for common scenario
746 for ( SPObject *child = this->children ; child ; child = child->next ) {
747 if ( SP_OBJECT_REPR(child) == repr ) {
748 return child;
749 }
750 }
752 return NULL;
753 }
755 /**
756 * Callback for child_added event.
757 * Invoked whenever the given mutation event happens in the XML tree.
758 */
759 void
760 SPObject::sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
761 {
762 GType type = sp_repr_type_lookup(child);
763 if (!type) {
764 return;
765 }
766 SPObject *ochild = SP_OBJECT(g_object_new(type, 0));
767 SPObject *prev = ref ? object->get_child_by_repr(ref) : NULL;
768 object->attach(ochild, prev);
769 sp_object_unref(ochild, NULL);
771 ochild->invoke_build(object->document, child, SP_OBJECT_IS_CLONED(object));
772 }
774 /**
775 * Removes, releases and unrefs all children of object.
776 *
777 * This is the opposite of build. It has to be invoked as soon as the
778 * object is removed from the tree, even if it is still alive according
779 * to reference count. The frontend unregisters the object from the
780 * document and releases the SPRepr bindings; implementations should free
781 * state data and release all child objects. Invoking release on
782 * SPRoot destroys the whole document tree.
783 * \see sp_object_build()
784 */
785 void SPObject::sp_object_release(SPObject *object)
786 {
787 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
788 while (object->children) {
789 object->detach(object->children);
790 }
791 }
793 /**
794 * Remove object's child whose node equals repr, release and
795 * unref it.
796 *
797 * Invoked whenever the given mutation event happens in the XML
798 * tree, BEFORE removal from the XML tree happens, so grouping
799 * objects can safely release the child data.
800 */
801 void
802 SPObject::sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child)
803 {
804 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
805 SPObject *ochild = object->get_child_by_repr(child);
806 g_return_if_fail (ochild != NULL || !strcmp("comment", child->name())); // comments have no objects
807 if (ochild)
808 object->detach(ochild);
809 }
811 /**
812 * Move object corresponding to child after sibling object corresponding
813 * to new_ref.
814 * Invoked whenever the given mutation event happens in the XML tree.
815 * \param old_ref Ignored
816 */
817 void SPObject::sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node */*old_ref*/,
818 Inkscape::XML::Node *new_ref)
819 {
820 SPObject *ochild = object->get_child_by_repr(child);
821 g_return_if_fail(ochild != NULL);
822 SPObject *prev = new_ref ? object->get_child_by_repr(new_ref) : NULL;
823 ochild->reorder(prev);
824 ochild->_position_changed_signal.emit(ochild);
825 }
827 /**
828 * Virtual build callback.
829 *
830 * This has to be invoked immediately after creation of an SPObject. The
831 * frontend method ensures that the new object is properly attached to
832 * the document and repr; implementation then will parse all of the attributes,
833 * generate the children objects and so on. Invoking build on the SPRoot
834 * object results in creation of the whole document tree (this is, what
835 * SPDocument does after the creation of the XML tree).
836 * \see sp_object_release()
837 */
838 void
839 SPObject::sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
840 {
841 /* Nothing specific here */
842 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
844 object->readAttr("xml:space");
845 object->readAttr("inkscape:label");
846 object->readAttr("inkscape:collect");
848 for (Inkscape::XML::Node *rchild = repr->firstChild() ; rchild != NULL; rchild = rchild->next()) {
849 GType type = sp_repr_type_lookup(rchild);
850 if (!type) {
851 continue;
852 }
853 SPObject *child = SP_OBJECT(g_object_new(type, 0));
854 object->attach(child, object->lastChild());
855 sp_object_unref(child, NULL);
856 child->invoke_build(document, rchild, SP_OBJECT_IS_CLONED(object));
857 }
858 }
860 void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
861 {
862 debug("id=%x, typename=%s", this, g_type_name_from_instance((GTypeInstance*)this));
864 //g_assert(object != NULL);
865 //g_assert(SP_IS_OBJECT(object));
866 g_assert(document != NULL);
867 g_assert(repr != NULL);
869 g_assert(this->document == NULL);
870 g_assert(this->repr == NULL);
871 g_assert(this->getId() == NULL);
873 /* Bookkeeping */
875 this->document = document;
876 this->repr = repr;
877 if (!cloned)
878 Inkscape::GC::anchor(repr);
879 this->cloned = cloned;
881 if (!SP_OBJECT_IS_CLONED(this)) {
882 this->document->bindObjectToRepr(this->repr, this);
884 if (Inkscape::XML::id_permitted(this->repr)) {
885 /* If we are not cloned, and not seeking, force unique id */
886 gchar const *id = this->repr->attribute("id");
887 if (!document->isSeeking()) {
888 {
889 gchar *realid = sp_object_get_unique_id(this, id);
890 g_assert(realid != NULL);
892 this->document->bindObjectToId(realid, this);
893 SPObjectImpl::setId(this, realid);
894 g_free(realid);
895 }
897 /* Redefine ID, if required */
898 if ((id == NULL) || (strcmp(id, this->getId()) != 0)) {
899 this->repr->setAttribute("id", this->getId());
900 }
901 } else if (id) {
902 // bind if id, but no conflict -- otherwise, we can expect
903 // a subsequent setting of the id attribute
904 if (!this->document->getObjectById(id)) {
905 this->document->bindObjectToId(id, this);
906 SPObjectImpl::setId(this, id);
907 }
908 }
909 }
910 } else {
911 g_assert(this->getId() == NULL);
912 }
914 /* Invoke derived methods, if any */
915 if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->build) {
916 (*((SPObjectClass *) G_OBJECT_GET_CLASS(this))->build)(this, document, repr);
917 }
919 /* Signalling (should be connected AFTER processing derived methods */
920 sp_repr_add_listener(repr, &object_event_vector, this);
921 }
923 long long int SPObject::getIntAttribute(char const *key, long long int def)
924 {
925 return sp_repr_get_int_attribute(getRepr(),key,def);
926 }
928 unsigned SPObject::getPosition(){
929 g_assert(this->repr);
931 return repr->position();
932 }
934 void SPObject::appendChild(Inkscape::XML::Node *child) {
935 g_assert(this->repr);
937 repr->appendChild(child);
938 }
940 void SPObject::releaseReferences() {
941 g_assert(this->document);
942 g_assert(this->repr);
944 sp_repr_remove_listener_by_data(this->repr, this);
946 this->_release_signal.emit(this);
947 SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
948 if (klass->release) {
949 klass->release(this);
950 }
952 /* all hrefs should be released by the "release" handlers */
953 g_assert(this->hrefcount == 0);
955 if (!SP_OBJECT_IS_CLONED(this)) {
956 if (this->id) {
957 this->document->bindObjectToId(this->id, NULL);
958 }
959 g_free(this->id);
960 this->id = NULL;
962 g_free(this->_default_label);
963 this->_default_label = NULL;
965 this->document->bindObjectToRepr(this->repr, NULL);
967 Inkscape::GC::release(this->repr);
968 } else {
969 g_assert(!this->id);
970 }
972 if (this->style) {
973 this->style = sp_style_unref(this->style);
974 }
976 this->document = NULL;
977 this->repr = NULL;
978 }
981 SPObject *SPObject::getNext()
982 {
983 return next;
984 }
986 SPObject *SPObject::getPrev()
987 {
988 return this->prev();
989 }
991 /**
992 * Callback for child_added node event.
993 */
994 void
995 SPObject::sp_object_repr_child_added(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data)
996 {
997 SPObject *object = SP_OBJECT(data);
999 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->child_added)
1000 (*((SPObjectClass *)G_OBJECT_GET_CLASS(object))->child_added)(object, child, ref);
1001 }
1003 /**
1004 * Callback for remove_child node event.
1005 */
1006 void
1007 SPObject::sp_object_repr_child_removed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node */*ref*/, gpointer data)
1008 {
1009 SPObject *object = SP_OBJECT(data);
1011 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->remove_child) {
1012 (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->remove_child)(object, child);
1013 }
1014 }
1016 /**
1017 * Callback for order_changed node event.
1018 *
1019 * \todo fixme:
1020 */
1021 void
1022 SPObject::sp_object_repr_order_changed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data)
1023 {
1024 SPObject *object = SP_OBJECT(data);
1026 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->order_changed) {
1027 (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->order_changed)(object, child, old, newer);
1028 }
1029 }
1031 /**
1032 * Callback for set event.
1033 */
1034 void
1035 SPObject::sp_object_private_set(SPObject *object, unsigned int key, gchar const *value)
1036 {
1037 g_assert(key != SP_ATTR_INVALID);
1039 switch (key) {
1040 case SP_ATTR_ID:
1042 //XML Tree being used here.
1043 if ( !SP_OBJECT_IS_CLONED(object) && object->getRepr()->type() == Inkscape::XML::ELEMENT_NODE ) {
1044 SPDocument *document=object->document;
1045 SPObject *conflict=NULL;
1047 gchar const *new_id = value;
1049 if (new_id) {
1050 conflict = document->getObjectById((char const *)new_id);
1051 }
1053 if ( conflict && conflict != object ) {
1054 if (!document->isSeeking()) {
1055 sp_object_ref(conflict, NULL);
1056 // give the conflicting object a new ID
1057 gchar *new_conflict_id = sp_object_get_unique_id(conflict, NULL);
1058 SP_OBJECT_REPR(conflict)->setAttribute("id", new_conflict_id);
1059 g_free(new_conflict_id);
1060 sp_object_unref(conflict, NULL);
1061 } else {
1062 new_id = NULL;
1063 }
1064 }
1066 if (object->getId()) {
1067 document->bindObjectToId(object->getId(), NULL);
1068 SPObjectImpl::setId(object, 0);
1069 }
1071 if (new_id) {
1072 SPObjectImpl::setId(object, new_id);
1073 document->bindObjectToId(object->getId(), object);
1074 }
1076 g_free(object->_default_label);
1077 object->_default_label = NULL;
1078 }
1079 break;
1080 case SP_ATTR_INKSCAPE_LABEL:
1081 g_free(object->_label);
1082 if (value) {
1083 object->_label = g_strdup(value);
1084 } else {
1085 object->_label = NULL;
1086 }
1087 g_free(object->_default_label);
1088 object->_default_label = NULL;
1089 break;
1090 case SP_ATTR_INKSCAPE_COLLECT:
1091 if ( value && !strcmp(value, "always") ) {
1092 object->setCollectionPolicy(SPObject::ALWAYS_COLLECT);
1093 } else {
1094 object->setCollectionPolicy(SPObject::COLLECT_WITH_PARENT);
1095 }
1096 break;
1097 case SP_ATTR_XML_SPACE:
1098 if (value && !strcmp(value, "preserve")) {
1099 object->xml_space.value = SP_XML_SPACE_PRESERVE;
1100 object->xml_space.set = TRUE;
1101 } else if (value && !strcmp(value, "default")) {
1102 object->xml_space.value = SP_XML_SPACE_DEFAULT;
1103 object->xml_space.set = TRUE;
1104 } else if (object->parent) {
1105 SPObject *parent;
1106 parent = object->parent;
1107 object->xml_space.value = parent->xml_space.value;
1108 }
1109 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1110 break;
1111 case SP_ATTR_STYLE:
1112 sp_style_read_from_object(object->style, object);
1113 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1114 break;
1115 default:
1116 break;
1117 }
1118 }
1120 /**
1121 * Call virtual set() function of object.
1122 */
1123 void
1124 SPObject::setKeyValue(unsigned int key, gchar const *value)
1125 {
1126 //g_assert(object != NULL);
1127 //g_assert(SP_IS_OBJECT(object));
1129 if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->set) {
1130 ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->set(this, key, value);
1131 }
1132 }
1134 /**
1135 * Read value of key attribute from XML node into object.
1136 */
1137 void
1138 SPObject::readAttr(gchar const *key)
1139 {
1140 //g_assert(object != NULL);
1141 //g_assert(SP_IS_OBJECT(object));
1142 g_assert(key != NULL);
1144 //XML Tree being used here.
1145 g_assert(this->getRepr() != NULL);
1147 unsigned int keyid = sp_attribute_lookup(key);
1148 if (keyid != SP_ATTR_INVALID) {
1149 /* Retrieve the 'key' attribute from the object's XML representation */
1150 gchar const *value = getRepr()->attribute(key);
1152 setKeyValue(keyid, value);
1153 }
1154 }
1156 /**
1157 * Callback for attr_changed node event.
1158 */
1159 void
1160 SPObject::sp_object_repr_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *key, gchar const */*oldval*/, gchar const */*newval*/, bool is_interactive, gpointer data)
1161 {
1162 SPObject *object = SP_OBJECT(data);
1164 object->readAttr(key);
1166 // manual changes to extension attributes require the normal
1167 // attributes, which depend on them, to be updated immediately
1168 if (is_interactive) {
1169 object->updateRepr(0);
1170 }
1171 }
1173 /**
1174 * Callback for content_changed node event.
1175 */
1176 void
1177 SPObject::sp_object_repr_content_changed(Inkscape::XML::Node */*repr*/, gchar const */*oldcontent*/, gchar const */*newcontent*/, gpointer data)
1178 {
1179 SPObject *object = SP_OBJECT(data);
1181 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)
1182 (*((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)(object);
1183 }
1185 /**
1186 * Return string representation of space value.
1187 */
1188 static gchar const*
1189 sp_xml_get_space_string(unsigned int space)
1190 {
1191 switch (space) {
1192 case SP_XML_SPACE_DEFAULT:
1193 return "default";
1194 case SP_XML_SPACE_PRESERVE:
1195 return "preserve";
1196 default:
1197 return NULL;
1198 }
1199 }
1201 /**
1202 * Callback for write event.
1203 */
1204 Inkscape::XML::Node *
1205 SPObject::sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
1206 {
1207 if (!repr && (flags & SP_OBJECT_WRITE_BUILD)) {
1208 repr = SP_OBJECT_REPR(object)->duplicate(doc);
1209 if (!( flags & SP_OBJECT_WRITE_EXT )) {
1210 repr->setAttribute("inkscape:collect", NULL);
1211 }
1212 } else {
1213 repr->setAttribute("id", object->getId());
1215 if (object->xml_space.set) {
1216 char const *xml_space;
1217 xml_space = sp_xml_get_space_string(object->xml_space.value);
1218 repr->setAttribute("xml:space", xml_space);
1219 }
1221 if ( flags & SP_OBJECT_WRITE_EXT &&
1222 object->collectionPolicy() == SPObject::ALWAYS_COLLECT )
1223 {
1224 repr->setAttribute("inkscape:collect", "always");
1225 } else {
1226 repr->setAttribute("inkscape:collect", NULL);
1227 }
1229 SPStyle const *const obj_style = SP_OBJECT_STYLE(object);
1230 if (obj_style) {
1231 gchar *s = sp_style_write_string(obj_style, SP_STYLE_FLAG_IFSET);
1232 repr->setAttribute("style", ( *s ? s : NULL ));
1233 g_free(s);
1234 } else {
1235 /** \todo I'm not sure what to do in this case. Bug #1165868
1236 * suggests that it can arise, but the submitter doesn't know
1237 * how to do so reliably. The main two options are either
1238 * leave repr's style attribute unchanged, or explicitly clear it.
1239 * Must also consider what to do with property attributes for
1240 * the element; see below.
1241 */
1242 char const *style_str = repr->attribute("style");
1243 if (!style_str) {
1244 style_str = "NULL";
1245 }
1246 g_warning("Item's style is NULL; repr style attribute is %s", style_str);
1247 }
1249 /** \note We treat object->style as authoritative. Its effects have
1250 * been written to the style attribute above; any properties that are
1251 * unset we take to be deliberately unset (e.g. so that clones can
1252 * override the property).
1253 *
1254 * Note that the below has an undesirable consequence of changing the
1255 * appearance on renderers that lack CSS support (e.g. SVG tiny);
1256 * possibly we should write property attributes instead of a style
1257 * attribute.
1258 */
1259 sp_style_unset_property_attrs (object);
1260 }
1262 return repr;
1263 }
1265 /**
1266 * Update this object's XML node with flags value.
1267 */
1268 Inkscape::XML::Node *
1269 SPObject::updateRepr(unsigned int flags) {
1270 if (!SP_OBJECT_IS_CLONED(this)) {
1271 Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
1272 if (repr) {
1273 return updateRepr(repr->document(), repr, flags);
1274 } else {
1275 g_critical("Attempt to update non-existent repr");
1276 return NULL;
1277 }
1278 } else {
1279 /* cloned objects have no repr */
1280 return NULL;
1281 }
1282 }
1284 /** Used both to create reprs in the original document, and to create
1285 * reprs in another document (e.g. a temporary document used when
1286 * saving as "Plain SVG"
1287 */
1288 Inkscape::XML::Node *
1289 SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags) {
1290 g_assert(doc != NULL);
1292 if (SP_OBJECT_IS_CLONED(this)) {
1293 /* cloned objects have no repr */
1294 return NULL;
1295 }
1296 if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write) {
1297 if (!(flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1298 repr = SP_OBJECT_REPR(this);
1299 }
1300 return ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write(this, doc, repr, flags);
1301 } else {
1302 g_warning("Class %s does not implement ::write", G_OBJECT_TYPE_NAME(this));
1303 if (!repr) {
1304 if (flags & SP_OBJECT_WRITE_BUILD) {
1305 repr = SP_OBJECT_REPR(this)->duplicate(doc);
1306 }
1307 /// \todo FIXME: else probably error (Lauris) */
1308 } else {
1309 repr->mergeFrom(SP_OBJECT_REPR(this), "id");
1310 }
1311 return repr;
1312 }
1313 }
1315 /* Modification */
1317 /**
1318 * Add \a flags to \a object's as dirtiness flags, and
1319 * recursively add CHILD_MODIFIED flag to
1320 * parent and ancestors (as far up as necessary).
1321 */
1322 void
1323 SPObject::requestDisplayUpdate(unsigned int flags)
1324 {
1325 g_return_if_fail( this->document != NULL );
1327 if (update_in_progress) {
1328 g_print("WARNING: Requested update while update in progress, counter = %d\n", update_in_progress);
1329 }
1331 /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1332 * SP_OBJECT_CHILD_MODIFIED_FLAG */
1333 g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1334 g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1335 g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1337 bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1339 this->uflags |= flags;
1341 /* If requestModified has already been called on this object or one of its children, then we
1342 * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1343 */
1344 if (already_propagated) {
1345 SPObject *parent = SP_OBJECT_PARENT(this);
1346 if (parent) {
1347 parent->requestDisplayUpdate(SP_OBJECT_CHILD_MODIFIED_FLAG);
1348 } else {
1349 SP_OBJECT_DOCUMENT(this)->request_modified();
1350 }
1351 }
1352 }
1354 /**
1355 * Update views
1356 */
1357 void
1358 SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
1359 {
1360 g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1362 update_in_progress ++;
1364 #ifdef SP_OBJECT_DEBUG_CASCADE
1365 g_print("Update %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), SP_OBJECT_ID(this), flags, this->uflags, this->mflags);
1366 #endif
1368 /* Get this flags */
1369 flags |= this->uflags;
1370 /* Copy flags to modified cascade for later processing */
1371 this->mflags |= this->uflags;
1372 /* We have to clear flags here to allow rescheduling update */
1373 this->uflags = 0;
1375 // Merge style if we have good reasons to think that parent style is changed */
1376 /** \todo
1377 * I am not sure whether we should check only propagated
1378 * flag. We are currently assuming that style parsing is
1379 * done immediately. I think this is correct (Lauris).
1380 */
1381 if ((flags & SP_OBJECT_STYLE_MODIFIED_FLAG) && (flags & SP_OBJECT_PARENT_MODIFIED_FLAG)) {
1382 if (this->style && this->parent) {
1383 sp_style_merge_from_parent(this->style, this->parent->style);
1384 }
1385 }
1387 try
1388 {
1389 if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update)
1390 ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);
1391 }
1392 catch(...)
1393 {
1394 /** \todo
1395 * in case of catching an exception we need to inform the user somehow that the document is corrupted
1396 * maybe by implementing an document flag documentOk
1397 * or by a modal error dialog
1398 */
1399 g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
1400 }
1402 update_in_progress --;
1403 }
1405 /**
1406 * Request modified always bubbles *up* the tree, as opposed to
1407 * request display update, which trickles down and relies on the
1408 * flags set during this pass...
1409 */
1410 void
1411 SPObject::requestModified(unsigned int flags)
1412 {
1413 g_return_if_fail( this->document != NULL );
1415 /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1416 * SP_OBJECT_CHILD_MODIFIED_FLAG */
1417 g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1418 g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1419 g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1421 bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1423 this->mflags |= flags;
1425 /* If requestModified has already been called on this object or one of its children, then we
1426 * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1427 */
1428 if (already_propagated) {
1429 SPObject *parent=SP_OBJECT_PARENT(this);
1430 if (parent) {
1431 parent->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
1432 } else {
1433 SP_OBJECT_DOCUMENT(this)->request_modified();
1434 }
1435 }
1436 }
1438 /**
1439 * Emits the MODIFIED signal with the object's flags.
1440 * The object's mflags are the original set aside during the update pass for
1441 * later delivery here. Once emitModified() is called, those flags don't
1442 * need to be stored any longer.
1443 */
1444 void
1445 SPObject::emitModified(unsigned int flags)
1446 {
1447 /* only the MODIFIED_CASCADE flag is legal here */
1448 g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1450 #ifdef SP_OBJECT_DEBUG_CASCADE
1451 g_print("Modified %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), SP_OBJECT_ID(this), flags, this->uflags, this->mflags);
1452 #endif
1454 flags |= this->mflags;
1455 /* We have to clear mflags beforehand, as signal handlers may
1456 * make changes and therefore queue new modification notifications
1457 * themselves. */
1458 this->mflags = 0;
1460 g_object_ref(G_OBJECT(this));
1461 SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
1462 if (klass->modified) {
1463 klass->modified(this, flags);
1464 }
1465 _modified_signal.emit(this, flags);
1466 g_object_unref(G_OBJECT(this));
1467 }
1469 gchar const *
1470 SPObject::getTagName(SPException *ex) const
1471 {
1472 g_assert(repr != NULL);
1473 /* If exception is not clear, return */
1474 if (!SP_EXCEPTION_IS_OK(ex)) {
1475 return NULL;
1476 }
1478 /// \todo fixme: Exception if object is NULL? */
1479 //XML Tree being used here.
1480 return getRepr()->name();
1481 }
1483 gchar const *
1484 SPObject::getAttribute(gchar const *key, SPException *ex) const
1485 {
1486 g_assert(this->repr != NULL);
1487 /* If exception is not clear, return */
1488 if (!SP_EXCEPTION_IS_OK(ex)) {
1489 return NULL;
1490 }
1492 /// \todo fixme: Exception if object is NULL? */
1493 //XML Tree being used here.
1494 return (gchar const *) getRepr()->attribute(key);
1495 }
1497 void
1498 SPObject::setAttribute(gchar const *key, gchar const *value, SPException *ex)
1499 {
1500 g_assert(this->repr != NULL);
1501 /* If exception is not clear, return */
1502 g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1504 /// \todo fixme: Exception if object is NULL? */
1505 //XML Tree being used here.
1506 getRepr()->setAttribute(key, value, false);
1507 }
1509 void
1510 SPObject::removeAttribute(gchar const *key, SPException *ex)
1511 {
1512 /* If exception is not clear, return */
1513 g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1515 /// \todo fixme: Exception if object is NULL? */
1516 //XML Tree being used here.
1517 getRepr()->setAttribute(key, NULL, false);
1518 }
1520 /* Helper */
1522 gchar *
1523 SPObject::sp_object_get_unique_id(SPObject *object, gchar const *id)
1524 {
1525 static unsigned long count = 0;
1527 g_assert(SP_IS_OBJECT(object));
1529 count++;
1531 //XML Tree being used here.
1532 gchar const *name = object->getRepr()->name();
1533 g_assert(name != NULL);
1535 gchar const *local = strchr(name, ':');
1536 if (local) {
1537 name = local + 1;
1538 }
1540 if (id != NULL) {
1541 if (object->document->getObjectById(id) == NULL) {
1542 return g_strdup(id);
1543 }
1544 }
1546 size_t const name_len = strlen(name);
1547 size_t const buflen = name_len + (sizeof(count) * 10 / 4) + 1;
1548 gchar *const buf = (gchar *) g_malloc(buflen);
1549 memcpy(buf, name, name_len);
1550 gchar *const count_buf = buf + name_len;
1551 size_t const count_buflen = buflen - name_len;
1552 do {
1553 ++count;
1554 g_snprintf(count_buf, count_buflen, "%lu", count);
1555 } while ( object->document->getObjectById(buf) != NULL );
1556 return buf;
1557 }
1559 /* Style */
1561 /**
1562 * Returns an object style property.
1563 *
1564 * \todo
1565 * fixme: Use proper CSS parsing. The current version is buggy
1566 * in a number of situations where key is a substring of the
1567 * style string other than as a property name (including
1568 * where key is a substring of a property name), and is also
1569 * buggy in its handling of inheritance for properties that
1570 * aren't inherited by default. It also doesn't allow for
1571 * the case where the property is specified but with an invalid
1572 * value (in which case I believe the CSS2 error-handling
1573 * behaviour applies, viz. behave as if the property hadn't
1574 * been specified). Also, the current code doesn't use CRSelEng
1575 * stuff to take a value from stylesheets. Also, we aren't
1576 * setting any hooks to force an update for changes in any of
1577 * the inputs (i.e., in any of the elements that this function
1578 * queries).
1579 *
1580 * \par
1581 * Given that the default value for a property depends on what
1582 * property it is (e.g., whether to inherit or not), and given
1583 * the above comment about ignoring invalid values, and that the
1584 * repr parent isn't necessarily the right element to inherit
1585 * from (e.g., maybe we need to inherit from the referencing
1586 * <use> element instead), we should probably make the caller
1587 * responsible for ascending the repr tree as necessary.
1588 */
1589 gchar const *
1590 SPObject::getStyleProperty(gchar const *key, gchar const *def) const
1591 {
1592 //g_return_val_if_fail(object != NULL, NULL);
1593 //g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
1594 g_return_val_if_fail(key != NULL, NULL);
1596 //XML Tree being used here.
1597 gchar const *style = getRepr()->attribute("style");
1598 if (style) {
1599 size_t const len = strlen(key);
1600 char const *p;
1601 while ( (p = strstr(style, key))
1602 != NULL )
1603 {
1604 p += len;
1605 while ((*p <= ' ') && *p) p++;
1606 if (*p++ != ':') break;
1607 while ((*p <= ' ') && *p) p++;
1608 size_t const inherit_len = sizeof("inherit") - 1;
1609 if (*p
1610 && !(strneq(p, "inherit", inherit_len)
1611 && (p[inherit_len] == '\0'
1612 || p[inherit_len] == ';'
1613 || g_ascii_isspace(p[inherit_len])))) {
1614 return p;
1615 }
1616 }
1617 }
1619 //XML Tree being used here.
1620 gchar const *val = getRepr()->attribute(key);
1621 if (val && !streq(val, "inherit")) {
1622 return val;
1623 }
1624 if (this->parent) {
1625 return (this->parent)->getStyleProperty(key, def);
1626 }
1628 return def;
1629 }
1631 /**
1632 * Lifts SVG version of all root objects to version.
1633 */
1634 void
1635 SPObject::_requireSVGVersion(Inkscape::Version version) {
1636 for ( SPObject::ParentIterator iter=this ; iter ; ++iter ) {
1637 SPObject *object=iter;
1638 if (SP_IS_ROOT(object)) {
1639 SPRoot *root=SP_ROOT(object);
1640 if ( root->version.svg < version ) {
1641 root->version.svg = version;
1642 }
1643 }
1644 }
1645 }
1647 /**
1648 * Returns previous object in sibling list or NULL.
1649 */
1650 SPObject *
1651 SPObject::prev()
1652 {
1653 SPObject *parent = SP_OBJECT_PARENT(this);
1654 for ( SPObject *i = parent->first_child(); i; i = SP_OBJECT_NEXT(i) ) {
1655 if (SP_OBJECT_NEXT(i) == this)
1656 return i;
1657 }
1658 return NULL;
1659 }
1661 /* Titles and descriptions */
1663 /* Note:
1664 Titles and descriptions are stored in 'title' and 'desc' child elements
1665 (see section 5.4 of the SVG 1.0 and 1.1 specifications). The spec allows
1666 an element to have more than one 'title' child element, but strongly
1667 recommends against this and requires using the first one if a choice must
1668 be made. The same applies to 'desc' elements. Therefore, these functions
1669 ignore all but the first 'title' child element and first 'desc' child
1670 element, except when deleting a title or description.
1671 */
1673 /**
1674 * Returns the title of this object, or NULL if there is none.
1675 * The caller must free the returned string using g_free() - see comment
1676 * for getTitleOrDesc() below.
1677 */
1678 gchar *
1679 SPObject::title() const
1680 {
1681 return getTitleOrDesc("svg:title");
1682 }
1684 /**
1685 * Sets the title of this object
1686 * A NULL first argument is interpreted as meaning that the existing title
1687 * (if any) should be deleted.
1688 * The second argument is optional - see setTitleOrDesc() below for details.
1689 */
1690 bool
1691 SPObject::setTitle(gchar const *title, bool verbatim)
1692 {
1693 return setTitleOrDesc(title, "svg:title", verbatim);
1694 }
1696 /**
1697 * Returns the description of this object, or NULL if there is none.
1698 * The caller must free the returned string using g_free() - see comment
1699 * for getTitleOrDesc() below.
1700 */
1701 gchar *
1702 SPObject::desc() const
1703 {
1704 return getTitleOrDesc("svg:desc");
1705 }
1707 /**
1708 * Sets the description of this object.
1709 * A NULL first argument is interpreted as meaning that the existing
1710 * description (if any) should be deleted.
1711 * The second argument is optional - see setTitleOrDesc() below for details.
1712 */
1713 bool
1714 SPObject::setDesc(gchar const *desc, bool verbatim)
1715 {
1716 return setTitleOrDesc(desc, "svg:desc", verbatim);
1717 }
1719 /**
1720 * Returns the title or description of this object, or NULL if there is none.
1721 *
1722 * The SVG spec allows 'title' and 'desc' elements to contain text marked up
1723 * using elements from other namespaces. Therefore, this function cannot
1724 * in general just return a pointer to an existing string - it must instead
1725 * construct a string containing the title or description without the mark-up.
1726 * Consequently, the return value is a newly allocated string (or NULL), and
1727 * must be freed (using g_free()) by the caller.
1728 */
1729 gchar *
1730 SPObject::getTitleOrDesc(gchar const *svg_tagname) const
1731 {
1732 SPObject *elem = findFirstChild(svg_tagname);
1733 if (elem == NULL) return NULL;
1734 return g_string_free(elem->textualContent(), FALSE);
1735 }
1737 /**
1738 * Sets or deletes the title or description of this object.
1739 * A NULL 'value' argument causes the title or description to be deleted.
1740 *
1741 * 'verbatim' parameter:
1742 * If verbatim==true, then the title or description is set to exactly the
1743 * specified value. If verbatim==false then two exceptions are made:
1744 * (1) If the specified value is just whitespace, then the title/description
1745 * is deleted.
1746 * (2) If the specified value is the same as the current value except for
1747 * mark-up, then the current value is left unchanged.
1748 * This is usually the desired behaviour, so 'verbatim' defaults to false for
1749 * setTitle() and setDesc().
1750 *
1751 * The return value is true if a change was made to the title/description,
1752 * and usually false otherwise.
1753 */
1754 bool
1755 SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim)
1756 {
1757 if (!verbatim) {
1758 // If the new title/description is just whitespace,
1759 // treat it as though it were NULL.
1760 if (value) {
1761 bool just_whitespace = true;
1762 for (const gchar *cp = value; *cp; ++cp) {
1763 if (!std::strchr("\r\n \t", *cp)) {
1764 just_whitespace = false;
1765 break;
1766 }
1767 }
1768 if (just_whitespace) value = NULL;
1769 }
1770 // Don't stomp on mark-up if there is no real change.
1771 if (value) {
1772 gchar *current_value = getTitleOrDesc(svg_tagname);
1773 if (current_value) {
1774 bool different = std::strcmp(current_value, value);
1775 g_free(current_value);
1776 if (!different) return false;
1777 }
1778 }
1779 }
1781 SPObject *elem = findFirstChild(svg_tagname);
1783 if (value == NULL) {
1784 if (elem == NULL) return false;
1785 // delete the title/description(s)
1786 while (elem) {
1787 elem->deleteObject();
1788 elem = findFirstChild(svg_tagname);
1789 }
1790 return true;
1791 }
1793 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
1795 if (elem == NULL) {
1796 // create a new 'title' or 'desc' element, putting it at the
1797 // beginning (in accordance with the spec's recommendations)
1798 Inkscape::XML::Node *xml_elem = xml_doc->createElement(svg_tagname);
1799 repr->addChild(xml_elem, NULL);
1800 elem = document->getObjectByRepr(xml_elem);
1801 Inkscape::GC::release(xml_elem);
1802 }
1803 else {
1804 // remove the current content of the 'text' or 'desc' element
1805 SPObject *child;
1806 while (NULL != (child = elem->firstChild())) child->deleteObject();
1807 }
1809 // add the new content
1810 elem->appendChildRepr(xml_doc->createTextNode(value));
1811 return true;
1812 }
1814 /**
1815 * Find the first child of this object with a given tag name,
1816 * and return it. Returns NULL if there is no matching child.
1817 */
1818 SPObject *
1819 SPObject::findFirstChild(gchar const *tagname) const
1820 {
1821 for (SPObject *child = children; child; child = child->next)
1822 {
1823 if (child->repr->type() == Inkscape::XML::ELEMENT_NODE &&
1824 !strcmp(child->repr->name(), tagname)) return child;
1825 }
1826 return NULL;
1827 }
1829 /**
1830 * Return the full textual content of an element (typically all the
1831 * content except the tags).
1832 * Must not be used on anything except elements.
1833 */
1834 GString*
1835 SPObject::textualContent() const
1836 {
1837 GString* text = g_string_new("");
1839 for (const SPObject *child = firstChild(); child; child = child->next)
1840 {
1841 Inkscape::XML::NodeType child_type = child->repr->type();
1843 if (child_type == Inkscape::XML::ELEMENT_NODE) {
1844 GString * new_text = child->textualContent();
1845 g_string_append(text, new_text->str);
1846 g_string_free(new_text, TRUE);
1847 }
1848 else if (child_type == Inkscape::XML::TEXT_NODE) {
1849 g_string_append(text, child->repr->content());
1850 }
1851 }
1852 return text;
1853 }
1855 /*
1856 Local Variables:
1857 mode:c++
1858 c-file-style:"stroustrup"
1859 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1860 indent-tabs-mode:nil
1861 fill-column:99
1862 End:
1863 */
1864 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :