sp-object.cpp revision 0de00189ead4347edfdce459510b99b572e43e26
/*
* 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
*
* Copyright (C) 1999-2008 authors
* Copyright (C) 2001-2002 Ximian, Inc.
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include <cstring>
#include <string>
#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-object-repr.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/longest-common-suffix.h"
#define noSP_OBJECT_DEBUG_CASCADE
#define noSP_OBJECT_DEBUG
#ifdef SP_OBJECT_DEBUG
g_print(f, ## a); \
g_print("\n"); \
}
#else
# define debug(f, a...) /* */
#endif
};
// 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.
*/
if (obj) {
}
}
/**
* Sets the id member of an object, freeing any prior content.
*/
}
if (id) {
}
}
}
};
{
if (!type) {
sizeof(SPObjectClass),
sizeof(SPObject),
16,
};
}
return type;
}
{
}
{
object->_total_hrefcount = 0;
//used XML Tree here.
// 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
}
{
if (spobject->_successor) {
}
}
}
namespace {
class RefCountEvent : public BaseRefCountEvent {
public:
{
}
};
class RefEvent : public RefCountEvent {
public:
{}
};
class UnrefEvent : public RefCountEvent {
public:
{}
};
}
return id;
}
return repr;
}
return repr;
}
{
return object;
}
{
return NULL;
}
{
return object;
}
{
return NULL;
}
g_critical("HRefs overcounted");
}
if ( iter->_total_hrefcount == 0 &&
{
}
}
if (topmost_collectable) {
}
}
while (object) {
if ( object == this ) {
return true;
}
}
return false;
}
namespace {
return &a == &b;
}
}
}
} else {
}
}
return result;
}
{
int result = 0;
// Need a common ancestor to be able to compare
if ( ancestor ) {
// we have an object and its ancestor (should not happen when sorting selection)
result = 1;
result = -1;
} else {
}
}
}
return result;
}
if ( !cloned ) {
} else {
g_critical("Attempt to append repr as child of cloned object");
return NULL;
}
}
{
}
{
}
if (add_ref) {
}
l = g_slist_prepend (l, child);
}
return l;
}
return _label;
}
if (_label) {
return _label;
} else {
if (!_default_label) {
if (getId()) {
} else {
}
}
return _default_label;
}
}
{
}
void SPObject::requestOrphanCollection() {
// do not remove style or script elements (Bug #276244)
if (SP_IS_STYLE_ELEM(this)) {
// leave it
} else if (SP_IS_SCRIPT(this)) {
// leave it
// 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?
*/
}
}
void SPObject::_sendDeleteSignalRecursive() {
}
}
{
sp_object_ref(this, NULL);
if (propagate) {
_delete_signal.emit(this);
}
if (propagate_descendants) {
this->_sendDeleteSignalRecursive();
}
}
if (_successor) {
}
sp_object_unref(this, NULL);
}
{
//g_return_if_fail(parent != NULL);
//g_return_if_fail(SP_IS_OBJECT(parent));
sp_object_ref(object, this);
if (prev) {
} else {
}
if (!next) {
this->_last_child = object;
}
}
{
//g_return_if_fail(object != NULL);
//g_return_if_fail(SP_IS_OBJECT(object));
g_return_if_fail(this != prev);
{
}
if (old_prev) {
} else {
}
if (!next) {
}
if (prev) {
} else {
}
if (!next) {
parent->_last_child = this;
}
}
{
//g_return_if_fail(parent != NULL);
//g_return_if_fail(SP_IS_OBJECT(parent));
{
}
if (prev) {
} else {
}
if (!next) {
this->_last_child = prev;
}
sp_object_unref(object, this);
}
{
} else {
break;
}
}
}
return result;
}
void SPObject::sp_object_child_added(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
{
if (!type) {
return;
}
}
{
}
}
{
if (ochild) {
}
}
void SPObject::sp_object_order_changed(SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node */*old_ref*/,
{
}
{
/* Nothing specific here */
if (!type) {
continue;
}
}
}
{
//g_assert(object != NULL);
//g_assert(SP_IS_OBJECT(object));
/* Bookkeeping */
if (!cloned) {
}
if ( !cloned ) {
/* If we are not cloned, and not seeking, force unique id */
{
}
/* Redefine ID, if required */
}
} else if (id) {
// bind if id, but no conflict -- otherwise, we can expect
// a subsequent setting of the id attribute
}
}
}
} else {
}
/* Invoke derived methods, if any */
}
/* Signalling (should be connected AFTER processing derived methods */
}
{
}
unsigned SPObject::getPosition(){
}
}
{
}
void SPObject::releaseReferences() {
sp_repr_remove_listener_by_data(this->repr, this);
this->_release_signal.emit(this);
}
/* all hrefs should be released by the "release" handlers */
if (!cloned) {
if (this->id) {
}
g_free(this->_default_label);
this->_default_label = NULL;
} else {
}
if (this->style) {
}
}
{
}
}
return prev;
}
void SPObject::sp_object_repr_child_added(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data)
{
}
}
void SPObject::sp_object_repr_child_removed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node */*ref*/, gpointer data)
{
}
}
void SPObject::sp_object_repr_order_changed(Inkscape::XML::Node */*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data)
{
}
}
{
switch (key) {
case SP_ATTR_ID:
//XML Tree being used here.
if (new_id) {
}
// give the conflicting object a new ID
} else {
}
}
}
if (new_id) {
}
}
break;
case SP_ATTR_INKSCAPE_LABEL:
if (value) {
} else {
}
break;
case SP_ATTR_INKSCAPE_COLLECT:
} else {
}
break;
case SP_ATTR_XML_SPACE:
}
break;
case SP_ATTR_STYLE:
break;
default:
break;
}
}
{
//g_assert(object != NULL);
//g_assert(SP_IS_OBJECT(object));
}
}
{
//g_assert(object != NULL);
//g_assert(SP_IS_OBJECT(object));
//XML Tree being used here.
if (keyid != SP_ATTR_INVALID) {
/* Retrieve the 'key' attribute from the object's XML representation */
}
}
void SPObject::sp_object_repr_attr_changed(Inkscape::XML::Node */*repr*/, gchar const *key, gchar const */*oldval*/, gchar const */*newval*/, bool is_interactive, gpointer data)
{
// 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::sp_object_repr_content_changed(Inkscape::XML::Node */*repr*/, gchar const */*oldcontent*/, gchar const */*newcontent*/, gpointer data)
{
}
}
/**
* Return string representation of space value.
*/
{
switch (space) {
case SP_XML_SPACE_DEFAULT:
return "default";
case SP_XML_SPACE_PRESERVE:
return "preserve";
default:
return NULL;
}
}
Inkscape::XML::Node * SPObject::sp_object_private_write(SPObject *object, Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
{
if (!( flags & SP_OBJECT_WRITE_EXT )) {
}
} else {
char const *xml_space;
}
if ( flags & SP_OBJECT_WRITE_EXT &&
{
} else {
}
if (obj_style) {
// Check for valid attributes. This may be time consuming.
// It is useful, though, for debugging Inkscape code.
unsigned int flags = sp_attribute_clean_get_prefs();
// g_warning("SPObject::sp_object_private_write: %s", object->getId() );
// g_warning(" old: :%s:", repr->attribute("style") );
// g_warning(" new: :%s:", s );
// g_warning(" cleaned: :%s:", s_cleaned );
g_free( s );
s = s_cleaned;
}
} else {
}
g_free(s);
} 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.
*/
if (!style_str) {
style_str = "NULL";
}
}
/** \note We treat object->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.
*/
}
return repr;
}
{
if ( !cloned ) {
if (repr) {
} else {
g_critical("Attempt to update non-existent repr");
return NULL;
}
} else {
/* cloned objects have no repr */
return NULL;
}
}
Inkscape::XML::Node * SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags)
{
if (cloned) {
/* cloned objects have no repr */
return NULL;
}
}
} else {
if (!repr) {
if (flags & SP_OBJECT_WRITE_BUILD) {
}
/// \todo FIXME: else probably error (Lauris) */
} else {
}
return repr;
}
}
/* Modification */
{
if (update_in_progress) {
}
/* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
* SP_OBJECT_CHILD_MODIFIED_FLAG */
bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
/* 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) {
} else {
}
}
}
{
#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 */
/* Copy flags to modified cascade for later processing */
/* 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).
*/
}
}
try
{
}
}
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);");
}
}
{
/* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
* SP_OBJECT_CHILD_MODIFIED_FLAG */
bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
/* 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) {
} else {
}
}
}
{
/* only the MODIFIED_CASCADE flag is legal here */
#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
/* We have to clear mflags beforehand, as signal handlers may
* make changes and therefore queue new modification notifications
* themselves. */
this->mflags = 0;
g_object_ref(G_OBJECT(this));
}
g_object_unref(G_OBJECT(this));
}
{
/* 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.
}
{
/* 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.
}
{
/* If exception is not clear, return */
/// \todo fixme: Exception if object is NULL? */
//XML Tree being used here.
}
{
/* If exception is not clear, return */
/// \todo fixme: Exception if object is NULL? */
//XML Tree being used here.
}
{
}
/* Helper */
{
static unsigned long count = 0;
count++;
//XML Tree being used here.
if (local) {
}
}
}
do {
++count;
return buf;
}
// Style
{
//g_return_val_if_fail(object != NULL, NULL);
//g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
//XML Tree being used here.
if (style) {
char const *p;
!= NULL )
{
p += len;
while ((*p <= ' ') && *p) {
p++;
}
if (*p++ != ':') {
break;
}
while ((*p <= ' ') && *p) {
p++;
}
if (*p
&& (p[inherit_len] == '\0'
|| p[inherit_len] == ';'
|| g_ascii_isspace(p[inherit_len])))) {
return p;
}
}
}
//XML Tree being used here.
return val;
}
if (this->parent) {
}
return def;
}
if (SP_IS_ROOT(object)) {
}
}
}
}
// 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.
*/
{
return getTitleOrDesc("svg:title");
}
{
}
{
return getTitleOrDesc("svg:desc");
}
{
}
{
if ( elem ) {
}
return result;
}
{
if (!verbatim) {
// If the new title/description is just whitespace,
// treat it as though it were NULL.
if (value) {
bool just_whitespace = true;
just_whitespace = false;
break;
}
}
if (just_whitespace) {
}
}
// Don't stomp on mark-up if there is no real change.
if (value) {
if (current_value) {
if (!different) {
return false;
}
}
}
}
return false;
}
// delete the title/description(s)
while (elem) {
elem->deleteObject();
}
return true;
}
// create a new 'title' or 'desc' element, putting it at the
// beginning (in accordance with the spec's recommendations)
}
else {
// remove the current content of the 'text' or 'desc' element
}
// add the new content
return true;
}
{
{
return child;
}
}
return NULL;
}
{
{
}
}
}
return text;
}
/*
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 :