Code

SVG 1.1 Conditional Processing Module rendering support (<switch> element, requiredRe...
authorknutux <knutux@users.sourceforge.net>
Thu, 20 Apr 2006 16:06:59 +0000 (16:06 +0000)
committerknutux <knutux@users.sourceforge.net>
Thu, 20 Apr 2006 16:06:59 +0000 (16:06 +0000)
Two more W3C SVG Test Suite testes pass after this change.

15 files changed:
src/Makefile_insert
src/attributes.cpp
src/attributes.h
src/conditions.cpp [new file with mode: 0644]
src/conditions.h [new file with mode: 0644]
src/display/sp-canvas.cpp
src/document.cpp
src/document.h
src/sp-item-group.cpp
src/sp-item-group.h
src/sp-item.cpp
src/sp-item.h
src/sp-object-repr.cpp
src/sp-switch.cpp [new file with mode: 0644]
src/sp-switch.h [new file with mode: 0644]

index a9c565af0b4353607f9cb8ed56fae0e43a7579b3..12b766bb3e01dafb878c17ed9fc865cd802ceadf 100644 (file)
@@ -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 \
index 39163c010f812a79671134f64fa9757bd7d0da25..541acfc0cfda10c5752e77a823001e270fe489dd 100644 (file)
@@ -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]))
index 897656845779ab8a356efba6c144005a3e63d08d..bcbed50d0f45aedb3d4bf96189cc8dbe128f44ca 100644 (file)
@@ -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 (file)
index 0000000..e5a1d47
--- /dev/null
@@ -0,0 +1,462 @@
+#define __SP_CONDITIONS_CPP__
+
+/*
+ * SVG conditional attribute evaluation
+ *
+ * Authors:
+ *   Andrius R. <knutux@gmail.com>
+ *
+ * Copyright (C) 2006 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <set>
+#include <glibmm/ustring.h>
+#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<Glib::ustring> 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<Glib::ustring> splitByWhitespace(gchar const *value) {
+    std::vector<Glib::ustring> 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<Glib::ustring> 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 (file)
index 0000000..35a20ae
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef CONDITIONS_H_
+#define CONDITIONS_H_
+
+#include "sp-item.h"
+
+bool sp_item_evaluate(SPItem const *item);
+
+#endif /*CONDITIONS_H_*/
index 5e19539f37bf9293e0116798259b5a75d71ca9c7..edd429f0d10b2a40cd4a2dc7aefef865172eaf68 100644 (file)
@@ -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;
index 863266c051321f49d663f4be8e16409fcdb9514c..ef04735ef6252f78aeaf95c658eb0e04abbfe617 100644 (file)
@@ -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
index f06ad2f6d9ab86fc770cb8aa46a8827b2c98e74f..f601b15ff168a705f614ef9715a5daab62c03cea 100644 (file)
@@ -26,6 +26,7 @@
 #include "gc-managed.h"
 #include "gc-finalized.h"
 #include "gc-anchored.h"
+#include <glibmm/ustring.h>
 
 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();
 
index b4b69e1e488db966d4183af78709242897dff0d2..3ce551b983199f763841b7acdaaa622dbfa0be80 100644 (file)
@@ -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<unsigned int, SPGroup::LayerMode>();
 }
 
@@ -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("<b>Group</b> of <b>%d</b> object",
-                                "<b>Group</b> of <b>%d</b> 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("<b>Group</b> of <b>%d</b> object",
+                 "<b>Group</b> of <b>%d</b> 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);
+}
index 578454c9237e6591ad55fa7389c8168ab6cc8d47..ebf5c040272fe4b21a7490a8250e8b93e74db6d9 100644 (file)
@@ -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);
index 0e45bc7f43df067a961ba81326effdf2cc51ddcd..c2e3bb1bfcf24b76f5e4805b40843c8cd0338918 100644 (file)
 #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<void, SPObject *, SPObject *> cs1=item->clip_ref->changedSignal();
-                       sigc::slot2<void,SPObject*, SPObject *> sl1=sigc::bind(sigc::ptr_fun(clip_ref_changed), item);
+                       sigc::signal<void, SPObject *, SPObject *> cs1=this->clip_ref->changedSignal();
+                       sigc::slot2<void,SPObject*, SPObject *> sl1=sigc::bind(sigc::ptr_fun(clip_ref_changed), this);
                        cs1.connect(sl1);
                }
 
-    item->mask_ref = new SPMaskReference(SP_OBJECT(item));
-               sigc::signal<void, SPObject *, SPObject *> cs2=item->mask_ref->changedSignal();
-               sigc::slot2<void,SPObject*, SPObject *> sl2=sigc::bind(sigc::ptr_fun(mask_ref_changed), item);
+    this->mask_ref = new SPMaskReference(this);
+               sigc::signal<void, SPObject *, SPObject *> cs2=this->mask_ref->changedSignal();
+               sigc::slot2<void,SPObject*, SPObject *> 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<void, NR::Matrix const *, SPItem *>();
+    new (&this->_transformed_signal) sigc::signal<void, NR::Matrix const *, SPItem *>();
 }
 
 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);
 }
 
index 141d3f88119d0dd5e774243adf615e28036f5529..cb34817e53bed9dcfe52b323a62ace2a7b1fc8a9 100644 (file)
@@ -107,12 +107,17 @@ struct SPItem : public SPObject {
     std::vector<SPGuideConstraint> constraints;
     
     sigc::signal<void, NR::Matrix const *, SPItem *> _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<void, NR::Matrix const *, SPItem *> 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<std::vector<NR::Point> > SnapPointsIter;
index 3987dbcbb99aa54edd291845317aec98aa1cd6ff..0f2ff9e6a5f7439c318b85459116e726c2cd0ddc 100644 (file)
@@ -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 (file)
index 0000000..64aa868
--- /dev/null
@@ -0,0 +1,182 @@
+#define __SP_SWITCH_CPP__
+
+/*
+ * SVG <switch> implementation
+ *
+ * Authors:
+ *   Andrius R. <knutux@gmail.com>
+ *
+ * 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 <glibmm/i18n.h>
+#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("<b>Conditional group</b> of <b>%d</b> object",
+                 "<b>Conditional group</b> of <b>%d</b> 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 (file)
index 0000000..84fc352
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef __SP_SWITCH_H__
+#define __SP_SWITCH_H__
+
+/*
+ * SVG <switch> implementation
+ *
+ * Authors:
+ *   Andrius R. <knutux@gmail.com>
+ *
+ * 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