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