Code

switch SPStyle to using SPFilterReference for filters; sp_style_new now requires...
[inkscape.git] / src / uri-references.cpp
1 #define __SP_URI_REFERENCES_C__
3 /*
4  * Helper methods for resolving URI References
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *
9  * Copyright (C) 2001-2002 Lauris Kaplinski
10  * Copyright (C) 2001 Ximian, Inc.
11  *
12  * Released under GNU GPL, read the file 'COPYING' for more information
13  */
15 #include "document.h"
16 #include "sp-object.h"
17 #include "uri.h"
18 #include "uri-references.h"
20 #include <sigc++/functors/mem_fun.h>
22 static gchar *uri_to_id(SPDocument *document, const gchar *uri);
24 namespace Inkscape {
26 URIReference::URIReference(SPObject *owner)
27         : _owner(owner), _owner_document(NULL), _obj(NULL), _uri(NULL)
28 {
29         g_assert(_owner != NULL);
30         /* FIXME !!! attach to owner's destroy signal to clean up in case */
31 }
33 URIReference::URIReference(SPDocument *owner_document)
34         : _owner_document(owner_document), _owner(NULL), _obj(NULL), _uri(NULL)
35 {
36         g_assert(_owner_document != NULL);
37 }
39 URIReference::~URIReference() {
40         detach();
41 }
43 void URIReference::attach(const URI &uri) throw(BadURIException)
44 {
45         SPDocument *document;
46   if (_owner) {
47     document = SP_OBJECT_DOCUMENT(_owner);
48         } else if (_owner_document) {
49     document = _owner_document;
50         } else {
51     g_assert_not_reached();
52         }
53         gchar const *fragment = uri.getFragment();
54         if ( !uri.isRelative() || uri.getQuery() || !fragment ) {
55                 throw UnsupportedURIException();
56         }
58         /* FIXME !!! real xpointer support should be delegated to document */
59         /* for now this handles the minimal xpointer form that SVG 1.0
60          * requires of us
61          */
62         gchar *id;
63         if (!strncmp(fragment, "xpointer(", 9)) {
64                 /* FIXME !!! this is wasteful */
65                 /* FIXME: It looks as though this is including "))" in the id.  I suggest moving
66                    the strlen calculation and validity testing to before strdup, and copying just
67                    the id without the "))".  -- pjrm */
68                 if (!strncmp(fragment, "xpointer(id(", 12)) {
69                         id = g_strdup(fragment+12);
70                         size_t const len = strlen(id);
71                         if ( len < 3 || strcmp(id+len-2, "))") ) {
72                                 g_free(id);
73                                 throw MalformedURIException();
74                         }
75                 } else {
76                         throw UnsupportedURIException();
77                 }
78         } else {
79                 id = g_strdup(fragment);
80         }
82         /* FIXME !!! validate id as an NCName somewhere */
84         if (_uri) {
85                 delete _uri;
86         }
87         _uri = new URI(uri);
89         _connection.disconnect();
90         _setObject(document->getObjectById(id));
91         _connection = document->connectIdChanged(id, sigc::mem_fun(*this, &URIReference::_setObject));
93         g_free(id);
94 }
96 void URIReference::detach() {
97         _connection.disconnect();
98         delete _uri;
99         _uri = NULL;
100         _setObject(NULL);
103 void URIReference::_setObject(SPObject *obj) {
104         if ( obj && !_acceptObject(obj) ) {
105                 obj = NULL;
106         }
108         if ( obj == _obj ) return;
110         SPObject *old_obj=_obj;
111         _obj = obj;
113         _release_connection.disconnect();
114         if (_obj) {
115                 sp_object_href(_obj, _owner);
116                 _release_connection = _obj->connectRelease(sigc::mem_fun(*this, &URIReference::_release));
117         }
118         _changed_signal.emit(old_obj, _obj);
119         if (old_obj) {
120                 /* release the old object _after_ the signal emission */
121                 sp_object_hunref(old_obj, _owner);
122         }
125 /* If an object is deleted, current semantics require that we release
126  * it on its "release" signal, rather than later, when its ID is actually
127  * unregistered from the document.
128  */
129 void URIReference::_release(SPObject *obj) {
130         g_assert( _obj == obj );
131         _setObject(NULL);
134 } /* namespace Inkscape */
136 static gchar *
137 uri_to_id(SPDocument *document, const gchar *uri)
139         const gchar *e;
140         gchar *id;
141         gint len;
143         g_return_val_if_fail (document != NULL, NULL);
145         if (!uri) return NULL;
146         /* fixme: xpointer, everything */
147         if (strncmp (uri, "url(#", 5)) return NULL;
149         e = uri + 5;
150         while (*e) {
151                 if (*e == ')') break;
152                 if (!isalnum (*e) && (*e != '_') && (*e != '-') && (*e != ':') && (*e != '.')) return NULL;
153                 e += 1;
154                 if (!*e) return NULL;
155         }
157         len = e - uri - 5;
158         if (len < 1) return NULL;
160         id = (gchar*)g_new(gchar, len + 1);
161         memcpy (id, uri + 5, len);
162         id[len] = '\0';
164         return id;
167 SPObject *
168 sp_uri_reference_resolve (SPDocument *document, const gchar *uri)
170         gchar *id;
172         id = uri_to_id(document, uri);
173         if (!id) return NULL;
175         SPObject *ref;
176         ref = document->getObjectById(id);
177         g_free(id);
178         return ref;