Code

bug 1243190: add tref element support; limited editing support thus far (patch by...
authorcyreve <cyreve@users.sourceforge.net>
Wed, 11 Jul 2007 21:44:47 +0000 (21:44 +0000)
committercyreve <cyreve@users.sourceforge.net>
Wed, 11 Jul 2007 21:44:47 +0000 (21:44 +0000)
13 files changed:
src/Makefile_insert
src/desktop-style.cpp
src/dialogs/find.cpp
src/selection-chemistry.cpp
src/sp-object-repr.cpp
src/sp-text.cpp
src/sp-tref-reference.cpp [new file with mode: 0644]
src/sp-tref-reference.h [new file with mode: 0644]
src/sp-tref.cpp [new file with mode: 0644]
src/sp-tref.h [new file with mode: 0644]
src/sp-tspan.cpp
src/text-editing.cpp
src/ui/dialog/find.cpp

index d2ce0c1939cb0cfa5f21063f57d477971b40a98e..989b0cb1df3c8da07659a1212da1ac0317cb7404 100644 (file)
@@ -243,6 +243,8 @@ libinkpre_a_SOURCES =       \
        sp-switch.cpp sp-switch.h\
        sp-text.cpp sp-text.h   \
        sp-textpath.h   \
+       sp-tref-reference.cpp sp-tref-reference.h \
+       sp-tref.cpp sp-tref.h \
        sp-tspan.cpp sp-tspan.h \
        sp-use-reference.cpp sp-use-reference.h \
        sp-use.cpp sp-use.h     \
index 1634db367e4907fea28018e790c369ab2e53e4d9..a6a4d8567518399c31f87e2c39d42e9d67ed4792 100644 (file)
@@ -18,8 +18,6 @@
 #include "svg/svg.h"
 #include "svg/svg-color.h"
 #include "selection.h"
-#include "sp-tspan.h"
-#include "sp-textpath.h"
 #include "inkscape.h"
 #include "style.h"
 #include "prefs-utils.h"
 #include "sp-flowregion.h"
 #include "sp-flowdiv.h"
 #include "sp-linear-gradient.h"
-#include "sp-radial-gradient.h"
 #include "sp-pattern.h"
+#include "sp-radial-gradient.h"
+#include "sp-textpath.h"
+#include "sp-tref.h"
+#include "sp-tspan.h"
 #include "xml/repr.h"
 #include "libnrtype/font-style-to-pos.h"
 
@@ -427,7 +428,10 @@ objects_query_fillstroke (GSList *objects, SPStyle *style_res, bool const isfill
 
         // We consider paint "effectively set" for anything within text hierarchy
         SPObject *parent = SP_OBJECT_PARENT (obj);
-        bool paint_effectively_set = paint->set || (SP_IS_TEXT(parent) || SP_IS_TEXTPATH(parent) || SP_IS_TSPAN(parent) || SP_IS_FLOWTEXT(parent) || SP_IS_FLOWDIV(parent) || SP_IS_FLOWPARA(parent) || SP_IS_FLOWTSPAN(parent) || SP_IS_FLOWLINE(parent));
+        bool paint_effectively_set = 
+            paint->set || (SP_IS_TEXT(parent) || SP_IS_TEXTPATH(parent) || SP_IS_TSPAN(parent)
+            || SP_IS_FLOWTEXT(parent) || SP_IS_FLOWDIV(parent) || SP_IS_FLOWPARA(parent) 
+            || SP_IS_FLOWTSPAN(parent) || SP_IS_FLOWLINE(parent));
 
         // 1. Bail out with QUERY_STYLE_MULTIPLE_DIFFERENT if necessary
 
@@ -826,7 +830,7 @@ objects_query_fontnumbers (GSList *objects, SPStyle *style_res)
         SPObject *obj = SP_OBJECT (i->data);
 
         if (!SP_IS_TEXT(obj) && !SP_IS_FLOWTEXT(obj)
-            && !SP_IS_TSPAN(obj) && !SP_IS_TEXTPATH(obj)
+            && !SP_IS_TSPAN(obj) && !SP_IS_TREF(obj) && !SP_IS_TEXTPATH(obj)
             && !SP_IS_FLOWDIV(obj) && !SP_IS_FLOWPARA(obj) && !SP_IS_FLOWTSPAN(obj))
             continue;
 
@@ -919,7 +923,7 @@ objects_query_fontstyle (GSList *objects, SPStyle *style_res)
         SPObject *obj = SP_OBJECT (i->data);
 
         if (!SP_IS_TEXT(obj) && !SP_IS_FLOWTEXT(obj)
-            && !SP_IS_TSPAN(obj) && !SP_IS_TEXTPATH(obj)
+            && !SP_IS_TSPAN(obj) && !SP_IS_TREF(obj) && !SP_IS_TEXTPATH(obj)
             && !SP_IS_FLOWDIV(obj) && !SP_IS_FLOWPARA(obj) && !SP_IS_FLOWTSPAN(obj))
             continue;
 
@@ -974,7 +978,7 @@ objects_query_fontfamily (GSList *objects, SPStyle *style_res)
         SPObject *obj = SP_OBJECT (i->data);
 
         if (!SP_IS_TEXT(obj) && !SP_IS_FLOWTEXT(obj)
-            && !SP_IS_TSPAN(obj) && !SP_IS_TEXTPATH(obj)
+            && !SP_IS_TSPAN(obj) && !SP_IS_TREF(obj) && !SP_IS_TEXTPATH(obj)
             && !SP_IS_FLOWDIV(obj) && !SP_IS_FLOWPARA(obj) && !SP_IS_FLOWTSPAN(obj))
             continue;
 
index 257abb13815715b5fc5879f580685f33a0ffafea..8a82046040931f42b8dee477bfe6ee9c4ebf61f0 100644 (file)
@@ -51,6 +51,7 @@ sp_find_dialog(){
 #include "../sp-flowtext.h"
 #include "../text-editing.h"
 #include "../sp-tspan.h"
+#include "../sp-tref.h"
 #include "../selection-chemistry.h"
 #include "../sp-defs.h"
 #include "../sp-rect.h"
@@ -238,7 +239,7 @@ item_type_match (SPItem *item, GtkWidget *widget)
     } else if (SP_IS_PATH(item) || SP_IS_LINE(item) || SP_IS_POLYLINE(item)) {
         return (type_checkbox (widget, "paths"));
 
-    } else if (SP_IS_TEXT(item) || SP_IS_TSPAN(item) || SP_IS_STRING(item)) {
+    } else if (SP_IS_TEXT(item) || SP_IS_TSPAN(item) || SP_IS_TREF(item) || SP_IS_STRING(item)) {
         return (type_checkbox (widget, "texts"));
 
     } else if (SP_IS_GROUP(item) && !desktop->isLayer(item) ) { // never select layers!
index ce66243ccef8fe5a6dc46f3c7d9967d0eb8dced2..dd6ab6eb80a689d40350e9de84490c09a7ae51e6 100644 (file)
@@ -35,6 +35,7 @@
 #include "sp-use.h"
 #include "sp-textpath.h"
 #include "sp-tspan.h"
+#include "sp-tref.h"
 #include "sp-flowtext.h"
 #include "sp-flowregion.h"
 #include "text-editing.h"
@@ -993,7 +994,7 @@ take_style_from_item (SPItem *item)
             }
         }
     }
-    if (!(SP_IS_TEXT (item) || SP_IS_TSPAN (item) || SP_IS_STRING (item))) {
+    if (!(SP_IS_TEXT (item) || SP_IS_TSPAN (item) || SP_IS_TREF(item) || SP_IS_STRING (item))) {
         // do not copy text properties from non-text objects, it's confusing
         css = sp_css_attr_unset_text (css);
     }
@@ -1354,6 +1355,7 @@ bool
 selection_contains_original (SPItem *item, Inkscape::Selection *selection)
 {
     bool contains_original = false;
+    
     bool is_use = SP_IS_USE(item);
     SPItem *item_use = item;
     SPItem *item_use_first = item;
@@ -1364,7 +1366,14 @@ selection_contains_original (SPItem *item, Inkscape::Selection *selection)
         if (item_use == item_use_first)
             break;
         is_use = SP_IS_USE(item_use);
-    }   
+    }
+    
+    // If it's a tref, check whether the object containing the character
+    // data is part of the selection
+    if (!contains_original && SP_IS_TREF(item)) {
+        contains_original = selection->includes(SP_TREF(item)->getObjectReferredTo());
+    }
+       
     return contains_original;
 }
 
@@ -2111,15 +2120,21 @@ sp_selection_unlink()
          items != NULL;
          items = items->next)
     {
-        SPItem *use = (SPItem *) items->data;
+        SPItem *item = (SPItem *) items->data;
 
-        if (!SP_IS_USE(use)) {
-            // keep the non-yse item in the new selection
-            new_select = g_slist_prepend(new_select, use);
+        if (!(SP_IS_USE(item) || SP_IS_TREF(item))) {
+            // keep the non-use item in the new selection
+            new_select = g_slist_prepend(new_select, item);
             continue;
         }
 
-        SPItem *unlink = sp_use_unlink(SP_USE(use));
+        SPItem *unlink;
+        if (SP_IS_USE(item)) { 
+            unlink = sp_use_unlink(SP_USE(item));
+        } else /*if (SP_IS_TREF(use))*/ {
+            unlink = SP_ITEM(sp_tref_convert_to_tspan(SP_TREF(item)));
+        }
+        
         unlinked = true;
         // Add ungrouped items to the new selection.
         new_select = g_slist_prepend(new_select, unlink);
index 5a1554cbd1d59a14233e33068dac766f2f99856f..a4b47ca220c13c017567334e3593246ccd76e9aa 100644 (file)
@@ -31,6 +31,7 @@
 #include "sp-metadata.h"
 #include "sp-polyline.h"
 #include "sp-textpath.h"
+#include "sp-tref.h"
 #include "sp-tspan.h"
 #include "sp-pattern.h"
 #include "sp-clippath.h"
@@ -165,6 +166,7 @@ populate_dtables()
         { "svg:symbol", SP_TYPE_SYMBOL },
         { "svg:text", SP_TYPE_TEXT },
         { "svg:textPath", SP_TYPE_TEXTPATH },
+        { "svg:tref", SP_TYPE_TREF },
         { "svg:tspan", SP_TYPE_TSPAN },
         { "svg:use", SP_TYPE_USE }
     };
index 5c2c5412c3bd8e606876b70d6c8e6d3cd2dab666..d1105ac2065987e817fa3a709e2c3e17558ac516 100644 (file)
@@ -46,6 +46,7 @@
 #include "mod360.h"
 
 #include "sp-textpath.h"
+#include "sp-tref.h"
 #include "sp-tspan.h"
 
 #include "text-editing.h"
@@ -516,6 +517,9 @@ unsigned SPText::_buildLayoutInput(SPObject *root, Inkscape::Text::Layout::Optio
         bool use_xy = !in_textpath && (tspan->role == SP_TSPAN_ROLE_UNSPECIFIED || !tspan->attributes.singleXYCoordinates());
         tspan->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, use_xy, true);
     }
+    else if (SP_IS_TREF(root)) {
+        SP_TREF(root)->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, true, true);
+    }
     else if (SP_IS_TEXTPATH(root)) {
         in_textpath = true;
         SP_TEXTPATH(root)->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, false, true);
@@ -619,6 +623,9 @@ void SPText::_adjustCoordsRecursive(SPItem *item, NR::Matrix const &m, double ex
         SP_TEXT(item)->attributes.transform(m, ex, ex, is_root);
     else if (SP_IS_TEXTPATH(item))
         SP_TEXTPATH(item)->attributes.transform(m, ex, ex, is_root);
+    else if (SP_IS_TREF(item)) {
+        SP_TREF(item)->attributes.transform(m, ex, ex, is_root);
+    }
 
     for (SPObject *o = item->children; o != NULL; o = o->next) {
         if (SP_IS_ITEM(o))
diff --git a/src/sp-tref-reference.cpp b/src/sp-tref-reference.cpp
new file mode 100644 (file)
index 0000000..f3b6ef3
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * The reference corresponding to href of <tref> element.
+ *
+ * Copyright (C) 2007 Gail Banaszkiewicz
+ * 
+ * This file was created based on sp-use-reference.cpp
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information.
+ */
+
+//#include "enums.h"
+#include "sp-tref-reference.h"
+
+#include "sp-text.h"
+#include "sp-tref.h"
+#include "sp-tspan.h"
+
+
+
+bool SPTRefReference::_acceptObject(SPObject * const obj) const
+{
+    SPObject *owner = getOwner();
+    if (SP_IS_TREF(owner))
+        return sp_tref_reference_allowed(SP_TREF(getOwner()), obj);
+    else
+        return false;
+}
+
+
+void SPTRefReference::updateObserver()
+{
+    SPObject *referred = getObject();
+    
+    if (referred) {
+        if (subtreeObserved) {
+            subtreeObserved->removeObserver(*this);
+            delete subtreeObserved;   
+        }
+        
+        subtreeObserved = new Inkscape::XML::Subtree(*SP_OBJECT_REPR(referred));
+        subtreeObserved->addObserver(*this);
+    }
+}
+
+
+void SPTRefReference::notifyChildAdded(Inkscape::XML::Node &node, Inkscape::XML::Node &child,
+                                       Inkscape::XML::Node *prev)
+{
+    SPObject *owner = getOwner();
+    
+    if (owner && SP_IS_TREF(owner)) {
+        sp_tref_update_text(SP_TREF(owner));
+    }    
+}
+
+
+void SPTRefReference::notifyChildRemoved(Inkscape::XML::Node &node, Inkscape::XML::Node &child,
+                                         Inkscape::XML::Node *prev)
+{
+    SPObject *owner = getOwner();
+    
+    if (owner && SP_IS_TREF(owner)) {
+        sp_tref_update_text(SP_TREF(owner));
+    }
+}
+
+
+void SPTRefReference::notifyChildOrderChanged(Inkscape::XML::Node &node, Inkscape::XML::Node &child,
+                                              Inkscape::XML::Node *old_prev, Inkscape::XML::Node *new_prev)
+{
+    SPObject *owner = getOwner();
+    
+    if (owner && SP_IS_TREF(owner)) {
+        sp_tref_update_text(SP_TREF(owner));
+    }
+}
+                                        
+void SPTRefReference::notifyContentChanged(Inkscape::XML::Node &node,
+                                           Inkscape::Util::ptr_shared<char> old_content,
+                                           Inkscape::Util::ptr_shared<char> new_content)
+{
+    SPObject *owner = getOwner();
+    
+    if (owner && SP_IS_TREF(owner)) {
+        sp_tref_update_text(SP_TREF(owner));
+    }
+}
+
+
+void SPTRefReference::notifyAttributeChanged(Inkscape::XML::Node &node, GQuark name,
+                                             Inkscape::Util::ptr_shared<char> old_value,
+                                             Inkscape::Util::ptr_shared<char> new_value)
+{
+    // Do nothing - tref only cares about textual content
+}
+
+
+/*
+  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 :
diff --git a/src/sp-tref-reference.h b/src/sp-tref-reference.h
new file mode 100644 (file)
index 0000000..37a5e21
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef SEEN_SP_TREF_REFERENCE_H
+#define SEEN_SP_TREF_REFERENCE_H
+
+/*
+ * The reference corresponding to href of <tref> element.
+ * 
+ * This file was created based on sp-use-reference.h
+ *
+ * Copyright (C) 2007 Gail Banaszkiewicz
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information.
+ */
+
+#include <forward.h>
+#include <uri-references.h>
+#include <sigc++/sigc++.h>
+
+#include "util/share.h"
+#include "xml/node-observer.h"
+#include "xml/subtree.h"
+
+
+class SPTRefReference : public Inkscape::URIReference,
+                        public Inkscape::XML::NodeObserver {
+public:
+    SPTRefReference(SPObject *owner) : URIReference(owner), subtreeObserved(NULL) {
+        updateObserver();
+    }
+    
+    virtual ~SPTRefReference() {
+        if (subtreeObserved) {
+            subtreeObserved->removeObserver(*this);
+            delete subtreeObserved;
+        }   
+    }
+
+    SPItem *getObject() const {
+        return (SPItem *)URIReference::getObject();
+    }
+   
+    void updateObserver();
+    
+    /////////////////////////////////////////////////////////////////////
+    // Node Observer Functions
+    // -----------------------
+    virtual void notifyChildAdded(Inkscape::XML::Node &node, Inkscape::XML::Node &child, Inkscape::XML::Node *prev);
+    virtual void notifyChildRemoved(Inkscape::XML::Node &node, Inkscape::XML::Node &child, Inkscape::XML::Node *prev);
+    virtual void notifyChildOrderChanged(Inkscape::XML::Node &node, Inkscape::XML::Node &child,
+                                         Inkscape::XML::Node *old_prev, Inkscape::XML::Node *new_prev);
+    virtual void notifyContentChanged(Inkscape::XML::Node &node,
+                                      Inkscape::Util::ptr_shared<char> old_content,
+                                      Inkscape::Util::ptr_shared<char> new_content);
+    virtual void notifyAttributeChanged(Inkscape::XML::Node &node, GQuark name,
+                                        Inkscape::Util::ptr_shared<char> old_value,
+                                        Inkscape::Util::ptr_shared<char> new_value);
+    /////////////////////////////////////////////////////////////////////
+
+protected:
+    virtual bool _acceptObject(SPObject * obj) const; 
+    
+    Inkscape::XML::Subtree *subtreeObserved; 
+};
+
+#endif /* !SEEN_SP_TREF_REFERENCE_H */
+
+/*
+  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 :
diff --git a/src/sp-tref.cpp b/src/sp-tref.cpp
new file mode 100644 (file)
index 0000000..d5479b6
--- /dev/null
@@ -0,0 +1,626 @@
+#define __SP_TREF_CPP__
+
+/** \file
+ * SVG <tref> implementation - All character data within the referenced
+ * element, including character data enclosed within additional markup,
+ * will be rendered.
+ * 
+ * This file was created based on skeleton.cpp
+ */
+/*
+ * Authors:
+ *   Gail Banaszkiewicz <Gail.Banaszkiewicz@gmail.com>
+ *
+ * Copyright (C) 2007 Gail Banaszkiewicz
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <glibmm/i18n.h>
+
+#include "attributes.h"
+#include "document.h"
+#include "sp-object-repr.h"
+#include "sp-text.h"
+#include "sp-tspan.h"
+#include "sp-tref.h"
+#include "style.h"
+#include "text-editing.h"
+#include "uri.h"
+
+#include "display/nr-arena-group.h"
+#include "libnr/nr-matrix-fns.h"
+#include "xml/node.h"
+#include "xml/repr.h"
+
+
+//#define DEBUG_TREF
+#ifdef DEBUG_TREF
+# define debug(f, a...) { g_message("%s(%d) %s:", \
+                                  __FILE__,__LINE__,__FUNCTION__); \
+                          g_message(f, ## a); \
+                          g_message("\n"); \
+                        }
+#else
+# define debug(f, a...) /**/
+#endif
+
+
+static void build_string_from_root(Inkscape::XML::Node *root, Glib::ustring *retString);
+
+/* TRef base class */
+
+static void sp_tref_class_init(SPTRefClass *tref_class);
+static void sp_tref_init(SPTRef *tref);
+static void sp_tref_finalize(GObject *obj);
+
+static void sp_tref_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
+static void sp_tref_release(SPObject *object);
+static void sp_tref_set(SPObject *object, unsigned int key, gchar const *value);
+static void sp_tref_update(SPObject *object, SPCtx *ctx, guint flags);
+static void sp_tref_modified(SPObject *object, guint flags);
+static Inkscape::XML::Node *sp_tref_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
+
+static void sp_tref_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
+static gchar *sp_tref_description(SPItem *item);
+
+static void sp_tref_href_changed(SPObject *old_ref, SPObject *ref, SPTRef *tref);
+static void sp_tref_delete_self(SPObject *deleted, SPTRef *self);
+
+static SPObjectClass *tref_parent_class;
+
+GType
+sp_tref_get_type()
+{
+    static GType tref_type = 0;
+
+    if (!tref_type) {
+        GTypeInfo tref_info = {
+            sizeof(SPTRefClass),
+            NULL, NULL,
+            (GClassInitFunc) sp_tref_class_init,
+            NULL, NULL,
+            sizeof(SPTRef),
+            16,
+            (GInstanceInitFunc) sp_tref_init,
+            NULL,    /* value_table */
+        };
+        tref_type = g_type_register_static(SP_TYPE_ITEM, "SPTRef", &tref_info, (GTypeFlags)0);
+    }
+    return tref_type;
+}
+
+static void
+sp_tref_class_init(SPTRefClass *tref_class)
+{
+    GObjectClass *gobject_class = (GObjectClass *) tref_class;
+    SPObjectClass *sp_object_class = (SPObjectClass *)tref_class;
+
+    tref_parent_class = (SPObjectClass*)g_type_class_peek_parent(tref_class);
+
+    sp_object_class->build = sp_tref_build;
+    sp_object_class->release = sp_tref_release;
+    sp_object_class->write = sp_tref_write;
+    sp_object_class->set = sp_tref_set;
+    sp_object_class->update = sp_tref_update;
+    sp_object_class->modified = sp_tref_modified;
+    
+    gobject_class->finalize = sp_tref_finalize;
+    
+    SPItemClass *item_class = (SPItemClass *) tref_class;
+    
+    item_class->bbox = sp_tref_bbox;
+    item_class->description = sp_tref_description;
+}
+
+static void
+sp_tref_init(SPTRef *tref)
+{
+    new (&tref->attributes) TextTagAttributes;
+    
+    tref->href = NULL;
+    tref->uriOriginalRef = new SPTRefReference(SP_OBJECT(tref));
+    new (&tref->_delete_connection) sigc::connection();
+    new (&tref->_changed_connection) sigc::connection();
+    
+    tref->_changed_connection = 
+        tref->uriOriginalRef->changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_tref_href_changed), tref));
+}
+
+
+static void
+sp_tref_finalize(GObject *obj)
+{
+    SPTRef *tref = (SPTRef *) obj;
+
+    delete tref->uriOriginalRef;
+
+    tref->_delete_connection.~connection();
+    tref->_changed_connection.~connection();
+}
+
+
+/**
+ * Reads the Inkscape::XML::Node, and initializes SPTRef variables.
+ */
+static void
+sp_tref_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+{
+    if (((SPObjectClass *) tref_parent_class)->build) {
+        ((SPObjectClass *) tref_parent_class)->build(object, document, repr);
+    }
+
+    sp_object_read_attr(object, "xlink:href");
+    sp_object_read_attr(object, "x");
+    sp_object_read_attr(object, "y");
+    sp_object_read_attr(object, "dx");
+    sp_object_read_attr(object, "dy");
+    sp_object_read_attr(object, "rotate");
+}
+
+/**
+ * Drops any allocated memory.
+ */
+static void
+sp_tref_release(SPObject *object)
+{
+    SPTRef *tref = SP_TREF(object);
+    
+    tref->attributes.~TextTagAttributes();
+
+    tref->_delete_connection.disconnect();
+    tref->_changed_connection.disconnect();
+
+    g_free(tref->href);
+    tref->href = NULL;
+
+    tref->uriOriginalRef->detach();
+
+    if (((SPObjectClass *) tref_parent_class)->release)
+        ((SPObjectClass *) tref_parent_class)->release(object);
+}
+
+/**
+ * Sets a specific value in the SPTRef.
+ */
+static void
+sp_tref_set(SPObject *object, unsigned int key, gchar const *value)
+{
+    debug("0x%p %s(%u): '%s'",object,
+            sp_attribute_name(key),key,value ? value : "<no value>");
+            
+    SPTRef *tref = SP_TREF(object);
+    
+    if (tref->attributes.readSingleAttribute(key, value)) { // x, y, dx, dy, rotate
+        object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+    } else if (key == SP_ATTR_XLINK_HREF) { // xlink:href
+        if ( !value ) {
+            // No value
+            g_free(tref->href);
+            tref->href = NULL;
+            tref->uriOriginalRef->detach();
+        } else if ((tref->href && strcmp(value, tref->href) != 0) || (!tref->href)) {
+            
+            // Value has changed
+            
+            if ( tref->href ) {
+                g_free(tref->href);
+                tref->href = NULL;
+            }
+            
+            tref->href = g_strdup(value);
+            
+            try {
+                tref->uriOriginalRef->attach(Inkscape::URI(value));
+                tref->uriOriginalRef->updateObserver();
+            } catch ( Inkscape::BadURIException &e ) {
+                g_warning("%s", e.what());
+                tref->uriOriginalRef->detach();
+            }
+            
+            // No matter what happened, an update should be in order
+            SP_OBJECT(tref)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+        }
+    
+    } else { // default
+        if (((SPObjectClass *) tref_parent_class)->set) {
+            ((SPObjectClass *) tref_parent_class)->set(object, key, value);
+        }
+    }
+
+
+}
+
+/**
+ * Receives update notifications.  Code based on sp_use_update and sp_tspan_update.
+ */
+static void
+sp_tref_update(SPObject *object, SPCtx *ctx, guint flags)
+{
+    debug("0x%p",object);
+    
+    SPTRef *tref = SP_TREF(object);
+
+    if (((SPObjectClass *) tref_parent_class)->update) {
+        ((SPObjectClass *) tref_parent_class)->update(object, ctx, flags);
+    }
+
+    if (flags & SP_OBJECT_MODIFIED_FLAG) {
+        flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
+    }
+    
+    flags &= SP_OBJECT_MODIFIED_CASCADE;    
+
+    SPObject *child = tref->stringChild;
+    if (child) {
+        if ( flags || ( child->uflags & SP_OBJECT_MODIFIED_FLAG )) {
+            child->updateDisplay(ctx, flags);
+        }
+    }
+
+    
+}
+
+static void
+sp_tref_modified(SPObject *object, guint flags)
+{
+    SPTRef *tref_obj = SP_TREF(object);
+
+    if (flags & SP_OBJECT_MODIFIED_FLAG) {
+        flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
+    }
+    
+    flags &= SP_OBJECT_MODIFIED_CASCADE;
+
+    SPObject *child = tref_obj->stringChild;
+    if (child) {
+        g_object_ref(G_OBJECT(child));
+        if (flags || (child->mflags & SP_OBJECT_MODIFIED_FLAG)) {
+            child->emitModified(flags);
+        }
+        g_object_unref(G_OBJECT(child));
+    }
+}
+
+/**
+ * Writes its settings to an incoming repr object, if any.
+ */
+static Inkscape::XML::Node *
+sp_tref_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
+{
+    debug("0x%p",object);
+    
+    SPTRef *tref = SP_TREF(object);
+    
+    if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
+        Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
+        repr = xml_doc->createElement("svg:tref");
+    }
+    
+    tref->attributes.writeTo(repr);
+    
+    if (tref->uriOriginalRef->getURI()) {
+        gchar *uri_string = tref->uriOriginalRef->getURI()->toString();
+        debug("uri_string=%s", uri_string);
+        repr->setAttribute("xlink:href", uri_string);
+        g_free(uri_string);
+    }
+
+    if (((SPObjectClass *) tref_parent_class)->write) {
+        ((SPObjectClass *) tref_parent_class)->write(object, repr, flags);
+    }
+
+    return repr;
+}
+
+/**
+ *  The code for this function is swiped from the tspan bbox code, since tref should work pretty much the same way
+ */
+static void
+sp_tref_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags)
+{
+    // find out the ancestor text which holds our layout
+    SPObject *parent_text = SP_OBJECT(item);
+    for (; parent_text != NULL && !SP_IS_TEXT(parent_text); parent_text = SP_OBJECT_PARENT (parent_text));
+    if (parent_text == NULL) return;
+
+    // get the bbox of our portion of the layout
+    SP_TEXT(parent_text)->layout.getBoundingBox(
+        bbox, transform, sp_text_get_length_upto(parent_text, item), sp_text_get_length_upto(item, NULL) - 1);
+
+    // Add stroke width
+    SPStyle* style=SP_OBJECT_STYLE (item);
+    if (style->stroke.type != SP_PAINT_TYPE_NONE) {
+        double const scale = expansion(transform);
+        if ( fabs(style->stroke_width.computed * scale) > 0.01 ) { // sinon c'est 0=oon veut pas de bord
+            double const width = MAX(0.125, style->stroke_width.computed * scale);
+            if ( fabs(bbox->x1 - bbox->x0) > -0.00001 && fabs(bbox->y1 - bbox->y0) > -0.00001 ) {
+                bbox->x0-=0.5*width;
+                bbox->x1+=0.5*width;
+                bbox->y0-=0.5*width;
+                bbox->y1+=0.5*width;
+            }
+        }
+    }
+}
+
+
+static gchar *
+sp_tref_description(SPItem *item)
+{
+    SPTRef *tref = SP_TREF(item);
+
+    if (tref && tref->getObjectReferredTo()) {
+        char *child_desc = sp_item_description(SP_ITEM(tref->getObjectReferredTo()));
+        char *ret = g_strdup_printf(_("<b>Cloned character data</b> from: %s"), child_desc);
+        g_free(child_desc);
+        return ret;
+    } else {
+        return g_strdup(_("<b>Orphaned cloned character data</b>"));
+    }
+}
+
+
+/* For the sigc::connection changes (i.e. when the object being refered to changes) */
+static void
+sp_tref_href_changed(SPObject *old_ref, SPObject *ref, SPTRef *tref)
+{
+    if (tref)
+    {
+        // Save a pointer to the original object being referred to
+        SPObject *refRoot = tref->getObjectReferredTo();
+        
+        tref->_delete_connection.disconnect();
+        
+        if (tref->stringChild) {
+            sp_object_detach(SP_OBJECT(tref), tref->stringChild);
+            tref->stringChild = NULL;
+        }
+        
+        // Ensure that we are referring to a legitimate object
+        if (tref->href && refRoot && sp_tref_reference_allowed(tref, refRoot)) {
+        
+            // Update the text being referred to (will create a new string child)
+            sp_tref_update_text(tref);  
+            
+            // Restore the delete connection now that we're done messing with stuff
+            tref->_delete_connection = SP_OBJECT(refRoot)->connectDelete(sigc::bind(sigc::ptr_fun(&sp_tref_delete_self), tref));          
+        }
+            
+    }    
+}
+
+
+/**
+ * Delete the tref object
+ */
+static void
+sp_tref_delete_self(SPObject *deleted, SPTRef *self)
+{
+    SP_OBJECT(self)->deleteObject();
+}
+
+/**
+ * Return the object referred to via the URI reference
+ */
+SPObject * SPTRef::getObjectReferredTo(void)
+{
+    SPObject *referredObject = NULL;
+    
+    if (uriOriginalRef) {
+        referredObject = SP_OBJECT(uriOriginalRef->getObject());
+    }
+    
+    return referredObject;   
+}
+
+
+/**
+ * Returns true when the given tref is allowed to refer to a particular object
+ */
+bool
+sp_tref_reference_allowed(SPTRef *tref, SPObject *possible_ref)
+{
+    bool allowed = false;
+    
+    if (tref && possible_ref) {
+        if (tref != possible_ref) {
+            bool ancestor = false;
+            for (SPObject *obj = tref; obj; obj = SP_OBJECT_PARENT(obj)) {
+                if (possible_ref == obj) {
+                    ancestor = true;
+                    break;
+                }    
+            }
+            allowed = !ancestor;
+        }
+    }
+    
+    return allowed;
+}
+
+
+/**
+ * Returns true if a tref is fully contained in the confines of the given
+ * iterators and layout (or if there is no tref).
+ */
+bool
+sp_tref_fully_contained(SPObject *start_item, Glib::ustring::iterator &start, 
+                             SPObject *end_item, Glib::ustring::iterator &end)
+{
+    bool fully_contained = false;
+    
+    if (start_item && end_item) {
+        
+        // If neither the beginning or the end is a tref then we return true (whether there
+        // is a tref in the innards or not, because if there is one then it must be totally
+        // contained)
+        if (!(SP_IS_STRING(start_item) && SP_IS_TREF(SP_OBJECT_PARENT(start_item)))
+                && !(SP_IS_STRING(end_item) && SP_IS_TREF(SP_OBJECT_PARENT(end_item)))) {
+            fully_contained = true;
+        }
+        
+        // Both the beginning and end are trefs; but in this case, the string iterators
+        // must be at the right places
+        else if ((SP_IS_STRING(start_item) && SP_IS_TREF(SP_OBJECT_PARENT(start_item)))
+                && (SP_IS_STRING(end_item) && SP_IS_TREF(SP_OBJECT_PARENT(end_item)))) {
+            if (start == SP_STRING(start_item)->string.begin()
+                    && end == SP_STRING(start_item)->string.end()) {
+                fully_contained = true;
+            }
+        }
+        
+        // If the beginning is a string that is a child of a tref, the iterator has to be
+        // at the beginning of the item
+        else if ((SP_IS_STRING(start_item) && SP_IS_TREF(SP_OBJECT_PARENT(start_item)))
+                    && !(SP_IS_STRING(end_item) && SP_IS_TREF(SP_OBJECT_PARENT(end_item)))) {
+            if (start == SP_STRING(start_item)->string.begin()) {
+                fully_contained = true;
+            }
+        }
+        
+        // Same, but the for the end
+        else if (!(SP_IS_STRING(start_item) && SP_IS_TREF(SP_OBJECT_PARENT(start_item)))
+                    && (SP_IS_STRING(end_item) && SP_IS_TREF(SP_OBJECT_PARENT(end_item)))) {
+            if (end == SP_STRING(start_item)->string.end()) {
+                fully_contained = true;
+            }
+        }
+    }
+    
+    return fully_contained;
+}
+
+
+void
+sp_tref_update_text(SPTRef *tref)
+{
+    if (tref) {
+        // Get the character data that will be used with this tref
+        Glib::ustring charData = "";
+        build_string_from_root(SP_OBJECT_REPR(tref->getObjectReferredTo()), &charData);
+        
+        if (tref->stringChild) {
+            sp_object_detach(SP_OBJECT(tref), tref->stringChild);
+            tref->stringChild = NULL;
+        }
+        
+        // Create the node and SPString to be the tref's child
+        Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(tref));
+        
+        Inkscape::XML::Node *newStringRepr = xml_doc->createTextNode(charData.c_str());
+        tref->stringChild = SP_OBJECT(g_object_new(sp_repr_type_lookup(newStringRepr), NULL));
+        
+        // Add this SPString as a child of the tref
+        sp_object_attach(SP_OBJECT(tref), tref->stringChild, tref->lastChild());
+        sp_object_unref(tref->stringChild, NULL);
+        sp_object_invoke_build(tref->stringChild, SP_OBJECT(tref)->document, newStringRepr, TRUE);
+        
+        Inkscape::GC::release(newStringRepr);
+    }
+}
+
+
+
+/**
+ * Using depth-first search, build up a string by concatenating all SPStrings
+ * found in the tree starting at the root
+ */
+static void
+build_string_from_root(Inkscape::XML::Node *root, Glib::ustring *retString)
+{
+    if (root && retString) {
+        
+        // Stop and concatenate when a SPString is found
+        if (root->type() == Inkscape::XML::TEXT_NODE) {
+            *retString += (root->content());
+            
+            debug("%s", retString->c_str());
+        
+        // Otherwise, continue searching down the tree (with the assumption that no children nodes
+        // of a SPString are actually legal)
+        } else {
+            Inkscape::XML::Node *childNode;
+            for (childNode = root->firstChild(); childNode; childNode = childNode->next()) {
+                build_string_from_root(childNode, retString);
+            }
+        }
+    } 
+}
+
+/**
+ * This function will create a new tspan element with the same attributes as
+ * the tref had and add the same text as a child.  The tref is replaced in the
+ * tree with the new tspan.
+ * The code is based partially on sp_use_unlink
+ */
+SPObject *
+sp_tref_convert_to_tspan(SPTRef *tref)
+{
+    SPObject * new_tspan = NULL;
+    
+    if (tref && tref->stringChild) {
+        Inkscape::XML::Node *tref_repr = SP_OBJECT_REPR(tref);
+        Inkscape::XML::Node *tref_parent = sp_repr_parent(tref_repr);
+        
+        SPDocument *document = SP_OBJECT(tref)->document;
+        Inkscape::XML::Document *xml_doc = sp_document_repr_doc(document);
+        
+        Inkscape::XML::Node *new_tspan_repr = xml_doc->createElement("svg:tspan");
+        
+        // Add the new tspan element just after the current tref
+        tref_parent->addChild(new_tspan_repr, tref_repr);
+        Inkscape::GC::release(new_tspan_repr);
+        
+        new_tspan = document->getObjectByRepr(new_tspan_repr);
+        
+        // Create a new string child for the tspan
+        Inkscape::XML::Node *new_string_repr = SP_OBJECT_REPR(tref->stringChild)->duplicate(xml_doc);
+        new_tspan_repr->addChild(new_string_repr, NULL);    
+        
+        SPObject * new_string_child = document->getObjectByRepr(new_string_repr);
+
+        // Merge style from the tref
+        SPStyle *new_tspan_sty = SP_OBJECT_STYLE(new_tspan);
+        SPStyle const *tref_sty = SP_OBJECT_STYLE(tref);
+        sp_style_merge_from_dying_parent(new_tspan_sty, tref_sty);
+        sp_style_merge_from_parent(new_tspan_sty, new_tspan->parent->style);
+        
+        
+        SP_OBJECT(new_tspan)->updateRepr();
+        
+        // Hold onto our SPObject and repr for now.
+        sp_object_ref(SP_OBJECT(tref), NULL);
+        Inkscape::GC::anchor(tref_repr);
+        
+        // Remove ourselves, not propagating delete events to avoid a
+        // chain-reaction with other elements that might reference us.
+        SP_OBJECT(tref)->deleteObject(false);
+        
+        // Give the copy our old id and let go of our old repr.
+        new_tspan_repr->setAttribute("id", tref_repr->attribute("id"));
+        Inkscape::GC::release(tref_repr);
+        
+        // Establish the succession and let go of our object.
+        SP_OBJECT(tref)->setSuccessor(new_tspan);
+        sp_object_unref(SP_OBJECT(tref), NULL);
+    }
+    
+    return new_tspan;
+}
+
+
+/*
+  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/sp-tref.h b/src/sp-tref.h
new file mode 100644 (file)
index 0000000..eea8207
--- /dev/null
@@ -0,0 +1,80 @@
+#ifndef SP_TREF_H
+#define SP_TREF_H
+
+/** \file
+ * SVG <tref> implementation, see sp-tref.cpp.
+ * 
+ * This file was created based on skeleton.h
+ */
+/*
+ * Authors:
+ *   Gail Banaszkiewicz <Gail.Banaszkiewicz@gmail.com>
+ *
+ * Copyright (C) 2007 Gail Banaszkiewicz
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "sp-item.h"
+#include "sp-tref-reference.h"
+#include "text-tag-attributes.h"
+
+
+/* tref base class */
+
+#define SP_TYPE_TREF (sp_tref_get_type())
+#define SP_TREF(o) (G_TYPE_CHECK_INSTANCE_CAST((o), SP_TYPE_TREF, SPTRef))
+#define SP_TREF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SP_TYPE_TREF, SPTSpanClass))
+#define SP_IS_TREF(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), SP_TYPE_TREF))
+#define SP_IS_TREF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_TREF))
+
+class SPTRef;
+class SPTRef;
+
+struct SPTRef : public SPItem {
+    // Attributes that are used in the same way they would be in a tspan
+    TextTagAttributes attributes;
+    
+    // Text stored in the xlink:href attribute
+    gchar *href;
+    
+    // URI reference to original object
+    SPTRefReference *uriOriginalRef;
+    
+    // Shortcut pointer to the child of the tref (which is a copy
+    // of the character data stored at and/or below the node
+    // referenced by uriOriginalRef)
+    SPObject *stringChild;
+    
+    // The sigc connections for various notifications
+    sigc::connection _delete_connection;
+    sigc::connection _changed_connection;
+    
+    SPObject * getObjectReferredTo();
+};
+
+struct SPTRefClass {
+    SPItemClass parent_class;
+};
+
+GType sp_tref_get_type();
+
+void sp_tref_update_text(SPTRef *tref);
+bool sp_tref_reference_allowed(SPTRef *tref, SPObject *possible_ref);
+bool sp_tref_fully_contained(SPObject *start_item, Glib::ustring::iterator &start, 
+                             SPObject *end_item, Glib::ustring::iterator &end);
+SPObject * sp_tref_convert_to_tspan(SPTRef *item);
+
+
+#endif /* !SP_TREF_H */
+
+/*
+  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 :
index 0b8f97da1318207feee59b9e78049e69097c9d07..c53a75d5dd1defd06866919e903fe2cca344ec2d 100644 (file)
@@ -32,6 +32,7 @@
 #include "attributes.h"
 #include "sp-use-reference.h"
 #include "sp-tspan.h"
+#include "sp-tref.h"
 #include "sp-textpath.h"
 #include "text-editing.h"
 #include "style.h"
@@ -239,7 +240,7 @@ sp_tspan_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
         GSList *l = NULL;
         for (SPObject* child = sp_object_first_child(object) ; child != NULL ; child = SP_OBJECT_NEXT(child) ) {
             Inkscape::XML::Node* c_repr=NULL;
-            if ( SP_IS_TSPAN(child) ) {
+            if ( SP_IS_TSPAN(child) || SP_IS_TREF(child) ) {
                 c_repr = child->updateRepr(NULL, flags);
             } else if ( SP_IS_TEXTPATH(child) ) {
                 //c_repr = child->updateRepr(NULL, flags); // shouldn't happen
@@ -255,7 +256,7 @@ sp_tspan_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
         }
     } else {
         for (SPObject* child = sp_object_first_child(object) ; child != NULL ; child = SP_OBJECT_NEXT(child) ) {
-            if ( SP_IS_TSPAN(child) ) {
+            if ( SP_IS_TSPAN(child) || SP_IS_TREF(child) ) {
                 child->updateRepr(flags);
             } else if ( SP_IS_TEXTPATH(child) ) {
                 //c_repr = child->updateRepr(NULL, flags); // shouldn't happen
@@ -515,7 +516,7 @@ sp_textpath_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
         GSList *l = NULL;
         for (SPObject* child = sp_object_first_child(object) ; child != NULL ; child = SP_OBJECT_NEXT(child) ) {
             Inkscape::XML::Node* c_repr=NULL;
-            if ( SP_IS_TSPAN(child) ) {
+            if ( SP_IS_TSPAN(child) || SP_IS_TREF(child) ) {
                 c_repr = child->updateRepr(NULL, flags);
             } else if ( SP_IS_TEXTPATH(child) ) {
                 //c_repr = child->updateRepr(NULL, flags); // shouldn't happen
@@ -531,7 +532,7 @@ sp_textpath_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
         }
     } else {
         for (SPObject* child = sp_object_first_child(object) ; child != NULL ; child = SP_OBJECT_NEXT(child) ) {
-            if ( SP_IS_TSPAN(child) ) {
+            if ( SP_IS_TSPAN(child) || SP_IS_TREF(child) ) {
                 child->updateRepr(flags);
             } else if ( SP_IS_TEXTPATH(child) ) {
                 //c_repr = child->updateRepr(NULL, flags); // shouldn't happen
index 233ac8d3fe764f98cefb2ff1808541a31c2bc8f7..bed837cefabf2ff65903734a16ad15c3e5bd8e56 100644 (file)
 # include "config.h"
 #endif
 
+#include <glibmm/i18n.h>
+
 #include "desktop.h"
+#include "inkscape.h"
+#include "message-stack.h"
 #include "style.h"
 #include "unit-constants.h"
 
 #include "sp-flowtext.h"
 #include "sp-flowdiv.h"
 #include "sp-flowregion.h"
+#include "sp-tref.h"
 #include "sp-tspan.h"
 
 #include "text-editing.h"
 
+static const gchar *tref_edit_message = _("You cannot edit <b>cloned character data</b>.");
+
 static bool tidy_xml_tree_recursively(SPObject *root);
 
 Inkscape::Text::Layout const * te_get_layout (SPItem const *item)
@@ -151,13 +158,15 @@ static bool is_line_break_object(SPObject const *object)
 }
 
 /** returns the attributes for an object, or NULL if it isn't a text,
-tspan or textpath. */
+tspan, tref, or textpath. */
 static TextTagAttributes* attributes_for_object(SPObject *object)
 {
     if (SP_IS_TSPAN(object))
         return &SP_TSPAN(object)->attributes;
     if (SP_IS_TEXT(object))
         return &SP_TEXT(object)->attributes;
+    if (SP_IS_TREF(object))
+        return &SP_TREF(object)->attributes;
     if (SP_IS_TEXTPATH(object))
         return &SP_TEXTPATH(object)->attributes;
     return NULL;
@@ -177,7 +186,9 @@ unsigned sp_text_get_length(SPObject const *item)
     unsigned length = 0;
 
     if (SP_IS_STRING(item)) return SP_STRING(item)->string.length();
+    
     if (is_line_break_object(item)) length++;
+    
     for (SPObject const *child = item->firstChild() ; child ; child = SP_OBJECT_NEXT(child)) {
         if (SP_IS_STRING(child)) length += SP_STRING(child)->string.length();
         else length += sp_text_get_length(child);
@@ -191,15 +202,20 @@ unsigned sp_text_get_length_upto(SPObject const *item, SPObject const *upto)
 {
     unsigned length = 0;
 
+    // The string is the lowest level and the length can be counted directly. 
     if (SP_IS_STRING(item)) {
         return SP_STRING(item)->string.length();
     }
+    
+    // Take care of new lines...
     if (is_line_break_object(item) && !SP_IS_TEXT(item)) {
         if (item != SP_OBJECT_PARENT(item)->firstChild()) {
             // add 1 for each newline
             length++;
         }
     }
+    
+    // Count the length of the children
     for (SPObject const *child = item->firstChild() ; child ; child = SP_OBJECT_NEXT(child)) {
         if (upto && child == upto) {
             // hit upto, return immediately
@@ -320,6 +336,8 @@ Inkscape::Text::Layout::iterator sp_te_insert_line (SPItem *item, Inkscape::Text
     // texpaths attached to the same path, with a vertical shift
     if (SP_IS_TEXT_TEXTPATH (item))
         return position;
+        
+    SPDesktop *desktop = SP_ACTIVE_DESKTOP; 
 
     Inkscape::Text::Layout const *layout = te_get_layout(item);
     SPObject *split_obj = 0;
@@ -339,6 +357,12 @@ Inkscape::Text::Layout::iterator sp_te_insert_line (SPItem *item, Inkscape::Text
             Inkscape::GC::release(new_node);
         }
     } else if (SP_IS_STRING(split_obj)) {
+        // If the parent is a tref, editing on this particular string is disallowed.
+        if (SP_IS_TREF(SP_OBJECT_PARENT(split_obj))) {
+            desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, tref_edit_message);
+            return position;
+        }
+        
         Glib::ustring *string = &SP_STRING(split_obj)->string;
         unsigned char_index = 0;
         for (Glib::ustring::iterator it = string->begin() ; it != split_text_iter ; it++)
@@ -408,6 +432,8 @@ sp_te_insert(SPItem *item, Inkscape::Text::Layout::iterator const &position, gch
         g_warning("Trying to insert invalid utf8");
         return position;
     }
+    
+    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
 
     Inkscape::Text::Layout const *layout = te_get_layout(item);
     SPObject *source_obj = 0;
@@ -421,7 +447,13 @@ sp_te_insert(SPItem *item, Inkscape::Text::Layout::iterator const &position, gch
     layout->getSourceOfCharacter(it_prev_char, &rawptr, &iter_text);
     source_obj = SP_OBJECT(rawptr);
     if (SP_IS_STRING(source_obj)) {
-        // the simple case
+        // If the parent is a tref, editing on this particular string is disallowed.
+        if (SP_IS_TREF(SP_OBJECT_PARENT(source_obj))) {
+            desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, tref_edit_message);
+            return position;
+        }
+        
+        // Now the simple case can begin...
         if (!cursor_at_start) iter_text++;
         SPString *string_item = SP_STRING(source_obj);
         insert_into_spstring(string_item, cursor_at_end ? string_item->string.end() : iter_text, utf8);
@@ -457,6 +489,12 @@ sp_te_insert(SPItem *item, Inkscape::Text::Layout::iterator const &position, gch
                 g_assert(SP_IS_STRING(source_obj->firstChild()));
                 string_item = SP_STRING(source_obj->firstChild());
             }
+            // If the parent is a tref, editing on this particular string is disallowed.
+            if (SP_IS_TREF(SP_OBJECT_PARENT(string_item))) {
+                desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, tref_edit_message);
+                return position;
+            }
+            
             insert_into_spstring(string_item, cursor_at_end ? string_item->string.end() : string_item->string.begin(), utf8);
         }
     }
@@ -633,6 +671,9 @@ sp_te_delete (SPItem *item, Inkscape::Text::Layout::iterator const &start, Inksc
         first = end;
         last = start;
     }
+    
+    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+    
     Inkscape::Text::Layout const *layout = te_get_layout(item);
     SPObject *start_item = 0, *end_item = 0;
     void *rawptr = 0;
@@ -657,6 +698,12 @@ sp_te_delete (SPItem *item, Inkscape::Text::Layout::iterator const &start, Inksc
     if (start_item == end_item) {
         // the quick case where we're deleting stuff all from the same string
         if (SP_IS_STRING(start_item)) {     // always true (if it_start != it_end anyway)
+            // If the parent is a tref, editing on this particular string is disallowed.
+            if (SP_IS_TREF(SP_OBJECT_PARENT(start_item))) {
+                desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, tref_edit_message);
+                return end;
+            }
+            
             erase_from_spstring(SP_STRING(start_item), start_text_iter, end_text_iter);
         }
     } else {
@@ -665,6 +712,12 @@ sp_te_delete (SPItem *item, Inkscape::Text::Layout::iterator const &start, Inksc
         while (sub_item != item) {
             if (sub_item == end_item) {
                 if (SP_IS_STRING(sub_item)) {
+                    // If the parent is a tref, editing on this particular string is disallowed.
+                    if (SP_IS_TREF(SP_OBJECT_PARENT(sub_item))) {
+                        desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, tref_edit_message);
+                        return end;
+                    }
+            
                     Glib::ustring *string = &SP_STRING(sub_item)->string;
                     erase_from_spstring(SP_STRING(sub_item), string->begin(), end_text_iter);
                 }
@@ -1683,6 +1736,16 @@ void sp_te_apply_style(SPItem *text, Inkscape::Text::Layout::iterator const &sta
     if (is_line_break_object(end_item))
         end_item = SP_OBJECT_NEXT(end_item);
     if (end_item == 0) end_item = text;
+    
+    
+    /* Special case: With a tref, we only want to change its style when the whole
+     * string is selected, in which case the style can be applied directly to the
+     * tref node.  If only part of the tref's string child is selected, just return. */
+     
+    if (!sp_tref_fully_contained(start_item, start_text_iter, end_item, end_text_iter)) {
+        
+        return;
+    } 
 
     /* stage 1: applying the style. Go up to the closest common ancestor of
     start and end and then semi-recursively apply the style to all the
index 10f377ca86f88ea8e7896d5fcacee3efa6c6118a..22fa608bf25d684b38f9643c4c8f27a359ace07d 100644 (file)
@@ -35,6 +35,7 @@
 #include "sp-flowtext.h"
 #include "text-editing.h"
 #include "sp-tspan.h"
+#include "sp-tref.h"
 #include "selection-chemistry.h"
 #include "sp-defs.h"
 #include "sp-rect.h"
@@ -292,7 +293,7 @@ Find::item_type_match (SPItem *item)
     } else if (SP_IS_PATH(item) || SP_IS_LINE(item) || SP_IS_POLYLINE(item)) {
         return (_check_paths.get_active());
 
-    } else if (SP_IS_TEXT(item) || SP_IS_TSPAN(item) || SP_IS_STRING(item)) {
+    } else if (SP_IS_TEXT(item) || SP_IS_TSPAN(item) || SP_IS_TREF(item) || SP_IS_STRING(item)) {
         return (_check_texts.get_active());
 
     } else if (SP_IS_GROUP(item) && !desktop->isLayer(item) ) { // never select layers!