From 29ad4e90d149629a444e2b7aab6b48ca5a709d3e Mon Sep 17 00:00:00 2001 From: cyreve Date: Wed, 11 Jul 2007 21:44:47 +0000 Subject: [PATCH] bug 1243190: add tref element support; limited editing support thus far (patch by gbanaszk) --- src/Makefile_insert | 2 + src/desktop-style.cpp | 18 +- src/dialogs/find.cpp | 3 +- src/selection-chemistry.cpp | 29 +- src/sp-object-repr.cpp | 2 + src/sp-text.cpp | 7 + src/sp-tref-reference.cpp | 108 +++++++ src/sp-tref-reference.h | 75 +++++ src/sp-tref.cpp | 626 ++++++++++++++++++++++++++++++++++++ src/sp-tref.h | 80 +++++ src/sp-tspan.cpp | 9 +- src/text-editing.cpp | 67 +++- src/ui/dialog/find.cpp | 3 +- 13 files changed, 1007 insertions(+), 22 deletions(-) create mode 100644 src/sp-tref-reference.cpp create mode 100644 src/sp-tref-reference.h create mode 100644 src/sp-tref.cpp create mode 100644 src/sp-tref.h diff --git a/src/Makefile_insert b/src/Makefile_insert index d2ce0c193..989b0cb1d 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -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 \ diff --git a/src/desktop-style.cpp b/src/desktop-style.cpp index 1634db367..a6a4d8567 100644 --- a/src/desktop-style.cpp +++ b/src/desktop-style.cpp @@ -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" @@ -32,8 +30,11 @@ #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; diff --git a/src/dialogs/find.cpp b/src/dialogs/find.cpp index 257abb138..8a8204604 100644 --- a/src/dialogs/find.cpp +++ b/src/dialogs/find.cpp @@ -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! diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index ce66243cc..dd6ab6eb8 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -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); diff --git a/src/sp-object-repr.cpp b/src/sp-object-repr.cpp index 5a1554cbd..a4b47ca22 100644 --- a/src/sp-object-repr.cpp +++ b/src/sp-object-repr.cpp @@ -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 } }; diff --git a/src/sp-text.cpp b/src/sp-text.cpp index 5c2c5412c..d1105ac20 100644 --- a/src/sp-text.cpp +++ b/src/sp-text.cpp @@ -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 index 000000000..f3b6ef329 --- /dev/null +++ b/src/sp-tref-reference.cpp @@ -0,0 +1,108 @@ +/* + * The reference corresponding to href of 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 old_content, + Inkscape::Util::ptr_shared 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 old_value, + Inkscape::Util::ptr_shared 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 index 000000000..37a5e21ce --- /dev/null +++ b/src/sp-tref-reference.h @@ -0,0 +1,75 @@ +#ifndef SEEN_SP_TREF_REFERENCE_H +#define SEEN_SP_TREF_REFERENCE_H + +/* + * The reference corresponding to href of 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 +#include +#include + +#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 old_content, + Inkscape::Util::ptr_shared new_content); + virtual void notifyAttributeChanged(Inkscape::XML::Node &node, GQuark name, + Inkscape::Util::ptr_shared old_value, + Inkscape::Util::ptr_shared 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 index 000000000..d5479b6cd --- /dev/null +++ b/src/sp-tref.cpp @@ -0,0 +1,626 @@ +#define __SP_TREF_CPP__ + +/** \file + * SVG 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 + * + * 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 + +#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 : ""); + + 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(_("Cloned character data from: %s"), child_desc); + g_free(child_desc); + return ret; + } else { + return g_strdup(_("Orphaned cloned character data")); + } +} + + +/* 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 index 000000000..eea8207e3 --- /dev/null +++ b/src/sp-tref.h @@ -0,0 +1,80 @@ +#ifndef SP_TREF_H +#define SP_TREF_H + +/** \file + * SVG implementation, see sp-tref.cpp. + * + * This file was created based on skeleton.h + */ +/* + * Authors: + * Gail Banaszkiewicz + * + * 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 : diff --git a/src/sp-tspan.cpp b/src/sp-tspan.cpp index 0b8f97da1..c53a75d5d 100644 --- a/src/sp-tspan.cpp +++ b/src/sp-tspan.cpp @@ -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 diff --git a/src/text-editing.cpp b/src/text-editing.cpp index 233ac8d3f..bed837cef 100644 --- a/src/text-editing.cpp +++ b/src/text-editing.cpp @@ -14,7 +14,11 @@ # include "config.h" #endif +#include + #include "desktop.h" +#include "inkscape.h" +#include "message-stack.h" #include "style.h" #include "unit-constants.h" @@ -26,10 +30,13 @@ #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 cloned character data."); + 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 diff --git a/src/ui/dialog/find.cpp b/src/ui/dialog/find.cpp index 10f377ca8..22fa608bf 100644 --- a/src/ui/dialog/find.cpp +++ b/src/ui/dialog/find.cpp @@ -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! -- 2.30.2