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 void SPObject::setCSS(SPCSSAttr *css, gchar const *attr)
507 {
508 g_assert(this->getRepr() != NULL);
509 sp_repr_css_set(this->getRepr(), css, attr);
510 }
512 void SPObject::changeCSS(SPCSSAttr *css, gchar const *attr)
513 {
514 g_assert(this->getRepr() != NULL);
515 sp_repr_css_change(this->getRepr(), css, attr);
516 }
518 /**
519 * Retrieves the children as a GSList object, optionally ref'ing the children
520 * in the process, if add_ref is specified.
521 */
522 GSList *SPObject::childList(bool add_ref, Action) {
523 GSList *l = NULL;
524 for (SPObject *child = this->first_child() ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
525 if (add_ref)
526 g_object_ref (G_OBJECT (child));
528 l = g_slist_prepend (l, child);
529 }
530 return l;
532 }
534 /** Gets the label property for the object or a default if no label
535 * is defined.
536 */
537 gchar const *
538 SPObject::label() const {
539 return _label;
540 }
542 /** Returns a default label property for the object. */
543 gchar const *
544 SPObject::defaultLabel() const {
545 if (_label) {
546 return _label;
547 } else {
548 if (!_default_label) {
549 gchar const *id=SP_OBJECT_ID(this);
550 if (id) {
551 _default_label = g_strdup_printf("#%s", id);
552 } else {
553 _default_label = g_strdup_printf("<%s>", SP_OBJECT_REPR(this)->name());
554 }
555 }
556 return _default_label;
557 }
558 }
560 /** Sets the label property for the object */
561 void
562 SPObject::setLabel(gchar const *label) {
563 SP_OBJECT_REPR(this)->setAttribute("inkscape:label", label, false);
564 }
567 /** Queues the object for orphan collection */
568 void
569 SPObject::requestOrphanCollection() {
570 g_return_if_fail(document != NULL);
572 // do not remove style or script elements (Bug #276244)
573 if (SP_IS_STYLE_ELEM(this))
574 return;
575 if (SP_IS_SCRIPT(this))
576 return;
578 document->queueForOrphanCollection(this);
580 /** \todo
581 * This is a temporary hack added to make fill&stroke rebuild its
582 * gradient list when the defs are vacuumed. gradient-vector.cpp
583 * listens to the modified signal on defs, and now we give it that
584 * signal. Mental says that this should be made automatic by
585 * merging SPObjectGroup with SPObject; SPObjectGroup would issue
586 * this signal automatically. Or maybe just derive SPDefs from
587 * SPObjectGroup?
588 */
590 this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
591 }
593 /** Sends the delete signal to all children of this object recursively */
594 void
595 SPObject::_sendDeleteSignalRecursive() {
596 for (SPObject *child = this->first_child(); child; child = SP_OBJECT_NEXT(child)) {
597 child->_delete_signal.emit(child);
598 child->_sendDeleteSignalRecursive();
599 }
600 }
602 /**
603 * Deletes the object reference, unparenting it from its parent.
604 *
605 * If the \a propagate parameter is set to true, it emits a delete
606 * signal. If the \a propagate_descendants parameter is true, it
607 * recursively sends the delete signal to children.
608 */
609 void
610 SPObject::deleteObject(bool propagate, bool propagate_descendants)
611 {
612 sp_object_ref(this, NULL);
613 if (propagate) {
614 _delete_signal.emit(this);
615 }
616 if (propagate_descendants) {
617 this->_sendDeleteSignalRecursive();
618 }
620 Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
621 if (repr && sp_repr_parent(repr)) {
622 sp_repr_unparent(repr);
623 }
625 if (_successor) {
626 _successor->deleteObject(propagate, propagate_descendants);
627 }
628 sp_object_unref(this, NULL);
629 }
631 /**
632 * Put object into object tree, under parent, and behind prev;
633 * also update object's XML space.
634 */
635 void
636 SPObject::attach(SPObject *object, SPObject *prev)
637 {
638 //g_return_if_fail(parent != NULL);
639 //g_return_if_fail(SP_IS_OBJECT(parent));
640 g_return_if_fail(object != NULL);
641 g_return_if_fail(SP_IS_OBJECT(object));
642 g_return_if_fail(!prev || SP_IS_OBJECT(prev));
643 g_return_if_fail(!prev || prev->parent == this);
644 g_return_if_fail(!object->parent);
646 sp_object_ref(object, this);
647 object->parent = this;
648 this->_updateTotalHRefCount(object->_total_hrefcount);
650 SPObject *next;
651 if (prev) {
652 next = prev->next;
653 prev->next = object;
654 } else {
655 next = this->children;
656 this->children = object;
657 }
658 object->next = next;
659 if (!next) {
660 this->_last_child = object;
661 }
662 if (!object->xml_space.set)
663 object->xml_space.value = this->xml_space.value;
664 }
666 /**
667 * In list of object's siblings, move object behind prev.
668 */
669 void
670 SPObject::reorder(SPObject *prev) {
671 //g_return_if_fail(object != NULL);
672 //g_return_if_fail(SP_IS_OBJECT(object));
673 g_return_if_fail(this->parent != NULL);
674 g_return_if_fail(this != prev);
675 g_return_if_fail(!prev || SP_IS_OBJECT(prev));
676 g_return_if_fail(!prev || prev->parent == this->parent);
678 SPObject *const parent=this->parent;
680 SPObject *old_prev=NULL;
681 for ( SPObject *child = parent->children ; child && child != this ;
682 child = child->next )
683 {
684 old_prev = child;
685 }
687 SPObject *next=this->next;
688 if (old_prev) {
689 old_prev->next = next;
690 } else {
691 parent->children = next;
692 }
693 if (!next) {
694 parent->_last_child = old_prev;
695 }
696 if (prev) {
697 next = prev->next;
698 prev->next = this;
699 } else {
700 next = parent->children;
701 parent->children = this;
702 }
703 this->next = next;
704 if (!next) {
705 parent->_last_child = this;
706 }
707 }
709 /**
710 * Remove object from parent's children, release and unref it.
711 */
712 void
713 SPObject::detach(SPObject *object) {
714 //g_return_if_fail(parent != NULL);
715 //g_return_if_fail(SP_IS_OBJECT(parent));
716 g_return_if_fail(object != NULL);
717 g_return_if_fail(SP_IS_OBJECT(object));
718 g_return_if_fail(object->parent == this);
720 object->releaseReferences();
722 SPObject *prev=NULL;
723 for ( SPObject *child = this->children ; child && child != object ;
724 child = child->next )
725 {
726 prev = child;
727 }
729 SPObject *next=object->next;
730 if (prev) {
731 prev->next = next;
732 } else {
733 this->children = next;
734 }
735 if (!next) {
736 this->_last_child = prev;
737 }
739 object->next = NULL;
740 object->parent = NULL;
742 this->_updateTotalHRefCount(-object->_total_hrefcount);
743 sp_object_unref(object, this);
744 }
746 /**
747 * Return object's child whose node pointer equals repr.
748 */
749 SPObject *
750 SPObject::get_child_by_repr(Inkscape::XML::Node *repr)
751 {
752 //g_return_val_if_fail(object != NULL, NULL);
753 //g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
754 g_return_val_if_fail(repr != NULL, NULL);
756 if (this->_last_child && SP_OBJECT_REPR(this->_last_child) == repr)
757 return this->_last_child; // optimization for common scenario
758 for ( SPObject *child = this->children ; child ; child = child->next ) {
759 if ( SP_OBJECT_REPR(child) == repr ) {
760 return child;
761 }
762 }
764 return NULL;
765 }
767 /**
768 * Callback for child_added event.
769 * Invoked whenever the given mutation event happens in the XML tree.
770 */
771 void
772 SPObject::sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
773 {
774 GType type = sp_repr_type_lookup(child);
775 if (!type) {
776 return;
777 }
778 SPObject *ochild = SP_OBJECT(g_object_new(type, 0));
779 SPObject *prev = ref ? object->get_child_by_repr(ref) : NULL;
780 object->attach(ochild, prev);
781 sp_object_unref(ochild, NULL);
783 ochild->invoke_build(object->document, child, SP_OBJECT_IS_CLONED(object));
784 }
786 /**
787 * Removes, releases and unrefs all children of object.
788 *
789 * This is the opposite of build. It has to be invoked as soon as the
790 * object is removed from the tree, even if it is still alive according
791 * to reference count. The frontend unregisters the object from the
792 * document and releases the SPRepr bindings; implementations should free
793 * state data and release all child objects. Invoking release on
794 * SPRoot destroys the whole document tree.
795 * \see sp_object_build()
796 */
797 void SPObject::sp_object_release(SPObject *object)
798 {
799 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
800 while (object->children) {
801 object->detach(object->children);
802 }
803 }
805 /**
806 * Remove object's child whose node equals repr, release and
807 * unref it.
808 *
809 * Invoked whenever the given mutation event happens in the XML
810 * tree, BEFORE removal from the XML tree happens, so grouping
811 * objects can safely release the child data.
812 */
813 void
814 SPObject::sp_object_remove_child(SPObject *object, Inkscape::XML::Node *child)
815 {
816 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
817 SPObject *ochild = object->get_child_by_repr(child);
818 g_return_if_fail (ochild != NULL || !strcmp("comment", child->name())); // comments have no objects
819 if (ochild)
820 object->detach(ochild);
821 }
823 /**
824 * Move object corresponding to child after sibling object corresponding
825 * to new_ref.
826 * Invoked whenever the given mutation event happens in the XML tree.
827 * \param old_ref Ignored
828 */
829 void SPObject::sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node */*old_ref*/,
830 Inkscape::XML::Node *new_ref)
831 {
832 SPObject *ochild = object->get_child_by_repr(child);
833 g_return_if_fail(ochild != NULL);
834 SPObject *prev = new_ref ? object->get_child_by_repr(new_ref) : NULL;
835 ochild->reorder(prev);
836 ochild->_position_changed_signal.emit(ochild);
837 }
839 /**
840 * Virtual build callback.
841 *
842 * This has to be invoked immediately after creation of an SPObject. The
843 * frontend method ensures that the new object is properly attached to
844 * the document and repr; implementation then will parse all of the attributes,
845 * generate the children objects and so on. Invoking build on the SPRoot
846 * object results in creation of the whole document tree (this is, what
847 * SPDocument does after the creation of the XML tree).
848 * \see sp_object_release()
849 */
850 void
851 SPObject::sp_object_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
852 {
853 /* Nothing specific here */
854 debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
856 object->readAttr("xml:space");
857 object->readAttr("inkscape:label");
858 object->readAttr("inkscape:collect");
860 for (Inkscape::XML::Node *rchild = repr->firstChild() ; rchild != NULL; rchild = rchild->next()) {
861 GType type = sp_repr_type_lookup(rchild);
862 if (!type) {
863 continue;
864 }
865 SPObject *child = SP_OBJECT(g_object_new(type, 0));
866 object->attach(child, object->lastChild());
867 sp_object_unref(child, NULL);
868 child->invoke_build(document, rchild, SP_OBJECT_IS_CLONED(object));
869 }
870 }
872 void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
873 {
874 debug("id=%x, typename=%s", this, g_type_name_from_instance((GTypeInstance*)this));
876 //g_assert(object != NULL);
877 //g_assert(SP_IS_OBJECT(object));
878 g_assert(document != NULL);
879 g_assert(repr != NULL);
881 g_assert(this->document == NULL);
882 g_assert(this->repr == NULL);
883 g_assert(this->getId() == NULL);
885 /* Bookkeeping */
887 this->document = document;
888 this->repr = repr;
889 if (!cloned)
890 Inkscape::GC::anchor(repr);
891 this->cloned = cloned;
893 if (!SP_OBJECT_IS_CLONED(this)) {
894 this->document->bindObjectToRepr(this->repr, this);
896 if (Inkscape::XML::id_permitted(this->repr)) {
897 /* If we are not cloned, and not seeking, force unique id */
898 gchar const *id = this->repr->attribute("id");
899 if (!document->isSeeking()) {
900 {
901 gchar *realid = sp_object_get_unique_id(this, id);
902 g_assert(realid != NULL);
904 this->document->bindObjectToId(realid, this);
905 SPObjectImpl::setId(this, realid);
906 g_free(realid);
907 }
909 /* Redefine ID, if required */
910 if ((id == NULL) || (strcmp(id, this->getId()) != 0)) {
911 this->repr->setAttribute("id", this->getId());
912 }
913 } else if (id) {
914 // bind if id, but no conflict -- otherwise, we can expect
915 // a subsequent setting of the id attribute
916 if (!this->document->getObjectById(id)) {
917 this->document->bindObjectToId(id, this);
918 SPObjectImpl::setId(this, id);
919 }
920 }
921 }
922 } else {
923 g_assert(this->getId() == NULL);
924 }
926 /* Invoke derived methods, if any */
927 if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->build) {
928 (*((SPObjectClass *) G_OBJECT_GET_CLASS(this))->build)(this, document, repr);
929 }
931 /* Signalling (should be connected AFTER processing derived methods */
932 sp_repr_add_listener(repr, &object_event_vector, this);
933 }
935 long long int SPObject::getIntAttribute(char const *key, long long int def)
936 {
937 return sp_repr_get_int_attribute(getRepr(),key,def);
938 }
940 unsigned SPObject::getPosition(){
941 g_assert(this->repr);
943 return repr->position();
944 }
946 void SPObject::appendChild(Inkscape::XML::Node *child) {
947 g_assert(this->repr);
949 repr->appendChild(child);
950 }
952 void SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node * prev)
953 {
954 g_assert(this->repr);
956 repr->addChild(child,prev);
957 }
959 void SPObject::releaseReferences() {
960 g_assert(this->document);
961 g_assert(this->repr);
963 sp_repr_remove_listener_by_data(this->repr, this);
965 this->_release_signal.emit(this);
966 SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
967 if (klass->release) {
968 klass->release(this);
969 }
971 /* all hrefs should be released by the "release" handlers */
972 g_assert(this->hrefcount == 0);
974 if (!SP_OBJECT_IS_CLONED(this)) {
975 if (this->id) {
976 this->document->bindObjectToId(this->id, NULL);
977 }
978 g_free(this->id);
979 this->id = NULL;
981 g_free(this->_default_label);
982 this->_default_label = NULL;
984 this->document->bindObjectToRepr(this->repr, NULL);
986 Inkscape::GC::release(this->repr);
987 } else {
988 g_assert(!this->id);
989 }
991 if (this->style) {
992 this->style = sp_style_unref(this->style);
993 }
995 this->document = NULL;
996 this->repr = NULL;
997 }
1000 SPObject *SPObject::getNext()
1001 {
1002 return next;
1003 }
1005 SPObject *SPObject::getPrev()
1006 {
1007 return this->prev();
1008 }
1010 /**
1011 * Callback for child_added node event.
1012 */
1013 void
1014 SPObject::sp_object_repr_child_added(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))->child_added)
1019 (*((SPObjectClass *)G_OBJECT_GET_CLASS(object))->child_added)(object, child, ref);
1020 }
1022 /**
1023 * Callback for remove_child node event.
1024 */
1025 void
1026 SPObject::sp_object_repr_child_removed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node */*ref*/, gpointer data)
1027 {
1028 SPObject *object = SP_OBJECT(data);
1030 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->remove_child) {
1031 (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->remove_child)(object, child);
1032 }
1033 }
1035 /**
1036 * Callback for order_changed node event.
1037 *
1038 * \todo fixme:
1039 */
1040 void
1041 SPObject::sp_object_repr_order_changed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data)
1042 {
1043 SPObject *object = SP_OBJECT(data);
1045 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->order_changed) {
1046 (* ((SPObjectClass *)G_OBJECT_GET_CLASS(object))->order_changed)(object, child, old, newer);
1047 }
1048 }
1050 /**
1051 * Callback for set event.
1052 */
1053 void
1054 SPObject::sp_object_private_set(SPObject *object, unsigned int key, gchar const *value)
1055 {
1056 g_assert(key != SP_ATTR_INVALID);
1058 switch (key) {
1059 case SP_ATTR_ID:
1061 //XML Tree being used here.
1062 if ( !SP_OBJECT_IS_CLONED(object) && object->getRepr()->type() == Inkscape::XML::ELEMENT_NODE ) {
1063 SPDocument *document=object->document;
1064 SPObject *conflict=NULL;
1066 gchar const *new_id = value;
1068 if (new_id) {
1069 conflict = document->getObjectById((char const *)new_id);
1070 }
1072 if ( conflict && conflict != object ) {
1073 if (!document->isSeeking()) {
1074 sp_object_ref(conflict, NULL);
1075 // give the conflicting object a new ID
1076 gchar *new_conflict_id = sp_object_get_unique_id(conflict, NULL);
1077 SP_OBJECT_REPR(conflict)->setAttribute("id", new_conflict_id);
1078 g_free(new_conflict_id);
1079 sp_object_unref(conflict, NULL);
1080 } else {
1081 new_id = NULL;
1082 }
1083 }
1085 if (object->getId()) {
1086 document->bindObjectToId(object->getId(), NULL);
1087 SPObjectImpl::setId(object, 0);
1088 }
1090 if (new_id) {
1091 SPObjectImpl::setId(object, new_id);
1092 document->bindObjectToId(object->getId(), object);
1093 }
1095 g_free(object->_default_label);
1096 object->_default_label = NULL;
1097 }
1098 break;
1099 case SP_ATTR_INKSCAPE_LABEL:
1100 g_free(object->_label);
1101 if (value) {
1102 object->_label = g_strdup(value);
1103 } else {
1104 object->_label = NULL;
1105 }
1106 g_free(object->_default_label);
1107 object->_default_label = NULL;
1108 break;
1109 case SP_ATTR_INKSCAPE_COLLECT:
1110 if ( value && !strcmp(value, "always") ) {
1111 object->setCollectionPolicy(SPObject::ALWAYS_COLLECT);
1112 } else {
1113 object->setCollectionPolicy(SPObject::COLLECT_WITH_PARENT);
1114 }
1115 break;
1116 case SP_ATTR_XML_SPACE:
1117 if (value && !strcmp(value, "preserve")) {
1118 object->xml_space.value = SP_XML_SPACE_PRESERVE;
1119 object->xml_space.set = TRUE;
1120 } else if (value && !strcmp(value, "default")) {
1121 object->xml_space.value = SP_XML_SPACE_DEFAULT;
1122 object->xml_space.set = TRUE;
1123 } else if (object->parent) {
1124 SPObject *parent;
1125 parent = object->parent;
1126 object->xml_space.value = parent->xml_space.value;
1127 }
1128 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1129 break;
1130 case SP_ATTR_STYLE:
1131 sp_style_read_from_object(object->style, object);
1132 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1133 break;
1134 default:
1135 break;
1136 }
1137 }
1139 /**
1140 * Call virtual set() function of object.
1141 */
1142 void
1143 SPObject::setKeyValue(unsigned int key, gchar const *value)
1144 {
1145 //g_assert(object != NULL);
1146 //g_assert(SP_IS_OBJECT(object));
1148 if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->set) {
1149 ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->set(this, key, value);
1150 }
1151 }
1153 /**
1154 * Read value of key attribute from XML node into object.
1155 */
1156 void
1157 SPObject::readAttr(gchar const *key)
1158 {
1159 //g_assert(object != NULL);
1160 //g_assert(SP_IS_OBJECT(object));
1161 g_assert(key != NULL);
1163 //XML Tree being used here.
1164 g_assert(this->getRepr() != NULL);
1166 unsigned int keyid = sp_attribute_lookup(key);
1167 if (keyid != SP_ATTR_INVALID) {
1168 /* Retrieve the 'key' attribute from the object's XML representation */
1169 gchar const *value = getRepr()->attribute(key);
1171 setKeyValue(keyid, value);
1172 }
1173 }
1175 /**
1176 * Callback for attr_changed node event.
1177 */
1178 void
1179 SPObject::sp_object_repr_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *key, gchar const */*oldval*/, gchar const */*newval*/, bool is_interactive, gpointer data)
1180 {
1181 SPObject *object = SP_OBJECT(data);
1183 object->readAttr(key);
1185 // manual changes to extension attributes require the normal
1186 // attributes, which depend on them, to be updated immediately
1187 if (is_interactive) {
1188 object->updateRepr(0);
1189 }
1190 }
1192 /**
1193 * Callback for content_changed node event.
1194 */
1195 void
1196 SPObject::sp_object_repr_content_changed(Inkscape::XML::Node */*repr*/, gchar const */*oldcontent*/, gchar const */*newcontent*/, gpointer data)
1197 {
1198 SPObject *object = SP_OBJECT(data);
1200 if (((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)
1201 (*((SPObjectClass *) G_OBJECT_GET_CLASS(object))->read_content)(object);
1202 }
1204 /**
1205 * Return string representation of space value.
1206 */
1207 static gchar const*
1208 sp_xml_get_space_string(unsigned int space)
1209 {
1210 switch (space) {
1211 case SP_XML_SPACE_DEFAULT:
1212 return "default";
1213 case SP_XML_SPACE_PRESERVE:
1214 return "preserve";
1215 default:
1216 return NULL;
1217 }
1218 }
1220 /**
1221 * Callback for write event.
1222 */
1223 Inkscape::XML::Node *
1224 SPObject::sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
1225 {
1226 if (!repr && (flags & SP_OBJECT_WRITE_BUILD)) {
1227 repr = SP_OBJECT_REPR(object)->duplicate(doc);
1228 if (!( flags & SP_OBJECT_WRITE_EXT )) {
1229 repr->setAttribute("inkscape:collect", NULL);
1230 }
1231 } else {
1232 repr->setAttribute("id", object->getId());
1234 if (object->xml_space.set) {
1235 char const *xml_space;
1236 xml_space = sp_xml_get_space_string(object->xml_space.value);
1237 repr->setAttribute("xml:space", xml_space);
1238 }
1240 if ( flags & SP_OBJECT_WRITE_EXT &&
1241 object->collectionPolicy() == SPObject::ALWAYS_COLLECT )
1242 {
1243 repr->setAttribute("inkscape:collect", "always");
1244 } else {
1245 repr->setAttribute("inkscape:collect", NULL);
1246 }
1248 SPStyle const *const obj_style = SP_OBJECT_STYLE(object);
1249 if (obj_style) {
1250 gchar *s = sp_style_write_string(obj_style, SP_STYLE_FLAG_IFSET);
1251 repr->setAttribute("style", ( *s ? s : NULL ));
1252 g_free(s);
1253 } else {
1254 /** \todo I'm not sure what to do in this case. Bug #1165868
1255 * suggests that it can arise, but the submitter doesn't know
1256 * how to do so reliably. The main two options are either
1257 * leave repr's style attribute unchanged, or explicitly clear it.
1258 * Must also consider what to do with property attributes for
1259 * the element; see below.
1260 */
1261 char const *style_str = repr->attribute("style");
1262 if (!style_str) {
1263 style_str = "NULL";
1264 }
1265 g_warning("Item's style is NULL; repr style attribute is %s", style_str);
1266 }
1268 /** \note We treat object->style as authoritative. Its effects have
1269 * been written to the style attribute above; any properties that are
1270 * unset we take to be deliberately unset (e.g. so that clones can
1271 * override the property).
1272 *
1273 * Note that the below has an undesirable consequence of changing the
1274 * appearance on renderers that lack CSS support (e.g. SVG tiny);
1275 * possibly we should write property attributes instead of a style
1276 * attribute.
1277 */
1278 sp_style_unset_property_attrs (object);
1279 }
1281 return repr;
1282 }
1284 /**
1285 * Update this object's XML node with flags value.
1286 */
1287 Inkscape::XML::Node *
1288 SPObject::updateRepr(unsigned int flags) {
1289 if (!SP_OBJECT_IS_CLONED(this)) {
1290 Inkscape::XML::Node *repr=SP_OBJECT_REPR(this);
1291 if (repr) {
1292 return updateRepr(repr->document(), repr, flags);
1293 } else {
1294 g_critical("Attempt to update non-existent repr");
1295 return NULL;
1296 }
1297 } else {
1298 /* cloned objects have no repr */
1299 return NULL;
1300 }
1301 }
1303 /** Used both to create reprs in the original document, and to create
1304 * reprs in another document (e.g. a temporary document used when
1305 * saving as "Plain SVG"
1306 */
1307 Inkscape::XML::Node *
1308 SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags) {
1309 g_assert(doc != NULL);
1311 if (SP_OBJECT_IS_CLONED(this)) {
1312 /* cloned objects have no repr */
1313 return NULL;
1314 }
1315 if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write) {
1316 if (!(flags & SP_OBJECT_WRITE_BUILD) && !repr) {
1317 repr = SP_OBJECT_REPR(this);
1318 }
1319 return ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->write(this, doc, repr, flags);
1320 } else {
1321 g_warning("Class %s does not implement ::write", G_OBJECT_TYPE_NAME(this));
1322 if (!repr) {
1323 if (flags & SP_OBJECT_WRITE_BUILD) {
1324 repr = SP_OBJECT_REPR(this)->duplicate(doc);
1325 }
1326 /// \todo FIXME: else probably error (Lauris) */
1327 } else {
1328 repr->mergeFrom(SP_OBJECT_REPR(this), "id");
1329 }
1330 return repr;
1331 }
1332 }
1334 /* Modification */
1336 /**
1337 * Add \a flags to \a object's as dirtiness flags, and
1338 * recursively add CHILD_MODIFIED flag to
1339 * parent and ancestors (as far up as necessary).
1340 */
1341 void
1342 SPObject::requestDisplayUpdate(unsigned int flags)
1343 {
1344 g_return_if_fail( this->document != NULL );
1346 if (update_in_progress) {
1347 g_print("WARNING: Requested update while update in progress, counter = %d\n", update_in_progress);
1348 }
1350 /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1351 * SP_OBJECT_CHILD_MODIFIED_FLAG */
1352 g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1353 g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1354 g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1356 bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1358 this->uflags |= flags;
1360 /* If requestModified has already been called on this object or one of its children, then we
1361 * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1362 */
1363 if (already_propagated) {
1364 SPObject *parent = SP_OBJECT_PARENT(this);
1365 if (parent) {
1366 parent->requestDisplayUpdate(SP_OBJECT_CHILD_MODIFIED_FLAG);
1367 } else {
1368 SP_OBJECT_DOCUMENT(this)->request_modified();
1369 }
1370 }
1371 }
1373 /**
1374 * Update views
1375 */
1376 void
1377 SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
1378 {
1379 g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1381 update_in_progress ++;
1383 #ifdef SP_OBJECT_DEBUG_CASCADE
1384 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);
1385 #endif
1387 /* Get this flags */
1388 flags |= this->uflags;
1389 /* Copy flags to modified cascade for later processing */
1390 this->mflags |= this->uflags;
1391 /* We have to clear flags here to allow rescheduling update */
1392 this->uflags = 0;
1394 // Merge style if we have good reasons to think that parent style is changed */
1395 /** \todo
1396 * I am not sure whether we should check only propagated
1397 * flag. We are currently assuming that style parsing is
1398 * done immediately. I think this is correct (Lauris).
1399 */
1400 if ((flags & SP_OBJECT_STYLE_MODIFIED_FLAG) && (flags & SP_OBJECT_PARENT_MODIFIED_FLAG)) {
1401 if (this->style && this->parent) {
1402 sp_style_merge_from_parent(this->style, this->parent->style);
1403 }
1404 }
1406 try
1407 {
1408 if (((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update)
1409 ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);
1410 }
1411 catch(...)
1412 {
1413 /** \todo
1414 * in case of catching an exception we need to inform the user somehow that the document is corrupted
1415 * maybe by implementing an document flag documentOk
1416 * or by a modal error dialog
1417 */
1418 g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
1419 }
1421 update_in_progress --;
1422 }
1424 /**
1425 * Request modified always bubbles *up* the tree, as opposed to
1426 * request display update, which trickles down and relies on the
1427 * flags set during this pass...
1428 */
1429 void
1430 SPObject::requestModified(unsigned int flags)
1431 {
1432 g_return_if_fail( this->document != NULL );
1434 /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
1435 * SP_OBJECT_CHILD_MODIFIED_FLAG */
1436 g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
1437 g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
1438 g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
1440 bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
1442 this->mflags |= flags;
1444 /* If requestModified has already been called on this object or one of its children, then we
1445 * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
1446 */
1447 if (already_propagated) {
1448 SPObject *parent=SP_OBJECT_PARENT(this);
1449 if (parent) {
1450 parent->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
1451 } else {
1452 SP_OBJECT_DOCUMENT(this)->request_modified();
1453 }
1454 }
1455 }
1457 /**
1458 * Emits the MODIFIED signal with the object's flags.
1459 * The object's mflags are the original set aside during the update pass for
1460 * later delivery here. Once emitModified() is called, those flags don't
1461 * need to be stored any longer.
1462 */
1463 void
1464 SPObject::emitModified(unsigned int flags)
1465 {
1466 /* only the MODIFIED_CASCADE flag is legal here */
1467 g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
1469 #ifdef SP_OBJECT_DEBUG_CASCADE
1470 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);
1471 #endif
1473 flags |= this->mflags;
1474 /* We have to clear mflags beforehand, as signal handlers may
1475 * make changes and therefore queue new modification notifications
1476 * themselves. */
1477 this->mflags = 0;
1479 g_object_ref(G_OBJECT(this));
1480 SPObjectClass *klass=(SPObjectClass *)G_OBJECT_GET_CLASS(this);
1481 if (klass->modified) {
1482 klass->modified(this, flags);
1483 }
1484 _modified_signal.emit(this, flags);
1485 g_object_unref(G_OBJECT(this));
1486 }
1488 gchar const *
1489 SPObject::getTagName(SPException *ex) const
1490 {
1491 g_assert(repr != NULL);
1492 /* If exception is not clear, return */
1493 if (!SP_EXCEPTION_IS_OK(ex)) {
1494 return NULL;
1495 }
1497 /// \todo fixme: Exception if object is NULL? */
1498 //XML Tree being used here.
1499 return getRepr()->name();
1500 }
1502 gchar const *
1503 SPObject::getAttribute(gchar const *key, SPException *ex) const
1504 {
1505 g_assert(this->repr != NULL);
1506 /* If exception is not clear, return */
1507 if (!SP_EXCEPTION_IS_OK(ex)) {
1508 return NULL;
1509 }
1511 /// \todo fixme: Exception if object is NULL? */
1512 //XML Tree being used here.
1513 return (gchar const *) getRepr()->attribute(key);
1514 }
1516 void
1517 SPObject::setAttribute(gchar const *key, gchar const *value, SPException *ex)
1518 {
1519 g_assert(this->repr != NULL);
1520 /* If exception is not clear, return */
1521 g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1523 /// \todo fixme: Exception if object is NULL? */
1524 //XML Tree being used here.
1525 getRepr()->setAttribute(key, value, false);
1526 }
1528 void
1529 SPObject::removeAttribute(gchar const *key, SPException *ex)
1530 {
1531 /* If exception is not clear, return */
1532 g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
1534 /// \todo fixme: Exception if object is NULL? */
1535 //XML Tree being used here.
1536 getRepr()->setAttribute(key, NULL, false);
1537 }
1539 bool SPObject::storeAsDouble( gchar const *key, double *val ) const
1540 {
1541 g_assert(this->getRepr()!= NULL);
1542 return sp_repr_get_double(((Inkscape::XML::Node *)(this->getRepr())),key,val);
1543 }
1545 /* Helper */
1547 gchar *
1548 SPObject::sp_object_get_unique_id(SPObject *object, gchar const *id)
1549 {
1550 static unsigned long count = 0;
1552 g_assert(SP_IS_OBJECT(object));
1554 count++;
1556 //XML Tree being used here.
1557 gchar const *name = object->getRepr()->name();
1558 g_assert(name != NULL);
1560 gchar const *local = strchr(name, ':');
1561 if (local) {
1562 name = local + 1;
1563 }
1565 if (id != NULL) {
1566 if (object->document->getObjectById(id) == NULL) {
1567 return g_strdup(id);
1568 }
1569 }
1571 size_t const name_len = strlen(name);
1572 size_t const buflen = name_len + (sizeof(count) * 10 / 4) + 1;
1573 gchar *const buf = (gchar *) g_malloc(buflen);
1574 memcpy(buf, name, name_len);
1575 gchar *const count_buf = buf + name_len;
1576 size_t const count_buflen = buflen - name_len;
1577 do {
1578 ++count;
1579 g_snprintf(count_buf, count_buflen, "%lu", count);
1580 } while ( object->document->getObjectById(buf) != NULL );
1581 return buf;
1582 }
1584 /* Style */
1586 /**
1587 * Returns an object style property.
1588 *
1589 * \todo
1590 * fixme: Use proper CSS parsing. The current version is buggy
1591 * in a number of situations where key is a substring of the
1592 * style string other than as a property name (including
1593 * where key is a substring of a property name), and is also
1594 * buggy in its handling of inheritance for properties that
1595 * aren't inherited by default. It also doesn't allow for
1596 * the case where the property is specified but with an invalid
1597 * value (in which case I believe the CSS2 error-handling
1598 * behaviour applies, viz. behave as if the property hadn't
1599 * been specified). Also, the current code doesn't use CRSelEng
1600 * stuff to take a value from stylesheets. Also, we aren't
1601 * setting any hooks to force an update for changes in any of
1602 * the inputs (i.e., in any of the elements that this function
1603 * queries).
1604 *
1605 * \par
1606 * Given that the default value for a property depends on what
1607 * property it is (e.g., whether to inherit or not), and given
1608 * the above comment about ignoring invalid values, and that the
1609 * repr parent isn't necessarily the right element to inherit
1610 * from (e.g., maybe we need to inherit from the referencing
1611 * <use> element instead), we should probably make the caller
1612 * responsible for ascending the repr tree as necessary.
1613 */
1614 gchar const *
1615 SPObject::getStyleProperty(gchar const *key, gchar const *def) const
1616 {
1617 //g_return_val_if_fail(object != NULL, NULL);
1618 //g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
1619 g_return_val_if_fail(key != NULL, NULL);
1621 //XML Tree being used here.
1622 gchar const *style = getRepr()->attribute("style");
1623 if (style) {
1624 size_t const len = strlen(key);
1625 char const *p;
1626 while ( (p = strstr(style, key))
1627 != NULL )
1628 {
1629 p += len;
1630 while ((*p <= ' ') && *p) p++;
1631 if (*p++ != ':') break;
1632 while ((*p <= ' ') && *p) p++;
1633 size_t const inherit_len = sizeof("inherit") - 1;
1634 if (*p
1635 && !(strneq(p, "inherit", inherit_len)
1636 && (p[inherit_len] == '\0'
1637 || p[inherit_len] == ';'
1638 || g_ascii_isspace(p[inherit_len])))) {
1639 return p;
1640 }
1641 }
1642 }
1644 //XML Tree being used here.
1645 gchar const *val = getRepr()->attribute(key);
1646 if (val && !streq(val, "inherit")) {
1647 return val;
1648 }
1649 if (this->parent) {
1650 return (this->parent)->getStyleProperty(key, def);
1651 }
1653 return def;
1654 }
1656 /**
1657 * Lifts SVG version of all root objects to version.
1658 */
1659 void
1660 SPObject::_requireSVGVersion(Inkscape::Version version) {
1661 for ( SPObject::ParentIterator iter=this ; iter ; ++iter ) {
1662 SPObject *object=iter;
1663 if (SP_IS_ROOT(object)) {
1664 SPRoot *root=SP_ROOT(object);
1665 if ( root->version.svg < version ) {
1666 root->version.svg = version;
1667 }
1668 }
1669 }
1670 }
1672 /**
1673 * Returns previous object in sibling list or NULL.
1674 */
1675 SPObject *
1676 SPObject::prev()
1677 {
1678 SPObject *parent = SP_OBJECT_PARENT(this);
1679 for ( SPObject *i = parent->first_child(); i; i = SP_OBJECT_NEXT(i) ) {
1680 if (SP_OBJECT_NEXT(i) == this)
1681 return i;
1682 }
1683 return NULL;
1684 }
1686 /* Titles and descriptions */
1688 /* Note:
1689 Titles and descriptions are stored in 'title' and 'desc' child elements
1690 (see section 5.4 of the SVG 1.0 and 1.1 specifications). The spec allows
1691 an element to have more than one 'title' child element, but strongly
1692 recommends against this and requires using the first one if a choice must
1693 be made. The same applies to 'desc' elements. Therefore, these functions
1694 ignore all but the first 'title' child element and first 'desc' child
1695 element, except when deleting a title or description.
1696 */
1698 /**
1699 * Returns the title of this object, or NULL if there is none.
1700 * The caller must free the returned string using g_free() - see comment
1701 * for getTitleOrDesc() below.
1702 */
1703 gchar *
1704 SPObject::title() const
1705 {
1706 return getTitleOrDesc("svg:title");
1707 }
1709 /**
1710 * Sets the title of this object
1711 * A NULL first argument is interpreted as meaning that the existing title
1712 * (if any) should be deleted.
1713 * The second argument is optional - see setTitleOrDesc() below for details.
1714 */
1715 bool
1716 SPObject::setTitle(gchar const *title, bool verbatim)
1717 {
1718 return setTitleOrDesc(title, "svg:title", verbatim);
1719 }
1721 /**
1722 * Returns the description of this object, or NULL if there is none.
1723 * The caller must free the returned string using g_free() - see comment
1724 * for getTitleOrDesc() below.
1725 */
1726 gchar *
1727 SPObject::desc() const
1728 {
1729 return getTitleOrDesc("svg:desc");
1730 }
1732 /**
1733 * Sets the description of this object.
1734 * A NULL first argument is interpreted as meaning that the existing
1735 * description (if any) should be deleted.
1736 * The second argument is optional - see setTitleOrDesc() below for details.
1737 */
1738 bool
1739 SPObject::setDesc(gchar const *desc, bool verbatim)
1740 {
1741 return setTitleOrDesc(desc, "svg:desc", verbatim);
1742 }
1744 /**
1745 * Returns the title or description of this object, or NULL if there is none.
1746 *
1747 * The SVG spec allows 'title' and 'desc' elements to contain text marked up
1748 * using elements from other namespaces. Therefore, this function cannot
1749 * in general just return a pointer to an existing string - it must instead
1750 * construct a string containing the title or description without the mark-up.
1751 * Consequently, the return value is a newly allocated string (or NULL), and
1752 * must be freed (using g_free()) by the caller.
1753 */
1754 gchar *
1755 SPObject::getTitleOrDesc(gchar const *svg_tagname) const
1756 {
1757 SPObject *elem = findFirstChild(svg_tagname);
1758 if (elem == NULL) return NULL;
1759 return g_string_free(elem->textualContent(), FALSE);
1760 }
1762 /**
1763 * Sets or deletes the title or description of this object.
1764 * A NULL 'value' argument causes the title or description to be deleted.
1765 *
1766 * 'verbatim' parameter:
1767 * If verbatim==true, then the title or description is set to exactly the
1768 * specified value. If verbatim==false then two exceptions are made:
1769 * (1) If the specified value is just whitespace, then the title/description
1770 * is deleted.
1771 * (2) If the specified value is the same as the current value except for
1772 * mark-up, then the current value is left unchanged.
1773 * This is usually the desired behaviour, so 'verbatim' defaults to false for
1774 * setTitle() and setDesc().
1775 *
1776 * The return value is true if a change was made to the title/description,
1777 * and usually false otherwise.
1778 */
1779 bool
1780 SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim)
1781 {
1782 if (!verbatim) {
1783 // If the new title/description is just whitespace,
1784 // treat it as though it were NULL.
1785 if (value) {
1786 bool just_whitespace = true;
1787 for (const gchar *cp = value; *cp; ++cp) {
1788 if (!std::strchr("\r\n \t", *cp)) {
1789 just_whitespace = false;
1790 break;
1791 }
1792 }
1793 if (just_whitespace) value = NULL;
1794 }
1795 // Don't stomp on mark-up if there is no real change.
1796 if (value) {
1797 gchar *current_value = getTitleOrDesc(svg_tagname);
1798 if (current_value) {
1799 bool different = std::strcmp(current_value, value);
1800 g_free(current_value);
1801 if (!different) return false;
1802 }
1803 }
1804 }
1806 SPObject *elem = findFirstChild(svg_tagname);
1808 if (value == NULL) {
1809 if (elem == NULL) return false;
1810 // delete the title/description(s)
1811 while (elem) {
1812 elem->deleteObject();
1813 elem = findFirstChild(svg_tagname);
1814 }
1815 return true;
1816 }
1818 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
1820 if (elem == NULL) {
1821 // create a new 'title' or 'desc' element, putting it at the
1822 // beginning (in accordance with the spec's recommendations)
1823 Inkscape::XML::Node *xml_elem = xml_doc->createElement(svg_tagname);
1824 repr->addChild(xml_elem, NULL);
1825 elem = document->getObjectByRepr(xml_elem);
1826 Inkscape::GC::release(xml_elem);
1827 }
1828 else {
1829 // remove the current content of the 'text' or 'desc' element
1830 SPObject *child;
1831 while (NULL != (child = elem->firstChild())) child->deleteObject();
1832 }
1834 // add the new content
1835 elem->appendChildRepr(xml_doc->createTextNode(value));
1836 return true;
1837 }
1839 /**
1840 * Find the first child of this object with a given tag name,
1841 * and return it. Returns NULL if there is no matching child.
1842 */
1843 SPObject *
1844 SPObject::findFirstChild(gchar const *tagname) const
1845 {
1846 for (SPObject *child = children; child; child = child->next)
1847 {
1848 if (child->repr->type() == Inkscape::XML::ELEMENT_NODE &&
1849 !strcmp(child->repr->name(), tagname)) return child;
1850 }
1851 return NULL;
1852 }
1854 /**
1855 * Return the full textual content of an element (typically all the
1856 * content except the tags).
1857 * Must not be used on anything except elements.
1858 */
1859 GString*
1860 SPObject::textualContent() const
1861 {
1862 GString* text = g_string_new("");
1864 for (const SPObject *child = firstChild(); child; child = child->next)
1865 {
1866 Inkscape::XML::NodeType child_type = child->repr->type();
1868 if (child_type == Inkscape::XML::ELEMENT_NODE) {
1869 GString * new_text = child->textualContent();
1870 g_string_append(text, new_text->str);
1871 g_string_free(new_text, TRUE);
1872 }
1873 else if (child_type == Inkscape::XML::TEXT_NODE) {
1874 g_string_append(text, child->repr->content());
1875 }
1876 }
1877 return text;
1878 }
1880 /*
1881 Local Variables:
1882 mode:c++
1883 c-file-style:"stroustrup"
1884 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1885 indent-tabs-mode:nil
1886 fill-column:99
1887 End:
1888 */
1889 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :