Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
sp-tref.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
9/*
10 * Authors:
11 * Gail Banaszkiewicz <Gail.Banaszkiewicz@gmail.com>
12 * Jon A. Cruz <jon@joncruz.org>
13 * Abhishek Sharma
14 *
15 * Copyright (C) 2007 Gail Banaszkiewicz
16 *
17 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
18 */
19
20#include "sp-tref.h"
21
22#include <glibmm/i18n.h>
23
24#include "bad-uri-exception.h"
25#include "attributes.h"
26#include "document.h"
27#include "sp-factory.h"
28#include "sp-text.h"
29#include "style.h"
30#include "text-editing.h"
32
33//#define DEBUG_TREF
34#ifdef DEBUG_TREF
35# define debug(f, a...) { g_message("%s(%d) %s:", \
36 __FILE__,__LINE__,__FUNCTION__); \
37 g_message(f, ## a); \
38 g_message("\n"); \
39 }
40#else
41# define debug(f, a...)
42#endif
43
44
45static void build_string_from_root(Inkscape::XML::Node *root, Glib::ustring *retString);
46
47/* TRef base class */
48static void sp_tref_href_changed(SPObject *old_ref, SPObject *ref, SPTRef *tref);
49static void sp_tref_delete_self(SPObject *deleted, SPTRef *self);
50
52 : SPItem()
53 , href(nullptr)
54 , uriOriginalRef(this)
55 , stringChild(nullptr)
56{
57 _changed_connection = uriOriginalRef.changedSignal().connect(sigc::bind(sigc::ptr_fun(sp_tref_href_changed), this));
58}
59
63
74
76 //this->attributes.~TextTagAttributes();
77
78 this->_delete_connection.disconnect();
79 this->_changed_connection.disconnect();
80
81 g_free(href);
82 href = nullptr;
83
85
87}
88
89void SPTRef::set(SPAttr key, const gchar* value) {
90 debug("0x%p %s(%u): '%s'",this,
91 sp_attribute_name(key),key,value ? value : "<no value>");
92
93 if (this->attributes.readSingleAttribute(key, value, style, &viewport)) { // x, y, dx, dy, rotate
94 this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
95 } else if (key == SPAttr::XLINK_HREF) { // xlink:href
96 if ( !value ) {
97 // No value
98 g_free(href);
99 href = nullptr;
101 } else if ((this->href && strcmp(value, this->href) != 0) || (!this->href)) {
102 // Value has changed
103
104 if ( this->href ) {
105 g_free(this->href);
106 this->href = nullptr;
107 }
108
109 href = g_strdup(value);
110
111 try {
114 } catch (Inkscape::BadURIException const &e) {
115 g_warning("%s", e.what());
117 }
118
119 // No matter what happened, an update should be in order
120 requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
121 }
122 } else { // default
123 SPItem::set(key, value);
124 }
125}
126
127void SPTRef::update(SPCtx *ctx, guint flags) {
128 debug("0x%p",this);
129
130 unsigned childflags = flags;
131 if (flags & SP_OBJECT_MODIFIED_FLAG) {
132 childflags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
133 }
134 childflags &= SP_OBJECT_MODIFIED_CASCADE;
135
136 SPObject *child = this->stringChild;
137
138 if (child) {
139 if ( childflags || ( child->uflags & SP_OBJECT_MODIFIED_FLAG )) {
140 child->updateDisplay(ctx, childflags);
141 }
142 }
143
144 SPItem::update(ctx, flags);
145}
146
147void SPTRef::modified(unsigned int flags) {
148 if (flags & SP_OBJECT_MODIFIED_FLAG) {
149 flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
150 }
151
152 flags &= SP_OBJECT_MODIFIED_CASCADE;
153
154 SPObject *child = this->stringChild;
155
156 if (child) {
158
159 if (flags || (child->mflags & SP_OBJECT_MODIFIED_FLAG)) {
160 child->emitModified(flags);
161 }
162
164 }
165}
166
168 debug("0x%p",this);
169
170 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
171 repr = xml_doc->createElement("svg:tref");
172 }
173
174 this->attributes.writeTo(repr);
175
176 if (uriOriginalRef.getURI()) {
177 auto uri = uriOriginalRef.getURI()->str();
178 auto uri_string = uri.c_str();
179 debug("uri_string=%s", uri_string);
180 Inkscape::setHrefAttribute(*repr, uri_string);
181 }
182
183 SPItem::write(xml_doc, repr, flags);
184
185 return repr;
186}
187
190 // find out the ancestor text which holds our layout
191 SPObject const *parent_text = this;
192
193 while ( parent_text && !is<SPText>(parent_text) ) {
194 parent_text = parent_text->parent;
195 }
196
197 if (parent_text == nullptr) {
198 return bbox;
199 }
200
201 // get the bbox of our portion of the layout
202 return cast<SPText>(parent_text)->layout.bounds(transform,
203 type == SPItem::VISUAL_BBOX,
204 sp_text_get_length_upto(parent_text, this),
205 sp_text_get_length_upto(this, nullptr) - 1);
206}
207
208const char* SPTRef::typeName() const {
209 return "text-data";
210}
211
212const char* SPTRef::displayName() const {
213 return _("Cloned Character Data");
214}
215
216gchar* SPTRef::description() const {
217 SPObject const *referred = this->getObjectReferredTo();
218
219 if (referred) {
220 char *child_desc;
221
222 if (is<SPItem>(referred)) {
223 child_desc = cast<SPItem>(referred)->detailedDescription();
224 } else {
225 child_desc = g_strdup("");
226 }
227
228 char *ret = g_strdup_printf("%s%s",
229 (is<SPItem>(referred) ? _(" from ") : ""), child_desc);
230 g_free(child_desc);
231
232 return ret;
233 }
234
235 return g_strdup(_("[orphaned]"));
236}
237
238
239/* For the sigc::connection changes (i.e. when the object being referred to changes) */
240static void
241sp_tref_href_changed(SPObject */*old_ref*/, SPObject */*ref*/, SPTRef *tref)
242{
243 if (tref)
244 {
245 // Save a pointer to the original object being referred to
246 SPObject *refRoot = tref->getObjectReferredTo();
247
248 tref->_delete_connection.disconnect();
249
250 if (tref->stringChild) {
251 tref->detach(tref->stringChild);
252 tref->stringChild = nullptr;
253 }
254
255 // Ensure that we are referring to a legitimate object
256 if (tref->href && refRoot && sp_tref_reference_allowed(tref, refRoot)) {
257
258 // Update the text being referred to (will create a new string child)
260
261 // Restore the delete connection now that we're done messing with stuff
262 tref->_delete_connection = refRoot->connectDelete(sigc::bind(sigc::ptr_fun(&sp_tref_delete_self), tref));
263 }
264
265 }
266}
267
268
272static void
274{
275 self->deleteObject();
276}
277
285
290{
291 return uriOriginalRef.getObject();
292}
293
297bool
299{
300 bool allowed = false;
301
302 if (tref && possible_ref) {
303 if (tref != possible_ref) {
304 bool ancestor = false;
305 for (SPObject *obj = tref; obj; obj = obj->parent) {
306 if (possible_ref == obj) {
307 ancestor = true;
308 break;
309 }
310 }
311 allowed = !ancestor;
312 }
313 }
314
315 return allowed;
316}
317
318
323bool
324sp_tref_fully_contained(SPObject *start_item, Glib::ustring::iterator &start,
325 SPObject *end_item, Glib::ustring::iterator &end)
326{
327 bool fully_contained = false;
328
329 if (start_item && end_item) {
330
331 // If neither the beginning or the end is a tref then we return true (whether there
332 // is a tref in the innards or not, because if there is one then it must be totally
333 // contained)
334 if (!(is<SPString>(start_item) && is<SPTRef>(start_item->parent))
335 && !(is<SPString>(end_item) && is<SPTRef>(end_item->parent))) {
336 fully_contained = true;
337 }
338
339 // Both the beginning and end are trefs; but in this case, the string iterators
340 // must be at the right places
341 else if ((is<SPString>(start_item) && is<SPTRef>(start_item->parent))
342 && (is<SPString>(end_item) && is<SPTRef>(end_item->parent))) {
343 if (start == cast<SPString>(start_item)->string.begin()
344 && end == cast<SPString>(start_item)->string.end()) {
345 fully_contained = true;
346 }
347 }
348
349 // If the beginning is a string that is a child of a tref, the iterator has to be
350 // at the beginning of the item
351 else if ((is<SPString>(start_item) && is<SPTRef>(start_item->parent))
352 && !(is<SPString>(end_item) && is<SPTRef>(end_item->parent))) {
353 if (start == cast<SPString>(start_item)->string.begin()) {
354 fully_contained = true;
355 }
356 }
357
358 // Same, but the for the end
359 else if (!(is<SPString>(start_item) && is<SPTRef>(start_item->parent))
360 && (is<SPString>(end_item) && is<SPTRef>(end_item->parent))) {
361 if (end == cast<SPString>(start_item)->string.end()) {
362 fully_contained = true;
363 }
364 }
365 }
366
367 return fully_contained;
368}
369
370
372{
373 if (tref) {
374 // Get the character data that will be used with this tref
375 Glib::ustring charData = "";
377
378 if (tref->stringChild) {
379 tref->detach(tref->stringChild);
380 tref->stringChild = nullptr;
381 }
382
383 // Create the node and SPString to be the tref's child
384 Inkscape::XML::Document *xml_doc = tref->document->getReprDoc();
385
386 Inkscape::XML::Node *newStringRepr = xml_doc->createTextNode(charData.c_str());
388
389 // Add this SPString as a child of the tref
390 tref->attach(tref->stringChild, tref->lastChild());
391 sp_object_unref(tref->stringChild, nullptr);
392 (tref->stringChild)->invoke_build(tref->document, newStringRepr, FALSE);
393
394 Inkscape::GC::release(newStringRepr);
395 }
396}
397
398
399
404static void
406{
407 if (root && retString) {
408
409 // Stop and concatenate when a SPString is found
411 *retString += (root->content());
412
413 debug("%s", retString->c_str());
414
415 // Otherwise, continue searching down the tree (with the assumption that no children nodes
416 // of a SPString are actually legal)
417 } else {
418 Inkscape::XML::Node *childNode;
419 for (childNode = root->firstChild(); childNode; childNode = childNode->next()) {
420 build_string_from_root(childNode, retString);
421 }
422 }
423 }
424}
425
432SPObject *
434{
435 SPObject * new_tspan = nullptr;
436
438 // BASE CASE
440 if (is<SPTRef>(obj)) {
441
442 auto tref = cast<SPTRef>(obj);
443
444 if (tref && tref->stringChild) {
445 Inkscape::XML::Node *tref_repr = tref->getRepr();
446 Inkscape::XML::Node *tref_parent = tref_repr->parent();
447
448 SPDocument *document = tref->document;
449 Inkscape::XML::Document *xml_doc = document->getReprDoc();
450
451 Inkscape::XML::Node *new_tspan_repr = xml_doc->createElement("svg:tspan");
452
453 // Add the new tspan element just after the current tref
454 tref_parent->addChild(new_tspan_repr, tref_repr);
455 Inkscape::GC::release(new_tspan_repr);
456
457 new_tspan = document->getObjectByRepr(new_tspan_repr);
458
459 // Create a new string child for the tspan
460 Inkscape::XML::Node *new_string_repr = tref->stringChild->getRepr()->duplicate(xml_doc);
461 new_tspan_repr->addChild(new_string_repr, nullptr);
462
463 //SPObject * new_string_child = document->getObjectByRepr(new_string_repr);
464
465 // Merge style from the tref
466 new_tspan->style->merge( tref->style );
467 new_tspan->style->cascade( new_tspan->parent->style );
468 new_tspan->updateRepr();
469
470 // Hold onto our SPObject and repr for now.
471 sp_object_ref(tref);
472 Inkscape::GC::anchor(tref_repr);
473
474 // Remove ourselves, not propagating delete events to avoid a
475 // chain-reaction with other elements that might reference us.
476 tref->deleteObject(false);
477
478 // Give the copy our old id and let go of our old repr.
479 new_tspan_repr->setAttribute("id", tref_repr->attribute("id"));
480 Inkscape::GC::release(tref_repr);
481
482 // Establish the succession and let go of our object.
483 tref->setSuccessor(new_tspan);
484 sp_object_unref(tref);
485 }
486 }
488 // RECURSIVE CASE
490 else {
491 std::vector<SPObject *> l;
492 for (auto& child: obj->children) {
493 sp_object_ref(&child, obj);
494 l.push_back(&child);
495 }
496 for(auto child:l) {
497 // Note that there may be more than one conversion happening here, so if it's not a
498 // tref being passed into this function, the returned value can't be specifically known
499 new_tspan = sp_tref_convert_to_tspan(child);
500
502 }
503 }
504
505 return new_tspan;
506}
507
508
509/*
510 Local Variables:
511 mode:c++
512 c-file-style:"stroustrup"
513 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
514 indent-tabs-mode:nil
515 fill-column:99
516 End:
517*/
518// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
gchar const * sp_attribute_name(SPAttr id)
Get attribute name by id.
Lookup dictionary for attributes/properties.
SPAttr
Definition attributes.h:27
@ XLINK_HREF
TODO: insert short description here.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Axis-aligned rectangle that can be empty.
Definition rect.h:203
URI const * getURI() const
Returns a pointer to a URI containing the currently attached URI, or NULL if no URI is currently atta...
void detach()
Detaches from the currently attached URI target, if any; the current referrent is signaled as NULL.
sigc::signal< void(SPObject *, SPObject *)> changedSignal()
Accessor for the referrent change notification signal; this signal is emitted whenever the URIReferen...
void attach(URI const &uri)
Attaches to a URI, relative to the specified document.
Represents an URI as per RFC 2396.
Definition uri.h:36
std::string str(char const *baseuri=nullptr) const
Return the string representation of this URI.
Definition uri.cpp:281
Interface for refcounted XML nodes.
Definition node.h:80
virtual Node * parent()=0
Get the parent of this node.
virtual Node * next()=0
Get the next sibling of this node.
virtual void addChild(Node *child, Node *after)=0
Insert another node as a child of this node.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual Node * duplicate(Document *doc) const =0
Create a duplicate of this node.
virtual Node * firstChild()=0
Get the first child of this node.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
Typed SVG document implementation.
Definition document.h:101
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:211
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
Base class for visual SVG elements.
Definition sp-item.h:109
void update(SPCtx *ctx, unsigned int flags) override
Definition sp-item.cpp:779
Inkscape::XML::Node * write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override
Definition sp-item.cpp:850
void set(SPAttr key, char const *value) override
Definition sp-item.cpp:558
void release() override
Definition sp-item.cpp:534
Geom::Affine transform
Definition sp-item.h:138
Geom::Rect viewport
Definition sp-item.h:140
@ VISUAL_BBOX
Definition sp-item.h:118
void build(SPDocument *document, Inkscape::XML::Node *repr) override
Definition sp-item.cpp:511
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
Inkscape::XML::Node * repr
Definition sp-object.h:193
void detach(SPObject *object)
Remove object from parent's children, release and unref it.
sigc::connection connectDelete(sigc::slot< void(SPObject *)> slot)
Connects a slot to be called when an object is deleted.
Definition sp-object.h:545
SPObject * lastChild()
Definition sp-object.h:318
void attach(SPObject *object, SPObject *prev)
Put object into object tree, under parent, and behind prev; also update object's XML space.
SPDocument * document
Definition sp-object.h:188
void updateDisplay(SPCtx *ctx, unsigned int flags)
Updates the object's display immediately.
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
SPObject * parent
Definition sp-object.h:189
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
void readAttr(char const *key)
Read value of key attribute from XML node into object.
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
ChildrenList children
Definition sp-object.h:907
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
void merge(SPStyle const *parent)
Combine style and parent style specifications into a single style specification that preserves (as mu...
Definition style.cpp:843
void cascade(SPStyle const *parent)
Sets computed values in style, which may involve inheriting from (or in some other way calculating fr...
Definition style.cpp:817
SPItem * getObject() const
const char * displayName() const override
The item's type name as a translated human string.
Definition sp-tref.cpp:212
SPTRefReference uriOriginalRef
Definition sp-tref.h:38
SPObject * getObjectReferredTo()
Return the object referred to via the URI reference.
Definition sp-tref.cpp:281
void build(SPDocument *doc, Inkscape::XML::Node *repr) override
Definition sp-tref.cpp:64
TextTagAttributes attributes
Definition sp-tref.h:32
sigc::connection _changed_connection
Definition sp-tref.h:47
~SPTRef() override
Definition sp-tref.cpp:60
char * description() const override
Definition sp-tref.cpp:216
Geom::OptRect bbox(Geom::Affine const &transform, SPItem::BBoxType type) const override
Definition sp-tref.cpp:188
void release() override
Definition sp-tref.cpp:75
Inkscape::XML::Node * write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags) override
Definition sp-tref.cpp:167
SPObject * stringChild
Definition sp-tref.h:43
void modified(unsigned int flags) override
Definition sp-tref.cpp:147
char * href
Definition sp-tref.h:35
SPTRef()
Definition sp-tref.cpp:51
const char * typeName() const override
The item's type name, not node tag name.
Definition sp-tref.cpp:208
sigc::connection _delete_connection
Definition sp-tref.h:46
void set(SPAttr key, char const *value) override
Definition sp-tref.cpp:89
void update(SPCtx *ctx, unsigned int flags) override
Definition sp-tref.cpp:127
bool readSingleAttribute(SPAttr key, gchar const *value, SPStyle const *style, Geom::Rect const *viewport)
Process the parameters from the set() function of SPObject.
Definition sp-text.cpp:1349
void writeTo(Inkscape::XML::Node *node) const
Write out all the contents of attributes to the given node.
Definition sp-text.cpp:1393
RootCluster root
Geom::Point start
Geom::Point end
static R & anchor(R &r)
Increments the reference count of a anchored object.
Definition gc-anchored.h:92
static R & release(R &r)
Decrements the reference count of a anchored object.
@ TEXT_NODE
Text node, e.g. "Some text" in <group>Some text</group> is represented by a text node.
void setHrefAttribute(XML::Node &node, Util::const_char_ptr value)
If the 'href' attribute already exists for the given node, then set a new value for it.
static cairo_user_data_key_t key
Ocnode * child[8]
Definition quantize.cpp:33
Ocnode ** ref
Definition quantize.cpp:32
SPObject * sp_object_unref(SPObject *object, SPObject *owner)
Decrease reference count of object, with possible debugging and finalization.
SPObject * sp_object_ref(SPObject *object, SPObject *owner)
Increase reference count of object, with possible debugging.
bool sp_tref_fully_contained(SPObject *start_item, Glib::ustring::iterator &start, SPObject *end_item, Glib::ustring::iterator &end)
Returns true if a tref is fully contained in the confines of the given iterators and layout (or if th...
Definition sp-tref.cpp:324
static void build_string_from_root(Inkscape::XML::Node *root, Glib::ustring *retString)
Using depth-first search, build up a string by concatenating all SPStrings found in the tree starting...
Definition sp-tref.cpp:405
bool sp_tref_reference_allowed(SPTRef *tref, SPObject *possible_ref)
Returns true when the given tref is allowed to refer to a particular object.
Definition sp-tref.cpp:298
void sp_tref_update_text(SPTRef *tref)
Definition sp-tref.cpp:371
static void sp_tref_href_changed(SPObject *old_ref, SPObject *ref, SPTRef *tref)
Definition sp-tref.cpp:241
SPObject * sp_tref_convert_to_tspan(SPObject *obj)
This function will create a new tspan element with the same attributes as the tref had and add the sa...
Definition sp-tref.cpp:433
static void sp_tref_delete_self(SPObject *deleted, SPTRef *self)
Delete the tref object.
Definition sp-tref.cpp:273
SVG <tref> implementation, see sp-tref.cpp.
Interface for XML documents.
Definition document.h:43
virtual Node * createTextNode(char const *content)=0
virtual Node * createElement(char const *name)=0
static std::string get_type_string(Inkscape::XML::Node const &node)
Unused.
Definition sp-object.h:94
static SPObject * createObject(std::string const &id)
SPStyle - a style object for SPItem objects.
unsigned sp_text_get_length_upto(SPObject const *item, SPObject const *upto)
Recursively gets the length of all the SPStrings at or below the given item, before and not including...