From 35e0418c921e7057987f57ba30ba95ec5e03c9e5 Mon Sep 17 00:00:00 2001 From: knutux Date: Thu, 20 Apr 2006 16:06:59 +0000 Subject: [PATCH] SVG 1.1 Conditional Processing Module rendering support ( element, requiredReatures/requiredExtensions/systemLanguage attributes). Two more W3C SVG Test Suite testes pass after this change. --- src/Makefile_insert | 2 + src/attributes.cpp | 4 + src/attributes.h | 6 +- src/conditions.cpp | 462 ++++++++++++++++++++++++++++++++++++++ src/conditions.h | 8 + src/display/sp-canvas.cpp | 2 +- src/document.cpp | 29 +++ src/document.h | 3 + src/sp-item-group.cpp | 421 ++++++++++++++++++---------------- src/sp-item-group.h | 33 +++ src/sp-item.cpp | 78 +++++-- src/sp-item.h | 16 +- src/sp-object-repr.cpp | 3 +- src/sp-switch.cpp | 182 +++++++++++++++ src/sp-switch.h | 61 +++++ 15 files changed, 1101 insertions(+), 209 deletions(-) create mode 100644 src/conditions.cpp create mode 100644 src/conditions.h create mode 100644 src/sp-switch.cpp create mode 100644 src/sp-switch.h diff --git a/src/Makefile_insert b/src/Makefile_insert index a9c565af0..12b766bb3 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -45,6 +45,7 @@ libinkpre_a_SOURCES = \ color-rgba.h \ color-profile.cpp color-profile.h \ color-profile-fns.h \ + conditions.cpp conditions.h\ conn-avoid-ref.cpp conn-avoid-ref.h \ connector-context.cpp connector-context.h \ context-fns.cpp context-fns.h \ @@ -186,6 +187,7 @@ libinkpre_a_SOURCES = \ sp-stop.h \ sp-string.cpp sp-string.h \ sp-symbol.cpp sp-symbol.h \ + sp-switch.cpp sp-switch.h\ sp-text.cpp sp-text.h \ sp-textpath.h \ sp-tspan.cpp sp-tspan.h \ diff --git a/src/attributes.cpp b/src/attributes.cpp index 39163c010..541acfc0c 100644 --- a/src/attributes.cpp +++ b/src/attributes.cpp @@ -308,6 +308,10 @@ static SPStyleProp const props[] = { {SP_PROP_STROKE_OPACITY, "stroke-opacity"}, {SP_PROP_STROKE_WIDTH, "stroke-width"}, {SP_PROP_TEXT_RENDERING, "text-rendering"}, + /* Conditional */ + {SP_PROP_SYSTEM_LANGUAGE, "systemLanguage"}, + {SP_PROP_REQUIRED_FEATURES, "requiredFeatures"}, + {SP_PROP_REQUIRED_EXTENSIONS, "requiredExtensions"}, }; #define n_attrs (sizeof(props) / sizeof(props[0])) diff --git a/src/attributes.h b/src/attributes.h index 897656845..bcbed50d0 100644 --- a/src/attributes.h +++ b/src/attributes.h @@ -307,7 +307,11 @@ enum SPAttributeEnum { SP_PROP_STROKE_MITERLIMIT, SP_PROP_STROKE_OPACITY, SP_PROP_STROKE_WIDTH, - SP_PROP_TEXT_RENDERING + SP_PROP_TEXT_RENDERING, + /* Conditional */ + SP_PROP_SYSTEM_LANGUAGE, + SP_PROP_REQUIRED_FEATURES, + SP_PROP_REQUIRED_EXTENSIONS, }; #endif diff --git a/src/conditions.cpp b/src/conditions.cpp new file mode 100644 index 000000000..e5a1d47c9 --- /dev/null +++ b/src/conditions.cpp @@ -0,0 +1,462 @@ +#define __SP_CONDITIONS_CPP__ + +/* + * SVG conditional attribute evaluation + * + * Authors: + * Andrius R. + * + * Copyright (C) 2006 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include "conditions.h" +#include "xml/repr.h" +#include "dialogs/rdf.h" + +typedef bool (* condition_evaluator)(SPItem const *item, gchar const *value); + +struct Condition { + gchar const *attribute; + condition_evaluator evaluator; +}; + +static bool evaluateSystemLanguage(SPItem const *item, gchar const *value); +static bool evaluateRequiredFeatures(SPItem const *item, gchar const *value); +static bool evaluateRequiredExtensions(SPItem const *item, gchar const *value); + +/* define any conditional attributes and their handler functions in this array */ +static Condition _condition_handlers[] = { + { "systemLanguage", evaluateSystemLanguage }, + { "requiredFeatures", evaluateRequiredFeatures }, + { "requiredExtensions", evaluateRequiredExtensions }, +}; + +/* function which evaluates if item should be displayed */ +bool sp_item_evaluate(SPItem const *item) { + Inkscape::XML::Node *grepr = SP_OBJECT_REPR (item); + + for ( unsigned int i = 0 ; i < sizeof(_condition_handlers)/sizeof(_condition_handlers[0]) ; i++ ) { + gchar const *value = grepr->attribute(_condition_handlers[i].attribute); + if ( NULL == value ) + continue; + + if (!_condition_handlers[i].evaluator(item, value)) + return false; + } + + return true; +} + +#define ISALNUM(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z') || ((c) >= '0' && (c) <= '9')) + +static gchar *preprocessLanguageCode(gchar *lngcode) { + if ( NULL == lngcode ) + return NULL; + + lngcode = g_strstrip(lngcode); + if ( 0 == *lngcode ) + return lngcode; + for ( unsigned int i = 0 ; i < strlen(lngcode) ; i++ ) { + if ( lngcode[i] >= 'A' && lngcode[i] <= 'Z' ) { + lngcode[i] = g_ascii_tolower(lngcode[i]); + } else if ( '_' == lngcode[i] ) { + lngcode[i] = '-'; + } else if ( !ISALNUM(lngcode[i]) && '-' != lngcode[i] ) { + // only alpha numeric characters and '-' may be contained in language code + lngcode[0] = 0; + break; + } + } + + return lngcode; +} + +static bool evaluateSystemLanguage(SPItem const *item, gchar const *value) { + if ( NULL == value ) + return true; + + std::set language_codes; + gchar *str = NULL; + gchar **strlist = g_strsplit( value, ",", 0); + + for ( int i = 0 ; (str = strlist[i]) ; i++ ) { + gchar *lngcode = preprocessLanguageCode(str); + if ( 0 == *lngcode ) + continue; + language_codes.insert(lngcode); + + gchar *pos = strchr (lngcode, '-'); + if (pos) + { + // if subtag is used, primary tag is still a perfect match + *pos = 0; + if ( language_codes.find(lngcode) == language_codes.end() ) { + language_codes.insert(lngcode); + } + } + } + g_strfreev(strlist); + + if (language_codes.empty()) + return false; + + SPDocument *document = SP_OBJECT_DOCUMENT(item); + Glib::ustring document_language = document->getLanguage(); + + if (document_language.size() == 0) + return false; + + bool match = true; + strlist = g_strsplit( document_language.c_str(), ",", 0); + for ( int i = 0 ; (str = strlist[i]) ; i++ ) { + gchar *lngcode = preprocessLanguageCode(str); + if ( 0 == *lngcode ) + continue; + if ( language_codes.find(lngcode) != language_codes.end() ) { + match = true; + break; + } + match = false; + } + g_strfreev(strlist); + return match; +} + +static std::vector splitByWhitespace(gchar const *value) { + std::vector parts; + gchar *str = NULL; + gchar **strlist = g_strsplit( value, ",", 0); + + for ( int i = 0 ; (str = strlist[i]) ; i++ ) { + gchar *part = g_strstrip(str); + if ( 0 == *part ) + continue; + parts.push_back(part); + } + g_strfreev(strlist); + return parts; +} + +#define SVG11FEATURE "http://www.w3.org/TR/SVG11/feature#" +#define SVG10FEATURE "org.w3c." + +static bool evaluateSVG11Feature(gchar const *feature) { + static gchar *_supported_features[] = { + "SVG", // incomplete "SVG-static" - missing support for "Filter" + /* SVG - user agent supports at least one of the following: + "SVG-static", "SVG-animation", "SVG-dynamic" or "SVGDOM" */ + // "SVGDOM", // not sure + /* SVGDOM - user agent supports at least one of the following: + "SVGDOM-static", "SVGDOM-animation" or "SVGDOM-dynamic" */ + "SVG-static", // incomplete - missing support for "Filter" + /* SVG-static - user agent supports the following features: + "CoreAttribute", "Structure", "ContainerAttribute", + "ConditionalProcessing", "Image", "Style", "ViewportAttribute", + "Shape", "Text", "PaintAttribute", "OpacityAttribute", + "GraphicsAttribute", "Marker", "ColorProfile", + "Gradient", "Pattern", "Clip", "Mask", "Filter", + "XlinkAttribute", "Font", "Extensibility" */ + // "SVGDOM-static", // not sure + /* SVGDOM-static - All of the DOM interfaces and methods + that correspond to SVG-static */ + // "SVG-animation", // no support + /* SVG-animation - All of the language features from "SVG-static" + plus the feature "feature#Animation" */ + // "SVGDOM-animation", // no support + /* SVGDOM-animation - All of the DOM interfaces and methods + that correspond to SVG-animation */ + // "SVG-dynamic", // no support + /* SVG-dynamic - user agent supports all "SVG-animation" and the following features: + "Hyperlinking", "Scripting", "View", "Cursor", + "GraphicalEventsAttribute", "DocumentEventsAttribute", "AnimationEventsAttribute" */ + // "SVGDOM-dynamic", // no support + /* SVGDOM-dynamic - All of the DOM interfaces and methods + that correspond to SVG-dynamic */ + "CoreAttribute", + "Structure", + "BasicStructure", + "ContainerAttribute", + "ConditionalProcessing", + "Image", + "Style", + "ViewportAttribute", // not sure + "Shape", + "Text", + "BasicText", + "PaintAttribute", + "BasicPaintAttribute", + "OpacityAttribute", + "GraphicsAttribute", + "BasicGraphicsAttribute", + "Marker", + "ColorProfile", + "Gradient", + "Pattern", + "Clip", + "BasicClip", + "Mask", + // "Filter", + // "BasicFilter", + // "DocumentEventsAttribute", + // "GraphicalEventsAttribute", + // "AnimationEventsAttribute", + // "Cursor", // not sure + "Hyperlinking", // not sure + "XlinkAttribute", // not sure + "ExternalResourcesRequired", // not sure + "View", + // "Script", + // "Animation", + "Font", + "BasicFont", + "Extensibility", // not sure + }; + + for ( unsigned int i = 0 ; i < sizeof(_supported_features)/sizeof(_supported_features[0]); i++ ) { + if ( 0 == strcasecmp(feature, _supported_features[0]) ) + return true; + } + return false; +} + +static bool evaluateSVG10Feature(gchar const *feature) { + static gchar *_supported_features[] = { + "svg.static", // incomplete - no filter effects + "dom.svg.static", // not sure + // "svg.animation", + // "dom.svg.animation", + // "svg.dynamic", + // "dom.svg.dynamic" + // "svg.all", + // "dom.svg.all" + }; + for ( unsigned int i = 0 ; i < sizeof(_supported_features)/sizeof(_supported_features[0]); i++ ) { + if ( 0 == strcasecmp(feature, _supported_features[0]) ) + return true; + } + return false; +} + +static bool evaluateSingleFeature(gchar const *value) { + if ( NULL == value ) + return false; + gchar const *found; + found = strstr(value, SVG11FEATURE); + if ( value == found ) + return evaluateSVG11Feature(found + strlen(SVG11FEATURE)); + found = strstr(value, SVG10FEATURE); + if ( value == found ) + return evaluateSVG10Feature(found + strlen(SVG10FEATURE)); + return false; +} + +static bool evaluateRequiredFeatures(SPItem const *item, gchar const *value) { + if ( NULL == value ) + return true; + + std::vector parts = splitByWhitespace(value); + if ( 0 == parts.size() ) + return false; + + for ( unsigned int i = 0 ; i < parts.size() ; i++ ) { + if (!evaluateSingleFeature(parts[i].c_str())) { + return false; + } + } + + return true; +} + +static bool evaluateRequiredExtensions(SPItem const *item, gchar const *value) { + if ( NULL == value ) + return true; + return false; +} + +/* + * Language codes and names: +aa Afar +ab Abkhazian +af Afrikaans +am Amharic +ar Arabic +as Assamese +ay Aymara +az Azerbaijani + +ba Bashkir +be Byelorussian +bg Bulgarian +bh Bihari +bi Bislama +bn Bengali; Bangla +bo Tibetan +br Breton + +ca Catalan +co Corsican +cs Czech +cy Welsh + +da Danish +de German +dz Bhutani + +el Greek +en English +eo Esperanto +es Spanish +et Estonian +eu Basque + +fa Persian +fi Finnish +fj Fiji +fo Faroese +fr French +fy Frisian + +ga Irish +gd Scots Gaelic +gl Galician +gn Guarani +gu Gujarati + +ha Hausa +he Hebrew (formerly iw) +hi Hindi +hr Croatian +hu Hungarian +hy Armenian + +ia Interlingua +id Indonesian (formerly in) +ie Interlingue +ik Inupiak +is Icelandic +it Italian +iu Inuktitut + +ja Japanese +jw Javanese + +ka Georgian +kk Kazakh +kl Greenlandic +km Cambodian +kn Kannada +ko Korean +ks Kashmiri +ku Kurdish +ky Kirghiz + +la Latin +ln Lingala +lo Laothian +lt Lithuanian +lv Latvian, Lettish + +mg Malagasy +mi Maori +mk Macedonian +ml Malayalam +mn Mongolian +mo Moldavian +mr Marathi +ms Malay +mt Maltese +my Burmese + +na Nauru +ne Nepali +nl Dutch +no Norwegian + +oc Occitan +om (Afan) Oromo +or Oriya + +pa Punjabi +pl Polish +ps Pashto, Pushto +pt Portuguese + +qu Quechua + +rm Rhaeto-Romance +rn Kirundi +ro Romanian +ru Russian +rw Kinyarwanda + +sa Sanskrit +sd Sindhi +sg Sangho +sh Serbo-Croatian +si Sinhalese +sk Slovak +sl Slovenian +sm Samoan +sn Shona +so Somali +sq Albanian +sr Serbian +ss Siswati +st Sesotho +su Sundanese +sv Swedish +sw Swahili + +ta Tamil +te Telugu +tg Tajik +th Thai +ti Tigrinya +tk Turkmen +tl Tagalog +tn Setswana +to Tonga +tr Turkish +ts Tsonga +tt Tatar +tw Twi + +ug Uighur +uk Ukrainian +ur Urdu +uz Uzbek + +vi Vietnamese +vo Volapuk + +wo Wolof + +xh Xhosa + +yi Yiddish (formerly ji) +yo Yoruba + +za Zhuang +zh Chinese +zu Zulu + */ + + +/* + 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:encoding=utf-8:textwidth=99 : diff --git a/src/conditions.h b/src/conditions.h new file mode 100644 index 000000000..35a20ae4f --- /dev/null +++ b/src/conditions.h @@ -0,0 +1,8 @@ +#ifndef CONDITIONS_H_ +#define CONDITIONS_H_ + +#include "sp-item.h" + +bool sp_item_evaluate(SPItem const *item); + +#endif /*CONDITIONS_H_*/ diff --git a/src/display/sp-canvas.cpp b/src/display/sp-canvas.cpp index 5e19539f3..edd429f0d 100644 --- a/src/display/sp-canvas.cpp +++ b/src/display/sp-canvas.cpp @@ -1254,7 +1254,7 @@ emit_event (SPCanvas *canvas, GdkEvent *event) static int pick_current_item (SPCanvas *canvas, GdkEvent *event) { - int button_down; + int button_down = 0; double x, y; int retval = FALSE; diff --git a/src/document.cpp b/src/document.cpp index 863266c05..ef04735ef 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -673,6 +673,35 @@ SPObject *SPDocument::getObjectByRepr(Inkscape::XML::Node *repr) { return (SPObject*)g_hash_table_lookup(priv->reprdef, repr); } +Glib::ustring SPDocument::getLanguage() { + gchar const *document_language = rdf_get_work_entity(this, rdf_find_entity("language")); + if (document_language) { + while (isspace(*document_language)) + document_language++; + } + if ( !document_language || 0 == *document_language) { + // retrieve system language + document_language = getenv("LC_ALL"); + if ( NULL == document_language || *document_language == 0 ) { + document_language = getenv ("LC_MESSAGES"); + } + if ( NULL == document_language || *document_language == 0 ) { + document_language = getenv ("LANG"); + } + + if ( NULL != document_language ) { + gchar *pos = strchr(document_language, '_'); + if ( NULL != pos ) { + return Glib::ustring(document_language, pos - document_language); + } + } + } + + if ( NULL == document_language ) + return Glib::ustring(); + return document_language; +} + /* Object modification root handler */ void diff --git a/src/document.h b/src/document.h index f06ad2f6d..f601b15ff 100644 --- a/src/document.h +++ b/src/document.h @@ -26,6 +26,7 @@ #include "gc-managed.h" #include "gc-finalized.h" #include "gc-anchored.h" +#include namespace Avoid { class Router; @@ -98,6 +99,8 @@ struct SPDocument : public Inkscape::GC::Managed<>, void bindObjectToRepr(Inkscape::XML::Node *repr, SPObject *object); SPObject *getObjectByRepr(Inkscape::XML::Node *repr); + Glib::ustring getLanguage(); + void queueForOrphanCollection(SPObject *object); void collectOrphans(); diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp index b4b69e1e4..3ce551b98 100644 --- a/src/sp-item-group.cpp +++ b/src/sp-item-group.cpp @@ -38,7 +38,7 @@ static void sp_group_class_init (SPGroupClass *klass); static void sp_group_init (SPGroup *group); static void sp_group_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr); static void sp_group_release(SPObject *object); -static void sp_group_dispose (GObject *object); +static void sp_group_dispose(GObject *object); static void sp_group_child_added (SPObject * object, Inkscape::XML::Node * child, Inkscape::XML::Node * ref); static void sp_group_remove_child (SPObject * object, Inkscape::XML::Node * child); @@ -116,6 +116,7 @@ static void sp_group_init (SPGroup *group) { group->_layer_mode = SPGroup::GROUP; + group->group = new CGroup(group); new (&group->_display_modes) std::map(); } @@ -141,6 +142,7 @@ static void sp_group_dispose(GObject *object) { SP_GROUP(object)->_display_modes.~map(); + delete SP_GROUP(object)->group; } static void @@ -153,46 +155,7 @@ sp_group_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XM if (((SPObjectClass *) (parent_class))->child_added) (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref); - SPObject *last_child = object->lastChild(); - if (last_child && SP_OBJECT_REPR(last_child) == child) { - // optimization for the common special case where the child is being added at the end - SPObject *ochild = last_child; - if ( SP_IS_ITEM(ochild) ) { - /* TODO: this should be moved into SPItem somehow */ - SPItemView *v; - NRArenaItem *ac; - - for (v = item->display; v != NULL; v = v->next) { - ac = sp_item_invoke_show (SP_ITEM (ochild), NR_ARENA_ITEM_ARENA (v->arenaitem), v->key, v->flags); - - if (ac) { - nr_arena_item_append_child (v->arenaitem, ac); - nr_arena_item_unref (ac); - } - } - } - } else { // general case - SPObject *ochild = sp_object_get_child_by_repr(object, child); - if ( ochild && SP_IS_ITEM(ochild) ) { - /* TODO: this should be moved into SPItem somehow */ - SPItemView *v; - NRArenaItem *ac; - - unsigned position = sp_item_pos_in_parent(SP_ITEM(ochild)); - - for (v = item->display; v != NULL; v = v->next) { - ac = sp_item_invoke_show (SP_ITEM (ochild), NR_ARENA_ITEM_ARENA (v->arenaitem), v->key, v->flags); - - if (ac) { - nr_arena_item_add_child (v->arenaitem, ac, NULL); - nr_arena_item_set_order (ac, position); - nr_arena_item_unref (ac); - } - } - } - } - - object->requestModified(SP_OBJECT_MODIFIED_FLAG); + SP_GROUP(object)->group->onChildAdded(child); } /* fixme: hide (Lauris) */ @@ -203,7 +166,7 @@ sp_group_remove_child (SPObject * object, Inkscape::XML::Node * child) if (((SPObjectClass *) (parent_class))->remove_child) (* ((SPObjectClass *) (parent_class))->remove_child) (object, child); - object->requestModified(SP_OBJECT_MODIFIED_FLAG); + SP_GROUP(object)->group->onChildRemoved(child); } static void @@ -212,86 +175,22 @@ sp_group_order_changed (SPObject *object, Inkscape::XML::Node *child, Inkscape:: if (((SPObjectClass *) (parent_class))->order_changed) (* ((SPObjectClass *) (parent_class))->order_changed) (object, child, old_ref, new_ref); - SPObject *ochild = sp_object_get_child_by_repr(object, child); - if ( ochild && SP_IS_ITEM(ochild) ) { - /* TODO: this should be moved into SPItem somehow */ - SPItemView *v; - unsigned position = sp_item_pos_in_parent(SP_ITEM(ochild)); - for ( v = SP_ITEM (ochild)->display ; v != NULL ; v = v->next ) { - nr_arena_item_set_order (v->arenaitem, position); - } - } - - object->requestModified(SP_OBJECT_MODIFIED_FLAG); + SP_GROUP(object)->group->onOrderChanged(child, old_ref, new_ref); } static void sp_group_update (SPObject *object, SPCtx *ctx, unsigned int flags) { - SPGroup *group; - SPObject *child; - SPItemCtx *ictx, cctx; - GSList *l; - - group = SP_GROUP (object); - ictx = (SPItemCtx *) ctx; - cctx = *ictx; + if (((SPObjectClass *) (parent_class))->update) + ((SPObjectClass *) (parent_class))->update (object, ctx, flags); - if (((SPObjectClass *) (parent_class))->update) - ((SPObjectClass *) (parent_class))->update (object, ctx, flags); - - if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; - flags &= SP_OBJECT_MODIFIED_CASCADE; - - l = NULL; - for (child = sp_object_first_child(object) ; child != NULL ; child = SP_OBJECT_NEXT(child) ) { - g_object_ref (G_OBJECT (child)); - l = g_slist_prepend (l, child); - } - l = g_slist_reverse (l); - while (l) { - child = SP_OBJECT (l->data); - l = g_slist_remove (l, child); - if (flags || (child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { - if (SP_IS_ITEM (child)) { - SPItem const &chi = *SP_ITEM(child); - cctx.i2doc = chi.transform * ictx->i2doc; - cctx.i2vp = chi.transform * ictx->i2vp; - child->updateDisplay((SPCtx *)&cctx, flags); - } else { - child->updateDisplay(ctx, flags); - } - } - g_object_unref (G_OBJECT (child)); - } + SP_GROUP(object)->group->onUpdate(ctx, flags); } static void sp_group_modified (SPObject *object, guint flags) { - SPGroup *group; - SPObject *child; - GSList *l; - - group = SP_GROUP (object); - - if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; - flags &= SP_OBJECT_MODIFIED_CASCADE; - - l = NULL; - for (child = sp_object_first_child(object) ; child != NULL ; child = SP_OBJECT_NEXT(child) ) { - g_object_ref (G_OBJECT (child)); - l = g_slist_prepend (l, child); - } - l = g_slist_reverse (l); - while (l) { - child = SP_OBJECT (l->data); - l = g_slist_remove (l, child); - if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { - child->emitModified(flags); - } - g_object_unref (G_OBJECT (child)); - } + SP_GROUP(object)->group->onModified(flags); } static Inkscape::XML::Node * @@ -343,51 +242,18 @@ sp_group_write (SPObject *object, Inkscape::XML::Node *repr, guint flags) static void sp_group_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags) { - for (SPObject *o = sp_object_first_child(SP_OBJECT(item)); o != NULL; o = SP_OBJECT_NEXT(o)) { - if (SP_IS_ITEM(o)) { - SPItem *child = SP_ITEM(o); - NR::Matrix const ct(child->transform * transform); - sp_item_invoke_bbox_full(child, bbox, ct, flags, FALSE); - } - } + SP_GROUP(item)->group->calculateBBox(bbox, transform, flags); } static void sp_group_print (SPItem * item, SPPrintContext *ctx) { - SPGroup * group; - SPItem * child; - SPObject * o; - - group = SP_GROUP (item); - - for (o = sp_object_first_child(SP_OBJECT(item)) ; o != NULL ; o = SP_OBJECT_NEXT(o) ) { - if (SP_IS_ITEM (o)) { - child = SP_ITEM (o); - sp_item_invoke_print (SP_ITEM (o), ctx); - } - } + SP_GROUP(item)->group->onPrint(ctx); } static gchar * sp_group_description (SPItem * item) { - SPGroup * group; - SPObject * o; - gint len; - - group = SP_GROUP (item); - - len = 0; - for ( o = sp_object_first_child(SP_OBJECT(item)) ; o != NULL ; o = SP_OBJECT_NEXT(o) ) { - if (SP_IS_ITEM(o)) { - len += 1; - } - } - - return g_strdup_printf( - ngettext("Group of %d object", - "Group of %d objects", - len), len); + return SP_GROUP(item)->group->getDescription(); } static void sp_group_set(SPObject *object, unsigned key, char const *value) { @@ -412,53 +278,13 @@ static void sp_group_set(SPObject *object, unsigned key, char const *value) { static NRArenaItem * sp_group_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags) { - SPGroup *group; - NRArenaItem *ai, *ac, *ar; - SPItem * child; - SPObject * o; - - group = (SPGroup *) item; - - ai = NRArenaGroup::create(arena); - nr_arena_group_set_transparent(NR_ARENA_GROUP (ai), - group->effectiveLayerMode(key) == - SPGroup::LAYER); - - ar = NULL; - - for (o = sp_object_first_child(SP_OBJECT(item)) ; o != NULL; o = SP_OBJECT_NEXT(o) ) { - if (SP_IS_ITEM (o)) { - child = SP_ITEM (o); - ac = sp_item_invoke_show (child, arena, key, flags); - if (ac) { - nr_arena_item_add_child (ai, ac, ar); - ar = ac; - nr_arena_item_unref (ac); - } - } - } - - return ai; + return SP_GROUP(item)->group->show(arena, key, flags); } static void sp_group_hide (SPItem *item, unsigned int key) { - SPGroup * group; - SPItem * child; - SPObject * o; - - group = (SPGroup *) item; - - for (o = sp_object_first_child(SP_OBJECT(item)) ; o != NULL; o = SP_OBJECT_NEXT(o) ) { - if (SP_IS_ITEM (o)) { - child = SP_ITEM (o); - sp_item_invoke_hide (child, key); - } - } - - if (((SPItemClass *) parent_class)->hide) - ((SPItemClass *) parent_class)->hide (item, key); + SP_GROUP(item)->group->hide(key); } static void sp_group_snappoints (SPItem const *item, SnapPointsIter p) @@ -697,3 +523,220 @@ void SPGroup::_updateLayerMode(unsigned int display_key) { } } } + +CGroup::CGroup(SPGroup *group) { + _group = group; +} + +CGroup::~CGroup() { +} + +void CGroup::onChildAdded(Inkscape::XML::Node *child) { + SPObject *last_child = _group->lastChild(); + if (last_child && SP_OBJECT_REPR(last_child) == child) { + // optimization for the common special case where the child is being added at the end + SPObject *ochild = last_child; + if ( SP_IS_ITEM(ochild) ) { + /* TODO: this should be moved into SPItem somehow */ + SPItemView *v; + NRArenaItem *ac; + + for (v = _group->display; v != NULL; v = v->next) { + ac = sp_item_invoke_show (SP_ITEM (ochild), NR_ARENA_ITEM_ARENA (v->arenaitem), v->key, v->flags); + + if (ac) { + nr_arena_item_append_child (v->arenaitem, ac); + nr_arena_item_unref (ac); + } + } + } + } else { // general case + SPObject *ochild = sp_object_get_child_by_repr(_group, child); + if ( ochild && SP_IS_ITEM(ochild) ) { + /* TODO: this should be moved into SPItem somehow */ + SPItemView *v; + NRArenaItem *ac; + + unsigned position = sp_item_pos_in_parent(SP_ITEM(ochild)); + + for (v = _group->display; v != NULL; v = v->next) { + ac = sp_item_invoke_show (SP_ITEM (ochild), NR_ARENA_ITEM_ARENA (v->arenaitem), v->key, v->flags); + + if (ac) { + nr_arena_item_add_child (v->arenaitem, ac, NULL); + nr_arena_item_set_order (ac, position); + nr_arena_item_unref (ac); + } + } + } + } + + _group->requestModified(SP_OBJECT_MODIFIED_FLAG); +} + +void CGroup::onChildRemoved(Inkscape::XML::Node */*child*/) { + _group->requestModified(SP_OBJECT_MODIFIED_FLAG); +} + +void CGroup::onUpdate(SPCtx *ctx, unsigned int flags) { + SPObject *child; + SPItemCtx *ictx, cctx; + + ictx = (SPItemCtx *) ctx; + cctx = *ictx; + + if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + flags &= SP_OBJECT_MODIFIED_CASCADE; + + GSList *l = g_slist_reverse(_childList(true, ActionUpdate)); + while (l) { + child = SP_OBJECT (l->data); + l = g_slist_remove (l, child); + if (flags || (child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + if (SP_IS_ITEM (child)) { + SPItem const &chi = *SP_ITEM(child); + cctx.i2doc = chi.transform * ictx->i2doc; + cctx.i2vp = chi.transform * ictx->i2vp; + child->updateDisplay((SPCtx *)&cctx, flags); + } else { + child->updateDisplay(ctx, flags); + } + } + g_object_unref (G_OBJECT (child)); + } +} + +GSList *CGroup::_childList(bool add_ref, Action) { + GSList *l = NULL; + for (SPObject *child = sp_object_first_child(_group) ; child != NULL ; child = SP_OBJECT_NEXT(child) ) { + if (add_ref) + g_object_ref (G_OBJECT (child)); + + l = g_slist_prepend (l, child); + } + return l; +} + +void CGroup::onModified(guint flags) { + SPObject *child; + + if (flags & SP_OBJECT_MODIFIED_FLAG) flags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + flags &= SP_OBJECT_MODIFIED_CASCADE; + + GSList *l = g_slist_reverse(_childList(true)); + while (l) { + child = SP_OBJECT (l->data); + l = g_slist_remove (l, child); + if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + child->emitModified(flags); + } + g_object_unref (G_OBJECT (child)); + } +} + +void CGroup::calculateBBox(NRRect *bbox, NR::Matrix const &transform, unsigned const flags) { + GSList *l = _childList(false, ActionBBox); + while (l) { + SPObject *o = SP_OBJECT (l->data); + if (SP_IS_ITEM(o)) { + SPItem *child = SP_ITEM(o); + NR::Matrix const ct(child->transform * transform); + sp_item_invoke_bbox_full(child, bbox, ct, flags, FALSE); + } + l = g_slist_remove (l, o); + } +} + +void CGroup::onPrint(SPPrintContext *ctx) { + GSList *l = _childList(false); + while (l) { + SPObject *o = SP_OBJECT (l->data); + if (SP_IS_ITEM(o)) { + sp_item_invoke_print (SP_ITEM (o), ctx); + } + l = g_slist_remove (l, o); + } +} + +gint CGroup::getItemCount() { + gint len = 0; + for (SPObject *o = sp_object_first_child(SP_OBJECT(_group)) ; o != NULL ; o = SP_OBJECT_NEXT(o) ) { + if (SP_IS_ITEM(o)) { + len++; + } + } + + return len; +} + +gchar *CGroup::getDescription() { + gint len = getItemCount(); + return g_strdup_printf( + ngettext("Group of %d object", + "Group of %d objects", + len), len); +} + +NRArenaItem *CGroup::show (NRArena *arena, unsigned int key, unsigned int flags) { + NRArenaItem *ai; + + ai = NRArenaGroup::create(arena); + nr_arena_group_set_transparent(NR_ARENA_GROUP (ai), + _group->effectiveLayerMode(key) == + SPGroup::LAYER); + + _showChildren(arena, ai, key, flags); + return ai; +} + +void CGroup::_showChildren (NRArena *arena, NRArenaItem *ai, unsigned int key, unsigned int flags) { + NRArenaItem *ac = NULL; + NRArenaItem *ar = NULL; + SPItem * child = NULL; + GSList *l = _childList(false, ActionShow); + while (l) { + SPObject *o = SP_OBJECT (l->data); + if (SP_IS_ITEM (o)) { + child = SP_ITEM (o); + ac = sp_item_invoke_show (child, arena, key, flags); + if (ac) { + nr_arena_item_add_child (ai, ac, ar); + ar = ac; + nr_arena_item_unref (ac); + } + } + l = g_slist_remove (l, o); + } +} + +void CGroup::hide (unsigned int key) { + SPItem * child; + + GSList *l = _childList(false, ActionShow); + while (l) { + SPObject *o = SP_OBJECT (l->data); + if (SP_IS_ITEM (o)) { + child = SP_ITEM (o); + sp_item_invoke_hide (child, key); + } + l = g_slist_remove (l, o); + } + + if (((SPItemClass *) parent_class)->hide) + ((SPItemClass *) parent_class)->hide (_group, key); +} + +void CGroup::onOrderChanged (Inkscape::XML::Node *child, Inkscape::XML::Node *, Inkscape::XML::Node *) +{ + SPObject *ochild = sp_object_get_child_by_repr(_group, child); + if ( ochild && SP_IS_ITEM(ochild) ) { + /* TODO: this should be moved into SPItem somehow */ + SPItemView *v; + unsigned position = sp_item_pos_in_parent(SP_ITEM(ochild)); + for ( v = SP_ITEM (ochild)->display ; v != NULL ; v = v->next ) { + nr_arena_item_set_order (v->arenaitem, position); + } + } + + _group->requestModified(SP_OBJECT_MODIFIED_FLAG); +} diff --git a/src/sp-item-group.h b/src/sp-item-group.h index 578454c92..ebf5c0402 100644 --- a/src/sp-item-group.h +++ b/src/sp-item-group.h @@ -22,6 +22,8 @@ #define SP_IS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_GROUP)) #define SP_IS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_GROUP)) +class CGroup; + struct SPGroup : public SPItem { enum LayerMode { GROUP, LAYER }; @@ -42,6 +44,8 @@ struct SPGroup : public SPItem { LayerMode layerDisplayMode(unsigned int display_key) const; void setLayerDisplayMode(unsigned int display_key, LayerMode mode); + CGroup *group; + private: void _updateLayerMode(unsigned int display_key=0); }; @@ -50,6 +54,35 @@ struct SPGroupClass { SPItemClass parent_class; }; +/* + * Virtual methods of SPGroup + */ +class CGroup { +public: + CGroup(SPGroup *group); + virtual ~CGroup(); + + virtual void onChildAdded(Inkscape::XML::Node *child); + virtual void onChildRemoved(Inkscape::XML::Node *child); + virtual void onUpdate(SPCtx *ctx, unsigned int flags); + virtual void onModified(guint flags); + virtual void calculateBBox(NRRect *bbox, NR::Matrix const &transform, unsigned const flags); + virtual void onPrint(SPPrintContext *ctx); + virtual void onOrderChanged(Inkscape::XML::Node *child, Inkscape::XML::Node *old_ref, Inkscape::XML::Node *new_ref); + virtual gchar *getDescription(); + virtual NRArenaItem *show (NRArena *arena, unsigned int key, unsigned int flags); + virtual void hide (unsigned int key); + + gint getItemCount(); + +protected: + enum Action { ActionGeneral, ActionBBox, ActionUpdate, ActionShow }; + virtual GSList *_childList(bool add_ref, Action action = ActionGeneral); + virtual void _showChildren (NRArena *arena, NRArenaItem *ai, unsigned int key, unsigned int flags); + + SPGroup *_group; +}; + GType sp_group_get_type (void); void sp_item_group_ungroup (SPGroup *group, GSList **children, bool do_done = true); diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 0e45bc7f4..c2e3bb1bf 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -44,9 +44,11 @@ #include "sp-text.h" #include "sp-item-rm-unsatisfied-cns.h" #include "sp-pattern.h" +#include "sp-switch.h" #include "gradient-chemistry.h" #include "prefs-utils.h" #include "conn-avoid-ref.h" +#include "conditions.h" #include "libnr/nr-matrix-div.h" #include "libnr/nr-matrix-fns.h" @@ -130,34 +132,39 @@ sp_item_class_init(SPItemClass *klass) static void sp_item_init(SPItem *item) { - SPObject *object = SP_OBJECT(item); + item->init(); +} - item->sensitive = TRUE; +void SPItem::init() { + this->sensitive = TRUE; - item->transform_center_x = 0; - item->transform_center_y = 0; + this->transform_center_x = 0; + this->transform_center_y = 0; - item->transform = NR::identity(); + this->_is_evaluated = true; + this->_evaluated_status = StatusCalculated; - item->display = NULL; + this->transform = NR::identity(); - item->clip_ref = new SPClipPathReference(SP_OBJECT(item)); + this->display = NULL; + + this->clip_ref = new SPClipPathReference(this); { - sigc::signal cs1=item->clip_ref->changedSignal(); - sigc::slot2 sl1=sigc::bind(sigc::ptr_fun(clip_ref_changed), item); + sigc::signal cs1=this->clip_ref->changedSignal(); + sigc::slot2 sl1=sigc::bind(sigc::ptr_fun(clip_ref_changed), this); cs1.connect(sl1); } - item->mask_ref = new SPMaskReference(SP_OBJECT(item)); - sigc::signal cs2=item->mask_ref->changedSignal(); - sigc::slot2 sl2=sigc::bind(sigc::ptr_fun(mask_ref_changed), item); + this->mask_ref = new SPMaskReference(this); + sigc::signal cs2=this->mask_ref->changedSignal(); + sigc::slot2 sl2=sigc::bind(sigc::ptr_fun(mask_ref_changed), this); cs2.connect(sl2); - if (!object->style) object->style = sp_style_new_from_object(SP_OBJECT(item)); + if (!this->style) this->style = sp_style_new_from_object(this); - item->avoidRef = new SPAvoidRef(item); + this->avoidRef = new SPAvoidRef(this); - new (&item->_transformed_signal) sigc::signal(); + new (&this->_transformed_signal) sigc::signal(); } bool SPItem::isVisibleAndUnlocked() const { @@ -183,6 +190,8 @@ void SPItem::setLocked(bool locked) { } bool SPItem::isHidden() const { + if (!isEvaluated()) + return true; return style->display.computed == SP_CSS_DISPLAY_NONE; } @@ -195,6 +204,8 @@ void SPItem::setHidden(bool hide) { } bool SPItem::isHidden(unsigned display_key) const { + if (!isEvaluated()) + return true; for ( SPItemView *view(display) ; view ; view = view->next ) { if ( view->key == display_key ) { g_assert(view->arenaitem != NULL); @@ -211,6 +222,34 @@ bool SPItem::isHidden(unsigned display_key) const { return true; } +void SPItem::setEvaluated(bool evaluated) { + _is_evaluated = evaluated; + _evaluated_status = StatusSet; +} + +void SPItem::resetEvaluated() { + if ( StatusCalculated == _evaluated_status ) { + _evaluated_status = StatusUnknown; + bool oldValue = _is_evaluated; + if ( oldValue != isEvaluated() ) { + requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); + } + } if ( StatusSet == _evaluated_status ) { + SPObject const *const parent = SP_OBJECT_PARENT(this); + if (SP_IS_SWITCH(parent)) { + SP_SWITCH(parent)->resetChildEvaluated(); + } + } +} + +bool SPItem::isEvaluated() const { + if ( StatusUnknown == _evaluated_status ) { + _is_evaluated = sp_item_evaluate(this); + _evaluated_status = StatusCalculated; + } + return _is_evaluated; +} + /** * Returns something suitable for the `Hide' checkbox in the Object Properties dialog box. * Corresponds to setExplicitlyHidden. @@ -466,6 +505,13 @@ sp_item_set(SPObject *object, unsigned key, gchar const *value) item->transform_center_y = 0; } break; + case SP_PROP_SYSTEM_LANGUAGE: + case SP_PROP_REQUIRED_FEATURES: + case SP_PROP_REQUIRED_EXTENSIONS: + { + item->resetEvaluated(); + // pass to default handler + } default: if (SP_ATTRIBUTE_IS_CSS(key)) { sp_style_read_from_object(object->style, object); @@ -741,7 +787,7 @@ sp_item_bbox_desktop(SPItem *item, NRRect *bbox) NR::Rect sp_item_bbox_desktop(SPItem *item) { NRRect ret; - sp_item_bbox_desktop(item, &ret); + sp_item_invoke_bbox(item, &ret, sp_item_i2d_affine(item), TRUE); return NR::Rect(ret); } diff --git a/src/sp-item.h b/src/sp-item.h index 141d3f881..cb34817e5 100644 --- a/src/sp-item.h +++ b/src/sp-item.h @@ -107,12 +107,17 @@ struct SPItem : public SPObject { std::vector constraints; sigc::signal _transformed_signal; - + + void init(); bool isLocked() const; void setLocked(bool lock); bool isHidden() const; void setHidden(bool hidden); + + bool isEvaluated() const; + void setEvaluated(bool visible); + void resetEvaluated(); bool isHidden(unsigned display_key) const; @@ -141,6 +146,15 @@ struct SPItem : public SPObject { sigc::connection connectTransformed(sigc::slot slot) { return _transformed_signal.connect(slot); } + +private: + enum EvaluatedStatus + { + StatusUnknown, StatusCalculated, StatusSet + }; + + mutable bool _is_evaluated; + mutable EvaluatedStatus _evaluated_status; }; typedef std::back_insert_iterator > SnapPointsIter; diff --git a/src/sp-object-repr.cpp b/src/sp-object-repr.cpp index 3987dbcbb..0f2ff9e6a 100644 --- a/src/sp-object-repr.cpp +++ b/src/sp-object-repr.cpp @@ -40,6 +40,7 @@ #include "sp-flowregion.h" #include "sp-flowtext.h" #include "sp-style-elem.h" +#include "sp-switch.h" #include "color-profile-fns.h" #include "xml/repr.h" @@ -125,7 +126,7 @@ populate_dtables() { "svg:stop", SP_TYPE_STOP }, { "svg:svg", SP_TYPE_ROOT }, { "svg:style", SP_TYPE_STYLE_ELEM }, - { "svg:switch", SP_TYPE_GROUP }, + { "svg:switch", SP_TYPE_SWITCH }, { "svg:symbol", SP_TYPE_SYMBOL }, { "svg:text", SP_TYPE_TEXT }, { "svg:textPath", SP_TYPE_TEXTPATH }, diff --git a/src/sp-switch.cpp b/src/sp-switch.cpp new file mode 100644 index 000000000..64aa868cc --- /dev/null +++ b/src/sp-switch.cpp @@ -0,0 +1,182 @@ +#define __SP_SWITCH_CPP__ + +/* + * SVG implementation + * + * Authors: + * Andrius R. + * + * Copyright (C) 2006 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if defined(WIN32) || defined(__APPLE__) +# include +#endif + +#include "sp-switch.h" +#include "display/nr-arena-group.h" +#include "conditions.h" + +static void sp_switch_class_init (SPSwitchClass *klass); +static void sp_switch_init (SPSwitch *group); + +static SPGroupClass * parent_class; + +GType CSwitch::getType (void) +{ + static GType switch_type = 0; + if (!switch_type) { + GTypeInfo switch_info = { + sizeof (SPSwitchClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) sp_switch_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (SPSwitch), + 16, /* n_preallocs */ + (GInstanceInitFunc) sp_switch_init, + NULL, /* value_table */ + }; + switch_type = g_type_register_static (SP_TYPE_GROUP, "SPSwitch", &switch_info, (GTypeFlags)0); + } + return switch_type; +} + +static void +sp_switch_class_init (SPSwitchClass *) { + parent_class = (SPGroupClass *)g_type_class_ref (SP_TYPE_GROUP); +} + +static void sp_switch_init (SPSwitch *group) +{ + if (group->group) + delete group->group; + + group->group = new CSwitch(group); +} + +CSwitch::CSwitch(SPGroup *group) : CGroup(group), _cached_item(NULL), _release_handler_id(0) { +} + +CSwitch::~CSwitch() { + _releaseLastItem(_cached_item); +} + +SPObject *CSwitch::_evaluateFirst() { + for (SPObject *child = sp_object_first_child(_group) ; child != NULL ; child = SP_OBJECT_NEXT(child) ) { + if (sp_item_evaluate(SP_ITEM(child))) + return child; + } + return NULL; +} + +GSList *CSwitch::_childList(bool add_ref, Action action) { + if ( ActionGeneral != action ) { + return CGroup::_childList(add_ref, action); + } + + SPObject *child = _evaluateFirst(); + if (NULL == child) + return NULL; + + if (add_ref) + g_object_ref (G_OBJECT (child)); + + return g_slist_prepend (NULL, child); +} + +gchar *CSwitch::getDescription() { + gint len = getItemCount(); + return g_strdup_printf( + ngettext("Conditional group of %d object", + "Conditional group of %d objects", + len), len); +} + +void CSwitch::onChildAdded(Inkscape::XML::Node *) { + _reevaluate(true); +} + +void CSwitch::onChildRemoved(Inkscape::XML::Node *) { + _reevaluate(); +} + +void CSwitch::onOrderChanged (Inkscape::XML::Node *, Inkscape::XML::Node *, Inkscape::XML::Node *) +{ + _reevaluate(); +} + +void CSwitch::_reevaluate(bool add_to_arena) { + SPObject *evaluated_child = _evaluateFirst(); + if (!evaluated_child || _cached_item == evaluated_child) { + return; + } + + _releaseLastItem(_cached_item); + + SPItem * child; + for ( GSList *l = _childList(false, ActionShow); + NULL != l ; l = g_slist_remove (l, l->data)) + { + SPObject *o = SP_OBJECT (l->data); + if ( !SP_IS_ITEM (o) ) { + continue; + } + + child = SP_ITEM (o); + child->setEvaluated(o == evaluated_child); + } + + _cached_item = evaluated_child; + _release_handler_id = g_signal_connect( + G_OBJECT(evaluated_child), "release", + G_CALLBACK(&CSwitch::_releaseItem), + this); + + _group->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG); +} + +void CSwitch::_releaseItem(SPObject *obj, CSwitch *selection) +{ + selection->_releaseLastItem(obj); +} + +void CSwitch::_releaseLastItem(SPObject *obj) +{ + if (NULL == _cached_item || _cached_item != obj) + return; + + g_signal_handler_disconnect(G_OBJECT(_cached_item), _release_handler_id); + _release_handler_id = 0; + _cached_item = NULL; +} + +void CSwitch::_showChildren (NRArena *arena, NRArenaItem *ai, unsigned int key, unsigned int flags) { + SPObject *evaluated_child = _evaluateFirst(); + + NRArenaItem *ac = NULL; + NRArenaItem *ar = NULL; + SPItem * child; + GSList *l = _childList(false, ActionShow); + while (l) { + SPObject *o = SP_OBJECT (l->data); + if (SP_IS_ITEM (o)) { + child = SP_ITEM (o); + child->setEvaluated(o == evaluated_child); + ac = sp_item_invoke_show (child, arena, key, flags); + if (ac) { + nr_arena_item_add_child (ai, ac, ar); + ar = ac; + nr_arena_item_unref (ac); + } + } + l = g_slist_remove (l, o); + } +} diff --git a/src/sp-switch.h b/src/sp-switch.h new file mode 100644 index 000000000..84fc35298 --- /dev/null +++ b/src/sp-switch.h @@ -0,0 +1,61 @@ +#ifndef __SP_SWITCH_H__ +#define __SP_SWITCH_H__ + +/* + * SVG implementation + * + * Authors: + * Andrius R. + * + * Copyright (C) 2006 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "sp-item-group.h" + +#define SP_TYPE_SWITCH (CSwitch::getType()) +#define SP_SWITCH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SP_TYPE_SWITCH, SPSwitch)) +#define SP_SWITCH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_SWITCH, SPSwitchClass)) +#define SP_IS_SWITCH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SP_TYPE_SWITCH)) +#define SP_IS_SWITCH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SP_TYPE_SWITCH)) + +/* + * Virtual methods of SPSwitch + */ +class CSwitch : public CGroup { +public: + CSwitch(SPGroup *group); + virtual ~CSwitch(); + + friend class SPSwitch; + + static GType getType(); + + virtual void onChildAdded(Inkscape::XML::Node *child); + virtual void onChildRemoved(Inkscape::XML::Node *child); + virtual void onOrderChanged(Inkscape::XML::Node *child, Inkscape::XML::Node *old_ref, Inkscape::XML::Node *new_ref); + virtual gchar *getDescription(); + +protected: + virtual GSList *_childList(bool add_ref, Action action); + virtual void _showChildren (NRArena *arena, NRArenaItem *ai, unsigned int key, unsigned int flags); + + SPObject *_evaluateFirst(); + void _reevaluate(bool add_to_arena = false); + static void _releaseItem(SPObject *obj, CSwitch *selection); + void _releaseLastItem(SPObject *obj); + +private: + SPObject *_cached_item; + gulong _release_handler_id; +}; + +struct SPSwitch : public SPGroup { + void resetChildEvaluated() { ((CSwitch *)group)->_reevaluate(); } +}; + +struct SPSwitchClass : public SPGroupClass { +}; + +#endif -- 2.30.2