sp-object.cpp revision 87223513f0edf4dcd1727917ded980e3ac4fd4ae
843e19887f64dde75055cf8842fc4db2171eff45johnlev * SPObject implementation.
843e19887f64dde75055cf8842fc4db2171eff45johnlev * Lauris Kaplinski <lauris@kaplinski.com>
843e19887f64dde75055cf8842fc4db2171eff45johnlev * bulia byak <buliabyak@users.sf.net>
843e19887f64dde75055cf8842fc4db2171eff45johnlev * Stephen Silver <sasilver@users.sourceforge.net>
843e19887f64dde75055cf8842fc4db2171eff45johnlev * Jon A. Cruz <jon@joncruz.org>
843e19887f64dde75055cf8842fc4db2171eff45johnlev * Abhishek Sharma
843e19887f64dde75055cf8842fc4db2171eff45johnlev * Copyright (C) 1999-2008 authors
843e19887f64dde75055cf8842fc4db2171eff45johnlev * Copyright (C) 2001-2002 Ximian, Inc.
843e19887f64dde75055cf8842fc4db2171eff45johnlev * Released under GNU GPL, read the file 'COPYING' for more information
843e19887f64dde75055cf8842fc4db2171eff45johnlev return new SPObject();
843e19887f64dde75055cf8842fc4db2171eff45johnlev bool gridRegistered = SPFactory::instance().registerObject("inkscape:grid", createObject);
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabguint update_in_progress = 0; // guard against update-during-update
843e19887f64dde75055cf8842fc4db2171eff45johnlevInkscape::XML::NodeEventVector object_event_vector = {
843e19887f64dde75055cf8842fc4db2171eff45johnlev// A friend class used to set internal members on SPObject so as to not expose settors in SPObject's public API
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * Null's the id member of an SPObject without attempting to free prior contents.
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * Sets the id member of an object, freeing any prior content.
843e19887f64dde75055cf8842fc4db2171eff45johnlev static void setId( SPObject* obj, gchar const* id ) {
843e19887f64dde75055cf8842fc4db2171eff45johnlevstatic gchar *sp_object_get_unique_id(SPObject *object,
843e19887f64dde75055cf8842fc4db2171eff45johnlev : cloned(0), uflags(0), mflags(0), hrefcount(0), _total_hrefcount(0),
843e19887f64dde75055cf8842fc4db2171eff45johnlev document(NULL), parent(NULL), children(NULL), _last_child(NULL),
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab _successor(NULL), _collection_policy(SPObject::COLLECT_WITH_PARENT),
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab debug("id=%x, typename=%s",this, g_type_name_from_instance((GTypeInstance*)object));
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab //used XML Tree here.
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab // FIXME: now we create style for all objects, but per SVG, only the following can have style attribute:
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab // vg, g, defs, desc, title, symbol, use, image, switch, path, rect, circle, ellipse, line, polyline,
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab // polygon, text, tspan, tref, textPath, altGlyph, glyphRef, marker, linearGradient, radialGradient,
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab // stop, pattern, clipPath, mask, filter, feImage, a, font, glyph, missing-glyph, foreignObject
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab if (this->_successor) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev// CPPIFY: make pure virtual
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::update(SPCtx* ctx, unsigned int flags) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabnamespace {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabtypedef Debug::SimpleEvent<Debug::Event::REFCOUNT> BaseRefCountEvent;
843e19887f64dde75055cf8842fc4db2171eff45johnlev RefCountEvent(SPObject *object, int bias, Util::ptr_shared<char> name)
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab _addProperty("class", Debug::demangle(g_type_name(G_TYPE_FROM_INSTANCE(object))));
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab _addProperty("new-refcount", Util::format("%d", G_OBJECT(object)->ref_count + bias));
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab : RefCountEvent(object, 1, Util::share_static_string("sp-object-ref"))
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee : RefCountEvent(object, -1, Util::share_static_string("sp-object-unref"))
843e19887f64dde75055cf8842fc4db2171eff45johnlevInkscape::XML::Node const* SPObject::getRepr() const{
843e19887f64dde75055cf8842fc4db2171eff45johnlevSPObject *sp_object_ref(SPObject *object, SPObject *owner)
843e19887f64dde75055cf8842fc4db2171eff45johnlev g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
843e19887f64dde75055cf8842fc4db2171eff45johnlev Inkscape::Debug::EventTracker<RefEvent> tracker(object);
843e19887f64dde75055cf8842fc4db2171eff45johnlev //g_object_ref(G_OBJECT(object));
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabSPObject *sp_object_unref(SPObject *object, SPObject *owner)
843e19887f64dde75055cf8842fc4db2171eff45johnlev g_return_val_if_fail(!owner || SP_IS_OBJECT(owner), NULL);
843e19887f64dde75055cf8842fc4db2171eff45johnlev Inkscape::Debug::EventTracker<UnrefEvent> tracker(object);
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab //g_object_unref(G_OBJECT(object));
843e19887f64dde75055cf8842fc4db2171eff45johnlevSPObject *sp_object_href(SPObject *object, gpointer /*owner*/)
843e19887f64dde75055cf8842fc4db2171eff45johnlevSPObject *sp_object_hunref(SPObject *object, gpointer /*owner*/)
843e19887f64dde75055cf8842fc4db2171eff45johnlev for ( SPObject *iter = this ; iter ; iter = iter->parent ) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabbool SPObject::isAncestorOf(SPObject const *object) const {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab if ( object == this ) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab return true;
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab return false;
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabnamespace {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabbool same_objects(SPObject const &a, SPObject const &b) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab return &a == &b;
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabSPObject const *SPObject::nearestCommonAncestor(SPObject const *object) const {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab return longest_common_suffix<SPObject::ConstParentIterator>(this, object, NULL, &same_objects);
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybeestatic SPObject const *AncestorSon(SPObject const *obj, SPObject const *ancestor) {
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybeeint sp_object_compare_position(SPObject const *first, SPObject const *second)
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab SPObject const *ancestor = first->nearestCommonAncestor(second);
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab // Need a common ancestor to be able to compare
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab // we have an object and its ancestor (should not happen when sorting selection)
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab SPObject const *to_first = AncestorSon(first, ancestor);
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab SPObject const *to_second = AncestorSon(second, ancestor);
843e19887f64dde75055cf8842fc4db2171eff45johnlev result = sp_repr_compare_position(to_first->getRepr(), to_second->getRepr());
843e19887f64dde75055cf8842fc4db2171eff45johnlevSPObject *SPObject::appendChildRepr(Inkscape::XML::Node *repr) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev g_critical("Attempt to append repr as child of cloned object");
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::setCSS(SPCSSAttr *css, gchar const *attr)
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::changeCSS(SPCSSAttr *css, gchar const *attr)
843e19887f64dde75055cf8842fc4db2171eff45johnlev for ( SPObject *child = firstChild() ; child; child = child->getNext() ) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab _default_label = g_strdup_printf("<%s>", getRepr()->name());
843e19887f64dde75055cf8842fc4db2171eff45johnlev getRepr()->setAttribute("inkscape:label", label, false);
843e19887f64dde75055cf8842fc4db2171eff45johnlev Inkscape::Preferences *prefs = Inkscape::Preferences::get();
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab // do not remove style or script elements (Bug #276244)
843e19887f64dde75055cf8842fc4db2171eff45johnlev // leave it
843e19887f64dde75055cf8842fc4db2171eff45johnlev } else if (SP_IS_SCRIPT(this)) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev // leave it
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab } else if ((! prefs->getBool("/options/cleanupswatches/value", false)) && SP_IS_PAINT_SERVER(this) && static_cast<SPPaintServer*>(this)->isSwatch() ) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab // leave it
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab } else if (IS_COLORPROFILE(this)) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab // leave it
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * This is a temporary hack added to make fill&stroke rebuild its
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * gradient list when the defs are vacuumed. gradient-vector.cpp
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * listens to the modified signal on defs, and now we give it that
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * signal. Mental says that this should be made automatic by
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * merging SPObjectGroup with SPObject; SPObjectGroup would issue
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * this signal automatically. Or maybe just derive SPDefs from
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * SPObjectGroup?
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab for (SPObject *child = firstChild(); child; child = child->getNext()) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabvoid SPObject::deleteObject(bool propagate, bool propagate_descendants)
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab _successor->deleteObject(propagate, propagate_descendants);
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab //g_return_if_fail(parent != NULL);
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab //g_return_if_fail(SP_IS_OBJECT(parent));
5d2eda970e48f8985448151c73e699614ce9f357John Levon this->_updateTotalHRefCount(object->_total_hrefcount);
5d2eda970e48f8985448151c73e699614ce9f357John Levon //g_return_if_fail(object != NULL);
5d2eda970e48f8985448151c73e699614ce9f357John Levon //g_return_if_fail(SP_IS_OBJECT(object));
5d2eda970e48f8985448151c73e699614ce9f357John Levon g_return_if_fail(!prev || prev->parent == this->parent);
5d2eda970e48f8985448151c73e699614ce9f357John Levon for ( SPObject *child = parent->children ; child && child != this ;
843e19887f64dde75055cf8842fc4db2171eff45johnlev //g_return_if_fail(parent != NULL);
843e19887f64dde75055cf8842fc4db2171eff45johnlev //g_return_if_fail(SP_IS_OBJECT(parent));
843e19887f64dde75055cf8842fc4db2171eff45johnlev for ( SPObject *child = this->children ; child && child != object ;
5d2eda970e48f8985448151c73e699614ce9f357John Levon this->_updateTotalHRefCount(-object->_total_hrefcount);
843e19887f64dde75055cf8842fc4db2171eff45johnlevSPObject *SPObject::get_child_by_repr(Inkscape::XML::Node *repr)
843e19887f64dde75055cf8842fc4db2171eff45johnlev if ( _last_child && (_last_child->getRepr() == repr) ) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev result = _last_child; // optimization for common scenario
843e19887f64dde75055cf8842fc4db2171eff45johnlev for ( SPObject *child = children ; child ; child = child->next ) {
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev const std::string typeString = NodeTraits::get_type_string(*child);
843e19887f64dde75055cf8842fc4db2171eff45johnlev SPObject* ochild = SPFactory::instance().createObject(typeString);
843e19887f64dde75055cf8842fc4db2171eff45johnlev SPObject *prev = ref ? object->get_child_by_repr(ref) : NULL;
843e19887f64dde75055cf8842fc4db2171eff45johnlev ochild->invoke_build(object->document, child, object->cloned);
843e19887f64dde75055cf8842fc4db2171eff45johnlev } catch (const FactoryExceptions::TypeNotRegistered& e) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev // special cases
843e19887f64dde75055cf8842fc4db2171eff45johnlev if (node == "inkscape:clipboard") return; // SP node not necessary
843e19887f64dde75055cf8842fc4db2171eff45johnlev g_warning("TypeNotRegistered exception: %s", e.what());
843e19887f64dde75055cf8842fc4db2171eff45johnlev debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::remove_child(Inkscape::XML::Node* child) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
843e19887f64dde75055cf8842fc4db2171eff45johnlev SPObject *ochild = object->get_child_by_repr(child);
843e19887f64dde75055cf8842fc4db2171eff45johnlev g_return_if_fail (ochild != NULL || !strcmp("comment", child->name())); // comments have no objects
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::order_changed(Inkscape::XML::Node *child, Inkscape::XML::Node * old_ref, Inkscape::XML::Node *new_ref) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev SPObject *ochild = object->get_child_by_repr(child);
843e19887f64dde75055cf8842fc4db2171eff45johnlev SPObject *prev = new_ref ? object->get_child_by_repr(new_ref) : NULL;
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::build(SPDocument *document, Inkscape::XML::Node *repr) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev /* Nothing specific here */
843e19887f64dde75055cf8842fc4db2171eff45johnlev debug("id=%x, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
843e19887f64dde75055cf8842fc4db2171eff45johnlev for (Inkscape::XML::Node *rchild = repr->firstChild() ; rchild != NULL; rchild = rchild->next()) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev const std::string typeString = NodeTraits::get_type_string(*rchild);
843e19887f64dde75055cf8842fc4db2171eff45johnlev // special cases
843e19887f64dde75055cf8842fc4db2171eff45johnlev if (typeString.empty()) continue; // comments, usually
843e19887f64dde75055cf8842fc4db2171eff45johnlev if (typeString == "rdf:RDF") continue; // no SP node yet
843e19887f64dde75055cf8842fc4db2171eff45johnlev if (typeString == "inkscape:clipboard") continue; // SP node not necessary
843e19887f64dde75055cf8842fc4db2171eff45johnlev SPObject* child = SPFactory::instance().createObject(typeString);
843e19887f64dde75055cf8842fc4db2171eff45johnlev child->invoke_build(document, rchild, object->cloned);
843e19887f64dde75055cf8842fc4db2171eff45johnlev } catch (const FactoryExceptions::TypeNotRegistered& e) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev g_warning("TypeNotRegistered exception: %s", e.what());
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab debug("id=%x, typename=%s", this, g_type_name_from_instance((GTypeInstance*)this));
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab //g_assert(object != NULL);
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab //g_assert(SP_IS_OBJECT(object));
843e19887f64dde75055cf8842fc4db2171eff45johnlev /* Bookkeeping */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab /* If we are not cloned, and not seeking, force unique id */
843e19887f64dde75055cf8842fc4db2171eff45johnlev /* Redefine ID, if required */
843e19887f64dde75055cf8842fc4db2171eff45johnlev if ((id == NULL) || (strcmp(id, this->getId()) != 0)) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev } else if (id) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev // bind if id, but no conflict -- otherwise, we can expect
843e19887f64dde75055cf8842fc4db2171eff45johnlev // a subsequent setting of the id attribute
843e19887f64dde75055cf8842fc4db2171eff45johnlev /* Invoke derived methods, if any */
843e19887f64dde75055cf8842fc4db2171eff45johnlev /* Signalling (should be connected AFTER processing derived methods */
843e19887f64dde75055cf8842fc4db2171eff45johnlev sp_repr_add_listener(repr, &object_event_vector, this);
843e19887f64dde75055cf8842fc4db2171eff45johnlevint SPObject::getIntAttribute(char const *key, int def)
5d2eda970e48f8985448151c73e699614ce9f357John Levonvoid SPObject::appendChild(Inkscape::XML::Node *child) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabvoid SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node * prev)
5d2eda970e48f8985448151c73e699614ce9f357John Levon sp_repr_remove_listener_by_data(this->repr, this);
843e19887f64dde75055cf8842fc4db2171eff45johnlev /* all hrefs should be released by the "release" handlers */
843e19887f64dde75055cf8842fc4db2171eff45johnlev if (this->id) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev if (this->style) {
5d2eda970e48f8985448151c73e699614ce9f357John Levon for ( SPObject *obj = parent->firstChild(); obj && !prev; obj = obj->getNext() ) {
5d2eda970e48f8985448151c73e699614ce9f357John Levonvoid SPObject::repr_child_added(Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data)
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::repr_child_removed(Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node * /*ref*/, gpointer data)
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::repr_order_changed(Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data)
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::set(unsigned int key, gchar const* value) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev //XML Tree being used here.
843e19887f64dde75055cf8842fc4db2171eff45johnlev if ( !object->cloned && object->getRepr()->type() == Inkscape::XML::ELEMENT_NODE ) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev conflict = document->getObjectById((char const *)new_id);
843e19887f64dde75055cf8842fc4db2171eff45johnlev // give the conflicting object a new ID
843e19887f64dde75055cf8842fc4db2171eff45johnlev gchar *new_conflict_id = sp_object_get_unique_id(conflict, NULL);
843e19887f64dde75055cf8842fc4db2171eff45johnlev conflict->getRepr()->setAttribute("id", new_conflict_id);
843e19887f64dde75055cf8842fc4db2171eff45johnlev object->setCollectionPolicy(SPObject::ALWAYS_COLLECT);
843e19887f64dde75055cf8842fc4db2171eff45johnlev object->setCollectionPolicy(SPObject::COLLECT_WITH_PARENT);
843e19887f64dde75055cf8842fc4db2171eff45johnlev object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
843e19887f64dde75055cf8842fc4db2171eff45johnlev object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::setKeyValue(unsigned int key, gchar const *value)
843e19887f64dde75055cf8842fc4db2171eff45johnlev //g_assert(object != NULL);
843e19887f64dde75055cf8842fc4db2171eff45johnlev //g_assert(SP_IS_OBJECT(object));
843e19887f64dde75055cf8842fc4db2171eff45johnlev //g_assert(object != NULL);
843e19887f64dde75055cf8842fc4db2171eff45johnlev //g_assert(SP_IS_OBJECT(object));
843e19887f64dde75055cf8842fc4db2171eff45johnlev //XML Tree being used here.
843e19887f64dde75055cf8842fc4db2171eff45johnlev /* Retrieve the 'key' attribute from the object's XML representation */
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::repr_attr_changed(Inkscape::XML::Node * /*repr*/, gchar const *key, gchar const * /*oldval*/, gchar const * /*newval*/, bool is_interactive, gpointer data)
843e19887f64dde75055cf8842fc4db2171eff45johnlev // manual changes to extension attributes require the normal
843e19887f64dde75055cf8842fc4db2171eff45johnlev // attributes, which depend on them, to be updated immediately
843e19887f64dde75055cf8842fc4db2171eff45johnlevvoid SPObject::repr_content_changed(Inkscape::XML::Node * /*repr*/, gchar const * /*oldcontent*/, gchar const * /*newcontent*/, gpointer data)
843e19887f64dde75055cf8842fc4db2171eff45johnlev * Return string representation of space value.
843e19887f64dde75055cf8842fc4db2171eff45johnlevstatic gchar const *sp_xml_get_space_string(unsigned int space)
843e19887f64dde75055cf8842fc4db2171eff45johnlev return "default";
843e19887f64dde75055cf8842fc4db2171eff45johnlev return "preserve";
843e19887f64dde75055cf8842fc4db2171eff45johnlevInkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev char const *xml_space;
843e19887f64dde75055cf8842fc4db2171eff45johnlev xml_space = sp_xml_get_space_string(object->xml_space.value);
843e19887f64dde75055cf8842fc4db2171eff45johnlev object->collectionPolicy() == SPObject::ALWAYS_COLLECT )
843e19887f64dde75055cf8842fc4db2171eff45johnlev gchar *s = sp_style_write_string(obj_style, SP_STYLE_FLAG_IFSET);
843e19887f64dde75055cf8842fc4db2171eff45johnlev // Check for valid attributes. This may be time consuming.
843e19887f64dde75055cf8842fc4db2171eff45johnlev // It is useful, though, for debugging Inkscape code.
843e19887f64dde75055cf8842fc4db2171eff45johnlev Inkscape::Preferences *prefs = Inkscape::Preferences::get();
843e19887f64dde75055cf8842fc4db2171eff45johnlev if( prefs->getBool("/options/svgoutput/check_on_editing") ) {
843e19887f64dde75055cf8842fc4db2171eff45johnlev unsigned int flags = sp_attribute_clean_get_prefs();
843e19887f64dde75055cf8842fc4db2171eff45johnlev Glib::ustring s_cleaned = sp_attribute_clean_style( repr, s, flags );
843e19887f64dde75055cf8842fc4db2171eff45johnlev s = (s_cleaned.empty() ? NULL : g_strdup (s_cleaned.c_str()));
843e19887f64dde75055cf8842fc4db2171eff45johnlev /** \todo I'm not sure what to do in this case. Bug #1165868
843e19887f64dde75055cf8842fc4db2171eff45johnlev * suggests that it can arise, but the submitter doesn't know
843e19887f64dde75055cf8842fc4db2171eff45johnlev * how to do so reliably. The main two options are either
843e19887f64dde75055cf8842fc4db2171eff45johnlev * leave repr's style attribute unchanged, or explicitly clear it.
843e19887f64dde75055cf8842fc4db2171eff45johnlev * Must also consider what to do with property attributes for
843e19887f64dde75055cf8842fc4db2171eff45johnlev * the element; see below.
843e19887f64dde75055cf8842fc4db2171eff45johnlev g_warning("Item's style is NULL; repr style attribute is %s", style_str);
843e19887f64dde75055cf8842fc4db2171eff45johnlev /** \note We treat object->style as authoritative. Its effects have
843e19887f64dde75055cf8842fc4db2171eff45johnlev * been written to the style attribute above; any properties that are
843e19887f64dde75055cf8842fc4db2171eff45johnlev * unset we take to be deliberately unset (e.g. so that clones can
843e19887f64dde75055cf8842fc4db2171eff45johnlev * override the property).
843e19887f64dde75055cf8842fc4db2171eff45johnlev * Note that the below has an undesirable consequence of changing the
843e19887f64dde75055cf8842fc4db2171eff45johnlev * appearance on renderers that lack CSS support (e.g. SVG tiny);
843e19887f64dde75055cf8842fc4db2171eff45johnlev * possibly we should write property attributes instead of a style
843e19887f64dde75055cf8842fc4db2171eff45johnlev * attribute.
843e19887f64dde75055cf8842fc4db2171eff45johnlevInkscape::XML::Node * SPObject::updateRepr(unsigned int flags)
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee /* cloned objects have no repr */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabInkscape::XML::Node * SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags)
843e19887f64dde75055cf8842fc4db2171eff45johnlev /* cloned objects have no repr */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab/* Modification */
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee g_print("WARNING: Requested update while update in progress, counter = %d\n", update_in_progress);
843e19887f64dde75055cf8842fc4db2171eff45johnlev /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee * SP_OBJECT_CHILD_MODIFIED_FLAG */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
843e19887f64dde75055cf8842fc4db2171eff45johnlev g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee /* If requestModified has already been called on this object or one of its children, then we
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab parent->requestDisplayUpdate(SP_OBJECT_CHILD_MODIFIED_FLAG);
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabvoid SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab g_print("Update %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), getId(), flags, this->uflags, this->mflags);
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab /* Get this flags */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab /* Copy flags to modified cascade for later processing */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab /* We have to clear flags here to allow rescheduling update */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab // Merge style if we have good reasons to think that parent style is changed */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * I am not sure whether we should check only propagated
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee * flag. We are currently assuming that style parsing is
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee * done immediately. I think this is correct (Lauris).
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab if ((flags & SP_OBJECT_STYLE_MODIFIED_FLAG) && (flags & SP_OBJECT_PARENT_MODIFIED_FLAG)) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab sp_style_merge_from_parent(this->style, this->parent->style);
843e19887f64dde75055cf8842fc4db2171eff45johnlev * in case of catching an exception we need to inform the user somehow that the document is corrupted
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * maybe by implementing an document flag documentOk
843e19887f64dde75055cf8842fc4db2171eff45johnlev * or by a modal error dialog
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab /* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * SP_OBJECT_CHILD_MODIFIED_FLAG */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab g_return_if_fail(!(flags & SP_OBJECT_PARENT_MODIFIED_FLAG));
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab g_return_if_fail((flags & SP_OBJECT_MODIFIED_FLAG) || (flags & SP_OBJECT_CHILD_MODIFIED_FLAG));
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab /* If requestModified has already been called on this object or one of its children, then we
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab /* only the MODIFIED_CASCADE flag is legal here */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee g_print("Modified %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), getId(), flags, this->uflags, this->mflags);
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab /* We have to clear mflags beforehand, as signal handlers may
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * make changes and therefore queue new modification notifications
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab * themselves. */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab /* If exception is not clear, return */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab /// \todo fixme: Exception if object is NULL? */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab //XML Tree being used here.
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabgchar const *SPObject::getAttribute(gchar const *key, SPException *ex) const
843e19887f64dde75055cf8842fc4db2171eff45johnlev /* If exception is not clear, return */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab /// \todo fixme: Exception if object is NULL? */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab //XML Tree being used here.
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabvoid SPObject::setAttribute(gchar const *key, gchar const *value, SPException *ex)
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab /* If exception is not clear, return */
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee /// \todo fixme: Exception if object is NULL? */
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab //XML Tree being used here.
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybeevoid SPObject::removeAttribute(gchar const *key, SPException *ex)
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab /* If exception is not clear, return */
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee /// \todo fixme: Exception if object is NULL? */
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee //XML Tree being used here.
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybeebool SPObject::storeAsDouble( gchar const *key, double *val ) const
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee return sp_repr_get_double(((Inkscape::XML::Node *)(this->getRepr())),key,val);
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab static unsigned long count = 0;
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab //XML Tree being used here.
5d2eda970e48f8985448151c73e699614ce9f357John Levon size_t const buflen = name_len + (sizeof(count) * 10 / 4) + 1;
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab } while ( object->document->getObjectById(buf) != NULL );
843e19887f64dde75055cf8842fc4db2171eff45johnlevgchar const * SPObject::getStyleProperty(gchar const *key, gchar const *def) const
843e19887f64dde75055cf8842fc4db2171eff45johnlev //g_return_val_if_fail(object != NULL, NULL);
843e19887f64dde75055cf8842fc4db2171eff45johnlev //g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
843e19887f64dde75055cf8842fc4db2171eff45johnlev //XML Tree being used here.
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab char const *p;
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab while ((*p <= ' ') && *p) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab if (*p++ != ':') {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab while ((*p <= ' ') && *p) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab //XML Tree being used here.
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab if (this->parent) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rabvoid SPObject::_requireSVGVersion(Inkscape::Version version) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab for ( SPObject::ParentIterator iter=this ; iter ; ++iter ) {
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab// Titles and descriptions
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab Titles and descriptions are stored in 'title' and 'desc' child elements
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab (see section 5.4 of the SVG 1.0 and 1.1 specifications). The spec allows
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab an element to have more than one 'title' child element, but strongly
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab recommends against this and requires using the first one if a choice must
843e19887f64dde75055cf8842fc4db2171eff45johnlev be made. The same applies to 'desc' elements. Therefore, these functions
843e19887f64dde75055cf8842fc4db2171eff45johnlev ignore all but the first 'title' child element and first 'desc' child
843e19887f64dde75055cf8842fc4db2171eff45johnlev element, except when deleting a title or description.
5d2eda970e48f8985448151c73e699614ce9f357John Levon This will change in SVG 2, where multiple 'title' and 'desc' elements will
5d2eda970e48f8985448151c73e699614ce9f357John Levon be allowed with different localized strings.
843e19887f64dde75055cf8842fc4db2171eff45johnlevbool SPObject::setTitle(gchar const *title, bool verbatim)
843e19887f64dde75055cf8842fc4db2171eff45johnlev return setTitleOrDesc(title, "svg:title", verbatim);
843e19887f64dde75055cf8842fc4db2171eff45johnlevbool SPObject::setDesc(gchar const *desc, bool verbatim)
843e19887f64dde75055cf8842fc4db2171eff45johnlevgchar * SPObject::getTitleOrDesc(gchar const *svg_tagname) const
843e19887f64dde75055cf8842fc4db2171eff45johnlev result = g_string_free(elem->textualContent(), FALSE);
843e19887f64dde75055cf8842fc4db2171eff45johnlevbool SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool verbatim)
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab // If the new title/description is just whitespace,
843e19887f64dde75055cf8842fc4db2171eff45johnlev // treat it as though it were NULL.
843e19887f64dde75055cf8842fc4db2171eff45johnlev // Don't stomp on mark-up if there is no real change.
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab return false;
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee return false;
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab // delete the title/description(s)
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab return true;
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab Inkscape::XML::Document *xml_doc = document->getReprDoc();
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee // create a new 'title' or 'desc' element, putting it at the
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee // beginning (in accordance with the spec's recommendations)
349b53dd4e695e3d833b5380540385145b2d3ae8Stuart Maybee Inkscape::XML::Node *xml_elem = xml_doc->createElement(svg_tagname);
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab // remove the current content of the 'text' or 'desc' element
843e19887f64dde75055cf8842fc4db2171eff45johnlev while (NULL != (child = elem->firstChild())) child->deleteObject();
843e19887f64dde75055cf8842fc4db2171eff45johnlev // add the new content
843e19887f64dde75055cf8842fc4db2171eff45johnlev elem->appendChildRepr(xml_doc->createTextNode(value));
843e19887f64dde75055cf8842fc4db2171eff45johnlev return true;
843e19887f64dde75055cf8842fc4db2171eff45johnlevSPObject * SPObject::findFirstChild(gchar const *tagname) const
843e19887f64dde75055cf8842fc4db2171eff45johnlev for (SPObject *child = children; child; child = child->next)
843e19887f64dde75055cf8842fc4db2171eff45johnlev if (child->repr->type() == Inkscape::XML::ELEMENT_NODE &&
843e19887f64dde75055cf8842fc4db2171eff45johnlev for (const SPObject *child = firstChild(); child; child = child->next)
843e19887f64dde75055cf8842fc4db2171eff45johnlev Inkscape::XML::NodeType child_type = child->repr->type();
843e19887f64dde75055cf8842fc4db2171eff45johnlev Local Variables:
a576ab5b6e08c47732b3dedca9eaa8a8cbb85720rab c-file-style:"stroustrup"
843e19887f64dde75055cf8842fc4db2171eff45johnlev c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
843e19887f64dde75055cf8842fc4db2171eff45johnlev indent-tabs-mode:nil
843e19887f64dde75055cf8842fc4db2171eff45johnlev fill-column:99
843e19887f64dde75055cf8842fc4db2171eff45johnlev// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :