diff options
Diffstat (limited to 'src/sp-object.cpp')
| -rw-r--r-- | src/sp-object.cpp | 1702 |
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 : |
