summaryrefslogtreecommitdiffstats
path: root/src/sp-object.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/sp-object.cpp')
-rw-r--r--src/sp-object.cpp1702
1 files changed, 0 insertions, 1702 deletions
diff --git a/src/sp-object.cpp b/src/sp-object.cpp
deleted file mode 100644
index bc930a430..000000000
--- a/src/sp-object.cpp
+++ /dev/null
@@ -1,1702 +0,0 @@
-/*
- * SPObject implementation.
- *
- * Authors:
- * Lauris Kaplinski <lauris@kaplinski.com>
- * bulia byak <buliabyak@users.sf.net>
- * Stephen Silver <sasilver@users.sourceforge.net>
- * Jon A. Cruz <jon@joncruz.org>
- * Abhishek Sharma
- * Adrian Boguszewski
- *
- * Copyright (C) 1999-2016 authors
- * Copyright (C) 2001-2002 Ximian, Inc.
- *
- * Released under GNU GPL, read the file 'COPYING' for more information
- */
-
-#include <cstring>
-#include <string>
-#include <boost/range/adaptor/transformed.hpp>
-
-#include "helper/sp-marshal.h"
-#include "xml/node-event-vector.h"
-#include "attributes.h"
-#include "attribute-rel-util.h"
-#include "color-profile.h"
-#include "document.h"
-#include "preferences.h"
-#include "style.h"
-#include "sp-factory.h"
-#include "sp-paint-server.h"
-#include "sp-root.h"
-#include "sp-style-elem.h"
-#include "sp-script.h"
-#include "streq.h"
-#include "strneq.h"
-#include "xml/node-fns.h"
-#include "debug/event-tracker.h"
-#include "debug/simple-event.h"
-#include "debug/demangle.h"
-#include "util/format.h"
-#include "util/longest-common-suffix.h"
-
-using std::memcpy;
-using std::strchr;
-using std::strcmp;
-using std::strlen;
-using std::strstr;
-
-#define noSP_OBJECT_DEBUG_CASCADE
-
-#define noSP_OBJECT_DEBUG
-
-#ifdef SP_OBJECT_DEBUG
-# define debug(f, a...) { g_print("%s(%d) %s:", \
- __FILE__,__LINE__,__FUNCTION__); \
- g_print(f, ## a); \
- g_print("\n"); \
- }
-#else
-# define debug(f, a...) /* */
-#endif
-
-// Define to enable indented tracing of SPObject.
-//#define OBJECT_TRACE
-unsigned SPObject::indent_level = 0;
-
-guint update_in_progress = 0; // guard against update-during-update
-
-Inkscape::XML::NodeEventVector object_event_vector = {
- SPObject::repr_child_added,
- SPObject::repr_child_removed,
- SPObject::repr_attr_changed,
- SPObject::repr_content_changed,
- SPObject::repr_order_changed
-};
-
-/**
- * A friend class used to set internal members on SPObject so as to not expose settors in SPObject's public API
- */
-class SPObjectImpl
-{
-public:
-
-/**
- * Null's the id member of an SPObject without attempting to free prior contents.
- *
- * @param[inout] obj Pointer to the object which's id shall be nulled.
- */
- static void setIdNull( SPObject* obj ) {
- if (obj) {
- obj->id = 0;
- }
- }
-
-/**
- * Sets the id member of an object, freeing any prior content.
- *
- * @param[inout] obj Pointer to the object which's id shall be set.
- * @param[in] id New id
- */
- static void setId( SPObject* obj, gchar const* id ) {
- if (obj && (id != obj->id) ) {
- if (obj->id) {
- g_free(obj->id);
- obj->id = 0;
- }
- if (id) {
- obj->id = g_strdup(id);
- }
- }
- }
-};
-
-/**
- * Constructor, sets all attributes to default values.
- */
-SPObject::SPObject()
- : cloned(0), clone_original(NULL), uflags(0), mflags(0), hrefcount(0), _total_hrefcount(0),
- document(NULL), parent(NULL), id(NULL), repr(NULL), refCount(1), hrefList(std::list<SPObject*>()),
- _successor(NULL), _collection_policy(SPObject::COLLECT_WITH_PARENT),
- _label(NULL), _default_label(NULL)
-{
- debug("id=%p, typename=%s",this, g_type_name_from_instance((GTypeInstance*)this));
-
- //used XML Tree here.
- this->getRepr(); // TODO check why this call is made
-
- SPObjectImpl::setIdNull(this);
-
- // FIXME: now we create style for all objects, but per SVG, only the following can have style attribute:
- // vg, g, defs, desc, title, symbol, use, image, switch, path, rect, circle, ellipse, line, polyline,
- // polygon, text, tspan, tref, textPath, altGlyph, glyphRef, marker, linearGradient, radialGradient,
- // stop, pattern, clipPath, mask, filter, feImage, a, font, glyph, missing-glyph, foreignObject
- this->style = new SPStyle( NULL, this ); // Is it necessary to call with "this"?
- this->context_style = NULL;
-}
-
-/**
- * Destructor, frees the used memory and unreferences a potential successor of the object.
- */
-SPObject::~SPObject() {
- g_free(this->_label);
- g_free(this->_default_label);
-
- this->_label = NULL;
- this->_default_label = NULL;
-
- if (this->_successor) {
- sp_object_unref(this->_successor, NULL);
- this->_successor = NULL;
- }
- if (parent) {
- parent->children.erase(parent->children.iterator_to(*this));
- }
-
- if( style == NULL ) {
- // style pointer could be NULL if unreffed too many times.
- // Conjecture: style pointer is never NULL.
- std::cerr << "SPObject::~SPObject(): style pointer is NULL" << std::endl;
- } else if( style->refCount() > 1 ) {
- // Conjecture: style pointer should be unreffed by other classes before reaching here.
- // Conjecture is false for SPTSpan where ref is held by InputStreamTextSource.
- // As an additional note:
- // The outer tspan of a nested tspan will result in a ref count of five: one for the
- // TSpan itself, one for the InputStreamTextSource instance before the inner tspan and
- // one for the one after, along with one for each corresponding DrawingText instance.
- // std::cerr << "SPObject::~SPObject(): someone else still holding ref to style" << std::endl;
- //
- sp_style_unref( this->style );
- } else {
- delete this->style;
- }
-}
-
-// CPPIFY: make pure virtual
-void SPObject::read_content() {
- //throw;
-}
-
-void SPObject::update(SPCtx* /*ctx*/, unsigned int /*flags*/) {
- //throw;
-}
-
-void SPObject::modified(unsigned int /*flags*/) {
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::modified (default) (empty function)" );
- objectTrace( "SPObject::modified (default)", false );
-#endif
- //throw;
-}
-
-namespace {
-
-namespace Debug = Inkscape::Debug;
-namespace Util = Inkscape::Util;
-
-typedef Debug::SimpleEvent<Debug::Event::REFCOUNT> BaseRefCountEvent;
-
-class RefCountEvent : public BaseRefCountEvent {
-public:
- RefCountEvent(SPObject *object, int bias, Util::ptr_shared name)
- : BaseRefCountEvent(name)
- {
- _addProperty("object", Util::format("%p", object));
- _addProperty("class", Debug::demangle(g_type_name(G_TYPE_FROM_INSTANCE(object))));
- _addProperty("new-refcount", Util::format("%d", G_OBJECT(object)->ref_count + bias));
- }
-};
-
-class RefEvent : public RefCountEvent {
-public:
- RefEvent(SPObject *object)
- : RefCountEvent(object, 1, Util::share_static_string("sp-object-ref"))
- {}
-};
-
-class UnrefEvent : public RefCountEvent {
-public:
- UnrefEvent(SPObject *object)
- : RefCountEvent(object, -1, Util::share_static_string("sp-object-unref"))
- {}
-};
-
-}
-
-gchar const* SPObject::getId() const {
- return id;
-}
-
-Inkscape::XML::Node * SPObject::getRepr() {
- return repr;
-}
-
-Inkscape::XML::Node const* SPObject::getRepr() const{
- return repr;
-}
-
-
-SPObject *sp_object_ref(SPObject *object, SPObject *owner)
-{
- g_return_val_if_fail(object != NULL, NULL);
- g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
- g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
-
- Inkscape::Debug::EventTracker<RefEvent> tracker(object);
- //g_object_ref(G_OBJECT(object));
- object->refCount++;
- return object;
-}
-
-SPObject *sp_object_unref(SPObject *object, SPObject *owner)
-{
- g_return_val_if_fail(object != NULL, NULL);
- g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
- g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
-
- Inkscape::Debug::EventTracker<UnrefEvent> tracker(object);
- //g_object_unref(G_OBJECT(object));
- object->refCount--;
-
- if (object->refCount <= 0) {
- delete object;
- }
-
- return NULL;
-}
-
-SPObject *sp_object_href(SPObject *object, SPObject* owner)
-{
- g_return_val_if_fail(object != NULL, NULL);
- g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
-
- object->hrefcount++;
- object->_updateTotalHRefCount(1);
-
- if(owner)
- object->hrefList.push_front(owner);
-
- return object;
-}
-
-SPObject *sp_object_hunref(SPObject *object, SPObject* owner)
-{
- g_return_val_if_fail(object != NULL, NULL);
- g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
- g_return_val_if_fail(object->hrefcount > 0, NULL);
-
- object->hrefcount--;
- object->_updateTotalHRefCount(-1);
-
- if(owner)
- object->hrefList.remove(owner);
-
- return NULL;
-}
-
-void SPObject::_updateTotalHRefCount(int increment) {
- SPObject *topmost_collectable = NULL;
- for ( SPObject *iter = this ; iter ; iter = iter->parent ) {
- iter->_total_hrefcount += increment;
- if ( iter->_total_hrefcount < iter->hrefcount ) {
- g_critical("HRefs overcounted");
- }
- if ( iter->_total_hrefcount == 0 &&
- iter->_collection_policy != COLLECT_WITH_PARENT )
- {
- topmost_collectable = iter;
- }
- }
- if (topmost_collectable) {
- topmost_collectable->requestOrphanCollection();
- }
-}
-
-bool SPObject::isAncestorOf(SPObject const *object) const {
- g_return_val_if_fail(object != NULL, false);
- object = object->parent;
- while (object) {
- if ( object == this ) {
- return true;
- }
- object = object->parent;
- }
- return false;
-}
-
-namespace {
-
-bool same_objects(SPObject const &a, SPObject const &b) {
- return &a == &b;
-}
-
-}
-
-SPObject const *SPObject::nearestCommonAncestor(SPObject const *object) const {
- g_return_val_if_fail(object != NULL, NULL);
-
- using Inkscape::Algorithms::longest_common_suffix;
- return longest_common_suffix<SPObject::ConstParentIterator>(this, object, NULL, &same_objects);
-}
-
-static SPObject const *AncestorSon(SPObject const *obj, SPObject const *ancestor) {
- SPObject const *result = 0;
- if ( obj && ancestor ) {
- if (obj->parent == ancestor) {
- result = obj;
- } else {
- result = AncestorSon(obj->parent, ancestor);
- }
- }
- return result;
-}
-
-int sp_object_compare_position(SPObject const *first, SPObject const *second)
-{
- int result = 0;
- if (first != second) {
- SPObject const *ancestor = first->nearestCommonAncestor(second);
- // Need a common ancestor to be able to compare
- if ( ancestor ) {
- // we have an object and its ancestor (should not happen when sorting selection)
- if (ancestor == first) {
- result = 1;
- } else if (ancestor == second) {
- result = -1;
- } else {
- SPObject const *to_first = AncestorSon(first, ancestor);
- SPObject const *to_second = AncestorSon(second, ancestor);
-
- g_assert(to_second->parent == to_first->parent);
-
- result = sp_repr_compare_position(to_first->getRepr(), to_second->getRepr());
- }
- }
- }
- return result;
-}
-
-bool sp_object_compare_position_bool(SPObject const *first, SPObject const *second){
- return sp_object_compare_position(first,second)<0;
-}
-
-
-SPObject *SPObject::appendChildRepr(Inkscape::XML::Node *repr) {
- if ( !cloned ) {
- getRepr()->appendChild(repr);
- return document->getObjectByRepr(repr);
- } else {
- g_critical("Attempt to append repr as child of cloned object");
- return NULL;
- }
-}
-
-void SPObject::setCSS(SPCSSAttr *css, gchar const *attr)
-{
- g_assert(this->getRepr() != NULL);
- sp_repr_css_set(this->getRepr(), css, attr);
-}
-
-void SPObject::changeCSS(SPCSSAttr *css, gchar const *attr)
-{
- g_assert(this->getRepr() != NULL);
- sp_repr_css_change(this->getRepr(), css, attr);
-}
-
-std::vector<SPObject*> SPObject::childList(bool add_ref, Action) {
- std::vector<SPObject*> l;
- for (auto& child: children) {
- if (add_ref) {
- sp_object_ref(&child);
- }
- l.push_back(&child);
- }
- return l;
-
-}
-
-gchar const *SPObject::label() const {
- return _label;
-}
-
-gchar const *SPObject::defaultLabel() const {
- if (_label) {
- return _label;
- } else {
- if (!_default_label) {
- if (getId()) {
- _default_label = g_strdup_printf("#%s", getId());
- } else {
- _default_label = g_strdup_printf("<%s>", getRepr()->name());
- }
- }
- return _default_label;
- }
-}
-
-void SPObject::setLabel(gchar const *label)
-{
- getRepr()->setAttribute("inkscape:label", label, false);
-}
-
-
-void SPObject::requestOrphanCollection() {
- g_return_if_fail(document != NULL);
- Inkscape::Preferences *prefs = Inkscape::Preferences::get();
-
- // do not remove style or script elements (Bug #276244)
- if (dynamic_cast<SPStyleElem *>(this)) {
- // leave it
- } else if (dynamic_cast<SPScript *>(this)) {
- // leave it
-
- } else if ((! prefs->getBool("/options/cleanupswatches/value", false)) && SP_IS_PAINT_SERVER(this) && static_cast<SPPaintServer*>(this)->isSwatch() ) {
- // leave it
- } else if (IS_COLORPROFILE(this)) {
- // leave it
- } else {
- document->queueForOrphanCollection(this);
-
- /** \todo
- * This is a temporary hack added to make fill&stroke rebuild its
- * gradient list when the defs are vacuumed. gradient-vector.cpp
- * listens to the modified signal on defs, and now we give it that
- * signal. Mental says that this should be made automatic by
- * merging SPObjectGroup with SPObject; SPObjectGroup would issue
- * this signal automatically. Or maybe just derive SPDefs from
- * SPObjectGroup?
- */
-
- this->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
- }
-}
-
-void SPObject::_sendDeleteSignalRecursive() {
- for (auto& child: children) {
- child._delete_signal.emit(&child);
- child._sendDeleteSignalRecursive();
- }
-}
-
-void SPObject::deleteObject(bool propagate, bool propagate_descendants)
-{
- sp_object_ref(this, NULL);
- if ( SP_IS_LPE_ITEM(this) && SP_LPE_ITEM(this)->hasPathEffect()) {
- SP_LPE_ITEM(this)->removeAllPathEffects(false);
- }
- if (propagate) {
- _delete_signal.emit(this);
- }
- if (propagate_descendants) {
- this->_sendDeleteSignalRecursive();
- }
-
- Inkscape::XML::Node *repr = getRepr();
- if (repr && repr->parent()) {
- sp_repr_unparent(repr);
- }
-
- if (_successor) {
- _successor->deleteObject(propagate, propagate_descendants);
- }
- sp_object_unref(this, NULL);
-}
-
-void SPObject::cropToObject(SPObject *except)
-{
- std::vector<SPObject*> toDelete;
- for (auto& child: children) {
- if (SP_IS_ITEM(&child)) {
- if (child.isAncestorOf(except)) {
- child.cropToObject(except);
- } else if(&child != except) {
- toDelete.push_back(&child);
- }
- }
- }
- for (std::size_t i = 0; i < toDelete.size(); ++i) {
- (toDelete[i])->deleteObject(true, true);
- }
-}
-
-void SPObject::attach(SPObject *object, SPObject *prev)
-{
- //g_return_if_fail(parent != NULL);
- //g_return_if_fail(SP_IS_OBJECT(parent));
- g_return_if_fail(object != NULL);
- g_return_if_fail(SP_IS_OBJECT(object));
- g_return_if_fail(!prev || SP_IS_OBJECT(prev));
- g_return_if_fail(!prev || prev->parent == this);
- g_return_if_fail(!object->parent);
-
- sp_object_ref(object, this);
- object->parent = this;
- this->_updateTotalHRefCount(object->_total_hrefcount);
-
- auto it = children.begin();
- if (prev != nullptr) {
- it = ++children.iterator_to(*prev);
- }
- children.insert(it, *object);
-
- if (!object->xml_space.set)
- object->xml_space.value = this->xml_space.value;
-}
-
-void SPObject::reorder(SPObject* obj, SPObject* prev) {
- g_return_if_fail(obj != nullptr);
- g_return_if_fail(obj->parent);
- g_return_if_fail(obj->parent == this);
- g_return_if_fail(obj != prev);
- g_return_if_fail(!prev || prev->parent == obj->parent);
-
- auto it = children.begin();
- if (prev != nullptr) {
- it = ++children.iterator_to(*prev);
- }
-
- children.splice(it, children, children.iterator_to(*obj));
-}
-
-void SPObject::detach(SPObject *object)
-{
- //g_return_if_fail(parent != NULL);
- //g_return_if_fail(SP_IS_OBJECT(parent));
- g_return_if_fail(object != NULL);
- g_return_if_fail(SP_IS_OBJECT(object));
- g_return_if_fail(object->parent == this);
-
- children.erase(children.iterator_to(*object));
- object->releaseReferences();
-
- object->parent = NULL;
-
- this->_updateTotalHRefCount(-object->_total_hrefcount);
- sp_object_unref(object, this);
-}
-
-SPObject *SPObject::get_child_by_repr(Inkscape::XML::Node *repr)
-{
- g_return_val_if_fail(repr != NULL, NULL);
- SPObject *result = nullptr;
-
- if (children.size() > 0 && children.back().getRepr() == repr) {
- result = &children.back(); // optimization for common scenario
- } else {
- for (auto& child: children) {
- if (child.getRepr() == repr) {
- result = &child;
- break;
- }
- }
- }
- return result;
-}
-
-void SPObject::child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref) {
- SPObject* object = this;
-
- const std::string type_string = NodeTraits::get_type_string(*child);
-
- SPObject* ochild = SPFactory::createObject(type_string);
- if (ochild == NULL) {
- // Currenty, there are many node types that do not have
- // corresponding classes in the SPObject tree.
- // (rdf:RDF, inkscape:clipboard, ...)
- // Thus, simply ignore this case for now.
- return;
- }
-
- SPObject *prev = ref ? object->get_child_by_repr(ref) : NULL;
- object->attach(ochild, prev);
- sp_object_unref(ochild, NULL);
-
- ochild->invoke_build(object->document, child, object->cloned);
-}
-
-void SPObject::release() {
- SPObject* object = this;
- debug("id=%p, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
- auto tmp = children | boost::adaptors::transformed([](SPObject& obj){return &obj;});
- std::vector<SPObject *> toRelease(tmp.begin(), tmp.end());
-
- for (auto& p: toRelease) {
- object->detach(p);
- }
-}
-
-void SPObject::remove_child(Inkscape::XML::Node* child) {
- debug("id=%p, typename=%s", this, g_type_name_from_instance((GTypeInstance*)this));
-
- SPObject *ochild = this->get_child_by_repr(child);
-
- // If the xml node has got a corresponding child in the object tree
- if (ochild) {
- this->detach(ochild);
- }
-}
-
-void SPObject::order_changed(Inkscape::XML::Node *child, Inkscape::XML::Node * /*old_ref*/, Inkscape::XML::Node *new_ref) {
- SPObject* object = this;
-
- SPObject *ochild = object->get_child_by_repr(child);
- g_return_if_fail(ochild != NULL);
- SPObject *prev = new_ref ? object->get_child_by_repr(new_ref) : NULL;
- object->reorder(ochild, prev);
- ochild->_position_changed_signal.emit(ochild);
-}
-
-void SPObject::build(SPDocument *document, Inkscape::XML::Node *repr) {
-
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::build" );
-#endif
- SPObject* object = this;
-
- /* Nothing specific here */
- debug("id=%p, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
-
- object->readAttr("xml:space");
- object->readAttr("inkscape:label");
- object->readAttr("inkscape:collect");
- if(object->cloned && (repr->attribute("id")) ) // The cases where this happens are when the "original" has no id. This happens
- // if it is a SPString (a TextNode, e.g. in a <title>), or when importing
- // stuff externally modified to have no id.
- object->clone_original = document->getObjectById(repr->attribute("id"));
-
- for (Inkscape::XML::Node *rchild = repr->firstChild() ; rchild != NULL; rchild = rchild->next()) {
- const std::string typeString = NodeTraits::get_type_string(*rchild);
-
- SPObject* child = SPFactory::createObject(typeString);
- if (child == NULL) {
- // Currenty, there are many node types that do not have
- // corresponding classes in the SPObject tree.
- // (rdf:RDF, inkscape:clipboard, ...)
- // Thus, simply ignore this case for now.
- continue;
- }
-
- object->attach(child, object->lastChild());
- sp_object_unref(child, NULL);
- child->invoke_build(document, rchild, object->cloned);
- }
-
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::build", false );
-#endif
-}
-
-void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
-{
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::invoke_build" );
-#endif
- debug("id=%p, typename=%s", this, g_type_name_from_instance((GTypeInstance*)this));
-
- //g_assert(object != NULL);
- //g_assert(SP_IS_OBJECT(object));
- g_assert(document != NULL);
- g_assert(repr != NULL);
-
- g_assert(this->document == NULL);
- g_assert(this->repr == NULL);
- g_assert(this->getId() == NULL);
-
- /* Bookkeeping */
-
- this->document = document;
- this->repr = repr;
- if (!cloned) {
- Inkscape::GC::anchor(repr);
- }
- this->cloned = cloned;
-
- /* Invoke derived methods, if any */
- this->build(document, repr);
-
- if ( !cloned ) {
- this->document->bindObjectToRepr(this->repr, this);
-
- if (Inkscape::XML::id_permitted(this->repr)) {
- /* If we are not cloned, and not seeking, force unique id */
- gchar const *id = this->repr->attribute("id");
- if (!document->isSeeking()) {
- {
- gchar *realid = sp_object_get_unique_id(this, id);
- g_assert(realid != NULL);
-
- this->document->bindObjectToId(realid, this);
- SPObjectImpl::setId(this, realid);
- g_free(realid);
- }
-
- /* Redefine ID, if required */
- if ((id == NULL) || (strcmp(id, this->getId()) != 0)) {
- this->repr->setAttribute("id", this->getId());
- }
- } else if (id) {
- // bind if id, but no conflict -- otherwise, we can expect
- // a subsequent setting of the id attribute
- if (!this->document->getObjectById(id)) {
- this->document->bindObjectToId(id, this);
- SPObjectImpl::setId(this, id);
- }
- }
- }
- } else {
- g_assert(this->getId() == NULL);
- }
-
-
- /* Signalling (should be connected AFTER processing derived methods */
- sp_repr_add_listener(repr, &object_event_vector, this);
-
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::invoke_build", false );
-#endif
-}
-
-int SPObject::getIntAttribute(char const *key, int def)
-{
- sp_repr_get_int(getRepr(),key,&def);
- return def;
-}
-
-unsigned SPObject::getPosition(){
- g_assert(this->repr);
-
- return repr->position();
-}
-
-void SPObject::appendChild(Inkscape::XML::Node *child) {
- g_assert(this->repr);
-
- repr->appendChild(child);
-}
-
-SPObject* SPObject::nthChild(unsigned index) {
- g_assert(this->repr);
- if (hasChildren()) {
- std::vector<SPObject*> l;
- unsigned counter = 0;
- for (auto& child: children) {
- if (counter == index) {
- return &child;
- }
- counter++;
- }
- }
- return NULL;
-}
-
-void SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node * prev)
-{
- g_assert(this->repr);
-
- repr->addChild(child,prev);
-}
-
-void SPObject::releaseReferences() {
- g_assert(this->document);
- g_assert(this->repr);
-
- sp_repr_remove_listener_by_data(this->repr, this);
-
- this->_release_signal.emit(this);
-
- this->release();
-
- /* all hrefs should be released by the "release" handlers */
- g_assert(this->hrefcount == 0);
-
- if (!cloned) {
- if (this->id) {
- this->document->bindObjectToId(this->id, NULL);
- }
- g_free(this->id);
- this->id = NULL;
-
- g_free(this->_default_label);
- this->_default_label = NULL;
-
- this->document->bindObjectToRepr(this->repr, NULL);
-
- Inkscape::GC::release(this->repr);
- } else {
- g_assert(!this->id);
- }
-
- // style belongs to SPObject, we should not need to unref here.
- // if (this->style) {
- // this->style = sp_style_unref(this->style);
- // }
-
- this->document = NULL;
- this->repr = NULL;
-}
-
-
-SPObject *SPObject::getPrev()
-{
- SPObject *prev = nullptr;
- if (parent && !parent->children.empty() && &parent->children.front() != this) {
- prev = &*(--parent->children.iterator_to(*this));
- }
- return prev;
-}
-
-SPObject* SPObject::getNext()
-{
- SPObject *next = nullptr;
- if (parent && !parent->children.empty() && &parent->children.back() != this) {
- next = &*(++parent->children.iterator_to(*this));
- }
- return next;
-}
-
-void SPObject::repr_child_added(Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data)
-{
- SPObject *object = SP_OBJECT(data);
-
- object->child_added(child, ref);
-}
-
-void SPObject::repr_child_removed(Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node * /*ref*/, gpointer data)
-{
- SPObject *object = SP_OBJECT(data);
-
- object->remove_child(child);
-}
-
-void SPObject::repr_order_changed(Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data)
-{
- SPObject *object = SP_OBJECT(data);
-
- object->order_changed(child, old, newer);
-}
-
-void SPObject::set(unsigned int key, gchar const* value) {
-
-#ifdef OBJECT_TRACE
- std::stringstream temp;
- temp << "SPObject::set: " << key << " " << (value?value:"null");
- objectTrace( temp.str() );
-#endif
-
- g_assert(key != SP_ATTR_INVALID);
-
- SPObject* object = this;
-
- switch (key) {
- case SP_ATTR_ID:
-
- //XML Tree being used here.
- if ( !object->cloned && object->getRepr()->type() == Inkscape::XML::ELEMENT_NODE ) {
- SPDocument *document=object->document;
- SPObject *conflict=NULL;
-
- gchar const *new_id = value;
-
- if (new_id) {
- conflict = document->getObjectById((char const *)new_id);
- }
-
- if ( conflict && conflict != object ) {
- if (!document->isSeeking()) {
- sp_object_ref(conflict, NULL);
- // give the conflicting object a new ID
- gchar *new_conflict_id = sp_object_get_unique_id(conflict, NULL);
- conflict->getRepr()->setAttribute("id", new_conflict_id);
- g_free(new_conflict_id);
- sp_object_unref(conflict, NULL);
- } else {
- new_id = NULL;
- }
- }
-
- if (object->getId()) {
- document->bindObjectToId(object->getId(), NULL);
- SPObjectImpl::setId(object, 0);
- }
-
- if (new_id) {
- SPObjectImpl::setId(object, new_id);
- document->bindObjectToId(object->getId(), object);
- }
-
- g_free(object->_default_label);
- object->_default_label = NULL;
- }
- break;
- case SP_ATTR_INKSCAPE_LABEL:
- g_free(object->_label);
- if (value) {
- object->_label = g_strdup(value);
- } else {
- object->_label = NULL;
- }
- g_free(object->_default_label);
- object->_default_label = NULL;
- break;
- case SP_ATTR_INKSCAPE_COLLECT:
- if ( value && !strcmp(value, "always") ) {
- object->setCollectionPolicy(SPObject::ALWAYS_COLLECT);
- } else {
- object->setCollectionPolicy(SPObject::COLLECT_WITH_PARENT);
- }
- break;
- case SP_ATTR_XML_SPACE:
- if (value && !strcmp(value, "preserve")) {
- object->xml_space.value = SP_XML_SPACE_PRESERVE;
- object->xml_space.set = TRUE;
- } else if (value && !strcmp(value, "default")) {
- object->xml_space.value = SP_XML_SPACE_DEFAULT;
- object->xml_space.set = TRUE;
- } else if (object->parent) {
- SPObject *parent;
- parent = object->parent;
- object->xml_space.value = parent->xml_space.value;
- }
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
- break;
- case SP_ATTR_STYLE:
- object->style->readFromObject( object );
- object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
- break;
- default:
- break;
- }
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::set", false );
-#endif
-}
-
-void SPObject::setKeyValue(unsigned int key, gchar const *value)
-{
- //g_assert(object != NULL);
- //g_assert(SP_IS_OBJECT(object));
-
- this->set(key, value);
-}
-
-void SPObject::readAttr(gchar const *key)
-{
- //g_assert(object != NULL);
- //g_assert(SP_IS_OBJECT(object));
- g_assert(key != NULL);
-
- //XML Tree being used here.
- g_assert(this->getRepr() != NULL);
-
- unsigned int keyid = sp_attribute_lookup(key);
- if (keyid != SP_ATTR_INVALID) {
- /* Retrieve the 'key' attribute from the object's XML representation */
- gchar const *value = getRepr()->attribute(key);
-
- setKeyValue(keyid, value);
- }
-}
-
-void SPObject::repr_attr_changed(Inkscape::XML::Node * /*repr*/, gchar const *key, gchar const * /*oldval*/, gchar const * /*newval*/, bool is_interactive, gpointer data)
-{
- SPObject *object = SP_OBJECT(data);
-
- object->readAttr(key);
-
- // manual changes to extension attributes require the normal
- // attributes, which depend on them, to be updated immediately
- if (is_interactive) {
- object->updateRepr(0);
- }
-}
-
-void SPObject::repr_content_changed(Inkscape::XML::Node * /*repr*/, gchar const * /*oldcontent*/, gchar const * /*newcontent*/, gpointer data)
-{
- SPObject *object = SP_OBJECT(data);
-
- object->read_content();
-}
-
-/**
- * Return string representation of space value.
- */
-static gchar const *sp_xml_get_space_string(unsigned int space)
-{
- switch (space) {
- case SP_XML_SPACE_DEFAULT:
- return "default";
- case SP_XML_SPACE_PRESERVE:
- return "preserve";
- default:
- return NULL;
- }
-}
-
-Inkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags) {
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::write" );
-#endif
-
- if (!repr && (flags & SP_OBJECT_WRITE_BUILD)) {
- repr = this->getRepr()->duplicate(doc);
- if (!( flags & SP_OBJECT_WRITE_EXT )) {
- repr->setAttribute("inkscape:collect", NULL);
- }
- } else if (repr) {
- repr->setAttribute("id", this->getId());
-
- if (this->xml_space.set) {
- char const *xml_space;
- xml_space = sp_xml_get_space_string(this->xml_space.value);
- repr->setAttribute("xml:space", xml_space);
- }
-
- if ( flags & SP_OBJECT_WRITE_EXT &&
- this->collectionPolicy() == SPObject::ALWAYS_COLLECT )
- {
- repr->setAttribute("inkscape:collect", "always");
- } else {
- repr->setAttribute("inkscape:collect", NULL);
- }
-
- if (style) {
- // Write if property set by style attribute in this object
- Glib::ustring s =
- style->write(SP_STYLE_FLAG_IFSET | SP_STYLE_FLAG_IFSRC, SP_STYLE_SRC_STYLE_PROP);
-
- // Check for valid attributes. This may be time consuming.
- // It is useful, though, for debugging Inkscape code.
- Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- if( prefs->getBool("/options/svgoutput/check_on_editing") ) {
-
- unsigned int flags = sp_attribute_clean_get_prefs();
- Glib::ustring s_cleaned = sp_attribute_clean_style( repr, s.c_str(), flags );
- }
-
- if( s.empty() ) {
- repr->setAttribute("style", NULL);
- } else {
- repr->setAttribute("style", s.c_str());
- }
-
- } else {
- /** \todo I'm not sure what to do in this case. Bug #1165868
- * suggests that it can arise, but the submitter doesn't know
- * how to do so reliably. The main two options are either
- * leave repr's style attribute unchanged, or explicitly clear it.
- * Must also consider what to do with property attributes for
- * the element; see below.
- */
- char const *style_str = repr->attribute("style");
- if (!style_str) {
- style_str = "NULL";
- }
- g_warning("Item's style is NULL; repr style attribute is %s", style_str);
- }
-
- /** \note We treat this->style as authoritative. Its effects have
- * been written to the style attribute above; any properties that are
- * unset we take to be deliberately unset (e.g. so that clones can
- * override the property).
- *
- * Note that the below has an undesirable consequence of changing the
- * appearance on renderers that lack CSS support (e.g. SVG tiny);
- * possibly we should write property attributes instead of a style
- * attribute.
- */
- // With the changes to preserves style source this is no longer needed
- // and the above comment no longer applies. I leave it here until these
- // change are well tested.
- // sp_style_unset_property_attrs (this);
- }
-
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::write", false );
-#endif
- return repr;
-}
-
-Inkscape::XML::Node * SPObject::updateRepr(unsigned int flags)
-{
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateRepr 1" );
-#endif
-
- if ( !cloned ) {
- Inkscape::XML::Node *repr = getRepr();
- if (repr) {
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateRepr 1", false );
-#endif
- return updateRepr(repr->document(), repr, flags);
- } else {
- g_critical("Attempt to update non-existent repr");
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateRepr 1", false );
-#endif
- return NULL;
- }
- } else {
- /* cloned objects have no repr */
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateRepr 1", false );
-#endif
- return NULL;
- }
-}
-
-Inkscape::XML::Node * SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags)
-{
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateRepr 2" );
-#endif
-
- g_assert(doc != NULL);
-
- if (cloned) {
- /* cloned objects have no repr */
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateRepr 2", false );
-#endif
- return NULL;
- }
-
- if (!(flags & SP_OBJECT_WRITE_BUILD) && !repr) {
- repr = getRepr();
- }
-
-#ifdef OBJECT_TRACE
- Inkscape::XML::Node *node = write(doc, repr, flags);
- objectTrace( "SPObject::updateRepr 2", false );
- return node;
-#else
- return this->write(doc, repr, flags);
-#endif
-
-}
-
-/* Modification */
-
-void SPObject::requestDisplayUpdate(unsigned int flags)
-{
- g_return_if_fail( this->document != NULL );
-
- if (update_in_progress) {
- g_print("WARNING: Requested update while update in progress, counter = %d\n", update_in_progress);
- }
-
- /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
- * SP_OBJECT_CHILD_MODIFIED_FLAG */
- g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
- g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
- g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
-
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::requestDisplayUpdate" );
-#endif
-
- bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
-
- this->uflags |= flags;
-
- /* If requestModified has already been called on this object or one of its children, then we
- * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
- */
- if (already_propagated) {
- if (parent) {
- parent->requestDisplayUpdate(SP_OBJECT_CHILD_MODIFIED_FLAG);
- } else {
- document->requestModified();
- }
- }
-
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::requestDisplayUpdate", false );
-#endif
-
-}
-
-void SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
-{
- g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
-
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateDisplay" );
-#endif
-
- update_in_progress ++;
-
-#ifdef SP_OBJECT_DEBUG_CASCADE
- g_print("Update %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), getId(), flags, this->uflags, this->mflags);
-#endif
-
- /* Get this flags */
- flags |= this->uflags;
- /* Copy flags to modified cascade for later processing */
- this->mflags |= this->uflags;
- /* We have to clear flags here to allow rescheduling update */
- this->uflags = 0;
-
- // Merge style if we have good reasons to think that parent style is changed */
- /** \todo
- * I am not sure whether we should check only propagated
- * flag. We are currently assuming that style parsing is
- * done immediately. I think this is correct (Lauris).
- */
- if ((flags & SP_OBJECT_STYLE_MODIFIED_FLAG) && (flags & SP_OBJECT_PARENT_MODIFIED_FLAG)) {
- if (this->style && this->parent) {
- style->cascade( this->parent->style );
- }
- }
-
- try
- {
- this->update(ctx, flags);
- }
- catch(...)
- {
- /** \todo
- * in case of catching an exception we need to inform the user somehow that the document is corrupted
- * maybe by implementing an document flag documentOk
- * or by a modal error dialog
- */
- g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
- }
-
- update_in_progress --;
-
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateDisplay", false );
-#endif
-}
-
-void SPObject::requestModified(unsigned int flags)
-{
- g_return_if_fail( this->document != NULL );
-
- /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
- * SP_OBJECT_CHILD_MODIFIED_FLAG */
- g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
- g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
- g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
-
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::requestModified" );
-#endif
-
- bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
-
- this->mflags |= flags;
-
- /* If requestModified has already been called on this object or one of its children, then we
- * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
- */
- if (already_propagated) {
- if (parent) {
- parent->requestModified(SP_OBJECT_CHILD_MODIFIED_FLAG);
- } else {
- document->requestModified();
- }
- }
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::requestModified", false );
-#endif
-}
-
-void SPObject::emitModified(unsigned int flags)
-{
- /* only the MODIFIED_CASCADE flag is legal here */
- g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
-
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::emitModified", true, flags );
-#endif
-
-#ifdef SP_OBJECT_DEBUG_CASCADE
- g_print("Modified %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), getId(), flags, this->uflags, this->mflags);
-#endif
-
- flags |= this->mflags;
- /* We have to clear mflags beforehand, as signal handlers may
- * make changes and therefore queue new modification notifications
- * themselves. */
- this->mflags = 0;
-
- sp_object_ref(this);
-
- this->modified(flags);
-
- _modified_signal.emit(this, flags);
- sp_object_unref(this);
-
-#ifdef OBJECT_TRACE
- objectTrace( "SPObject::emitModified", false );
-#endif
-}
-
-gchar const *SPObject::getTagName(SPException *ex) const
-{
- g_assert(repr != NULL);
- /* If exception is not clear, return */
- if (!SP_EXCEPTION_IS_OK(ex)) {
- return NULL;
- }
-
- /// \todo fixme: Exception if object is NULL? */
- //XML Tree being used here.
- return getRepr()->name();
-}
-
-gchar const *SPObject::getAttribute(gchar const *key, SPException *ex) const
-{
- g_assert(this->repr != NULL);
- /* If exception is not clear, return */
- if (!SP_EXCEPTION_IS_OK(ex)) {
- return NULL;
- }
-
- /// \todo fixme: Exception if object is NULL? */
- //XML Tree being used here.
- return (gchar const *) getRepr()->attribute(key);
-}
-
-void SPObject::setAttribute(gchar const *key, gchar const *value, SPException *ex)
-{
- g_assert(this->repr != NULL);
- /* If exception is not clear, return */
- g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
-
- /// \todo fixme: Exception if object is NULL? */
- //XML Tree being used here.
- getRepr()->setAttribute(key, value, false);
-}
-
-void SPObject::setAttribute(char const *key, Glib::ustring const &value, SPException *ex)
-{
- setAttribute(key, value.empty() ? NULL : value.c_str(), ex);
-}
-
-void SPObject::setAttribute(Glib::ustring const &key, Glib::ustring const &value, SPException *ex)
-{
- setAttribute( key.empty() ? NULL : key.c_str(),
- value.empty() ? NULL : value.c_str(), ex);
-}
-
-
-void SPObject::removeAttribute(gchar const *key, SPException *ex)
-{
- /* If exception is not clear, return */
- g_return_if_fail(SP_EXCEPTION_IS_OK(ex));
-
- /// \todo fixme: Exception if object is NULL? */
- //XML Tree being used here.
- getRepr()->setAttribute(key, NULL, false);
-}
-
-bool SPObject::storeAsDouble( gchar const *key, double *val ) const
-{
- g_assert(this->getRepr()!= NULL);
- return sp_repr_get_double(((Inkscape::XML::Node *)(this->getRepr())),key,val);
-}
-
-/** Helper */
-gchar *
-sp_object_get_unique_id(SPObject *object,
- gchar const *id)
-{
- static unsigned long count = 0;
-
- g_assert(SP_IS_OBJECT(object));
-
- count++;
-
- //XML Tree being used here.
- gchar const *name = object->getRepr()->name();
- g_assert(name != NULL);
-
- gchar const *local = strchr(name, ':');
- if (local) {
- name = local + 1;
- }
-
- if (id != NULL) {
- if (object->document->getObjectById(id) == NULL) {
- return g_strdup(id);
- }
- }
-
- size_t const name_len = strlen(name);
- size_t const buflen = name_len + (sizeof(count) * 10 / 4) + 1;
- gchar *const buf = (gchar *) g_malloc(buflen);
- memcpy(buf, name, name_len);
- gchar *const count_buf = buf + name_len;
- size_t const count_buflen = buflen - name_len;
- do {
- ++count;
- g_snprintf(count_buf, count_buflen, "%lu", count);
- } while ( object->document->getObjectById(buf) != NULL );
- return buf;
-}
-
-// Style
-
-gchar const * SPObject::getStyleProperty(gchar const *key, gchar const *def) const
-{
- //g_return_val_if_fail(object != NULL, NULL);
- //g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
- g_return_val_if_fail(key != NULL, NULL);
-
- //XML Tree being used here.
- gchar const *style = getRepr()->attribute("style");
- if (style) {
- size_t const len = strlen(key);
- char const *p;
- while ( (p = strstr(style, key))
- != NULL )
- {
- p += len;
- while ((*p <= ' ') && *p) {
- p++;
- }
- if (*p++ != ':') {
- break;
- }
- while ((*p <= ' ') && *p) {
- p++;
- }
- size_t const inherit_len = sizeof("inherit") - 1;
- if (*p
- && !(strneq(p, "inherit", inherit_len)
- && (p[inherit_len] == '\0'
- || p[inherit_len] == ';'
- || g_ascii_isspace(p[inherit_len])))) {
- return p;
- }
- }
- }
-
- //XML Tree being used here.
- gchar const *val = getRepr()->attribute(key);
- if (val && !streq(val, "inherit")) {
- return val;
- }
- if (this->parent) {
- return (this->parent)->getStyleProperty(key, def);
- }
-
- return def;
-}
-
-void SPObject::_requireSVGVersion(Inkscape::Version version) {
- for ( SPObject::ParentIterator iter=this ; iter ; ++iter ) {
- SPObject *object = iter;
- if (SP_IS_ROOT(object)) {
- SPRoot *root = SP_ROOT(object);
- if ( root->version.svg < version ) {
- root->version.svg = version;
- }
- }
- }
-}
-
-// Titles and descriptions
-
-/* Note:
- Titles and descriptions are stored in 'title' and 'desc' child elements
- (see section 5.4 of the SVG 1.0 and 1.1 specifications). The spec allows
- an element to have more than one 'title' child element, but strongly
- recommends against this and requires using the first one if a choice must
- be made. The same applies to 'desc' elements. Therefore, these functions
- ignore all but the first 'title' child element and first 'desc' child
- element, except when deleting a title or description.
-
- This will change in SVG 2, where multiple 'title' and 'desc' elements will
- be allowed with different localized strings.
-*/
-
-gchar * SPObject::title() const
-{
- return getTitleOrDesc("svg:title");
-}
-
-bool SPObject::setTitle(gchar const *title, bool verbatim)
-{
- return setTitleOrDesc(title, "svg:title", verbatim);
-}
-
-gchar * SPObject::desc() const
-{
- return getTitleOrDesc("svg:desc");
-}
-
-bool SPObject::setDesc(gchar const *desc, bool verbatim)
-{
- return setTitleOrDesc(desc, "svg:desc", verbatim);
-}
-
-char * SPObject::getTitleOrDesc(gchar const *svg_tagname) const
-{
- char *result = NULL;
- SPObject *elem = findFirstChild(svg_tagname);
- if ( elem ) {
- //This string copy could be avoided by changing
- //the return type of SPObject::getTitleOrDesc
- //to std::unique_ptr<Glib::ustring>
- result = g_strdup(elem->textualContent().c_str());
- }
- return result;
-}
-
-bool SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim)
-{
- if (!verbatim) {
- // If the new title/description is just whitespace,
- // treat it as though it were NULL.
- if (value) {
- bool just_whitespace = true;
- for (const gchar *cp = value; *cp; ++cp) {
- if (!std::strchr("\r\n \t", *cp)) {
- just_whitespace = false;
- break;
- }
- }
- if (just_whitespace) {
- value = NULL;
- }
- }
- // Don't stomp on mark-up if there is no real change.
- if (value) {
- gchar *current_value = getTitleOrDesc(svg_tagname);
- if (current_value) {
- bool different = std::strcmp(current_value, value);
- g_free(current_value);
- if (!different) {
- return false;
- }
- }
- }
- }
-
- SPObject *elem = findFirstChild(svg_tagname);
-
- if (value == NULL) {
- if (elem == NULL) {
- return false;
- }
- // delete the title/description(s)
- while (elem) {
- elem->deleteObject();
- elem = findFirstChild(svg_tagname);
- }
- return true;
- }
-
- Inkscape::XML::Document *xml_doc = document->getReprDoc();
-
- if (elem == NULL) {
- // create a new 'title' or 'desc' element, putting it at the
- // beginning (in accordance with the spec's recommendations)
- Inkscape::XML::Node *xml_elem = xml_doc->createElement(svg_tagname);
- repr->addChild(xml_elem, NULL);
- elem = document->getObjectByRepr(xml_elem);
- Inkscape::GC::release(xml_elem);
- }
- else {
- // remove the current content of the 'text' or 'desc' element
- auto tmp = elem->children | boost::adaptors::transformed([](SPObject& obj) { return &obj; });
- std::vector<SPObject*> vec(tmp.begin(), tmp.end());
- for (auto &child: vec) {
- child->deleteObject();
- }
- }
-
- // add the new content
- elem->appendChildRepr(xml_doc->createTextNode(value));
- return true;
-}
-
-SPObject* SPObject::findFirstChild(gchar const *tagname) const
-{
- for (auto& child: const_cast<SPObject*>(this)->children)
- {
- if (child.repr->type() == Inkscape::XML::ELEMENT_NODE &&
- !strcmp(child.repr->name(), tagname)) {
- return &child;
- }
- }
- return nullptr;
-}
-
-Glib::ustring SPObject::textualContent() const
-{
- Glib::ustring text;
-
- for (auto& child: children)
- {
- Inkscape::XML::NodeType child_type = child.repr->type();
-
- if (child_type == Inkscape::XML::ELEMENT_NODE) {
- text += child.textualContent();
- }
- else if (child_type == Inkscape::XML::TEXT_NODE) {
- text += child.repr->content();
- }
- }
- return text;
-}
-
-// For debugging: Print SP tree structure.
-void SPObject::recursivePrintTree( unsigned level )
-{
- if (level == 0) {
- std::cout << "SP Object Tree" << std::endl;
- }
- std::cout << "SP: ";
- for (unsigned i = 0; i < level; ++i) {
- std::cout << " ";
- }
- std::cout << (getId()?getId():"No object id") << std::endl;
- for (auto& child: children) {
- child.recursivePrintTree(level + 1);
- }
-}
-
-// Function to allow tracing of program flow through SPObject and derived classes.
-// To trace function, add at entrance ('in' = true) and exit of function ('in' = false).
-void SPObject::objectTrace( std::string text, bool in, unsigned flags ) {
- if( in ) {
- for (unsigned i = 0; i < indent_level; ++i) {
- std::cout << " ";
- }
- std::cout << text << ":"
- << " entrance: "
- << (id?id:"null")
- << " uflags: " << uflags
- << " mflags: " << mflags
- << " flags: " << flags << std::endl;
- ++indent_level;
- } else {
- --indent_level;
- for (unsigned i = 0; i < indent_level; ++i) {
- std::cout << " ";
- }
- std::cout << text << ":"
- << " exit: "
- << (id?id:"null")
- << " uflags: " << uflags
- << " mflags: " << mflags
- << " flags: " << flags << std::endl;
- }
-}
-
-/*
- Local Variables:
- mode:c++
- c-file-style:"stroustrup"
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
- indent-tabs-mode:nil
- fill-column:99
- End:
-*/
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :