summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 8fcf996)
raw | patch | inline | side by side (parent: 8fcf996)
author | sasilver <sasilver@users.sourceforge.net> | |
Tue, 24 Jun 2008 10:49:28 +0000 (10:49 +0000) | ||
committer | sasilver <sasilver@users.sourceforge.net> | |
Tue, 24 Jun 2008 10:49:28 +0000 (10:49 +0000) |
src/CMakeLists.txt | patch | blob | history | |
src/Makefile_insert | patch | blob | history | |
src/file.cpp | patch | blob | history | |
src/id-clash.cpp | [new file with mode: 0644] | patch | blob |
src/id-clash.h | [new file with mode: 0644] | patch | blob |
src/ui/clipboard.cpp | patch | blob | history |
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5dfd490c5bae95188c0a74b290804642bb0725a5..23ac82fb60d692419cd90a2912b120c00caef404 100644 (file)
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
gradient-drag.cpp
guide-snapper.cpp
help.cpp
+id-clash.cpp
ige-mac-menu.c
ink-action.cpp
inkscape.cpp
diff --git a/src/Makefile_insert b/src/Makefile_insert
index 033d2781079f064e230d32c77df68da67c2aacc4..f60e482e6fe0c9a9efedb9dfa3e95038fb032a32 100644 (file)
--- a/src/Makefile_insert
+++ b/src/Makefile_insert
help.cpp help.h \
helper-fns.h \
icon-size.h \
+ id-clash.cpp id-clash.h \
ige-mac-menu.h ige-mac-menu.c \
inkscape-stock.cpp inkscape-stock.h\
inkscape.cpp inkscape.h inkscape-private.h \
diff --git a/src/file.cpp b/src/file.cpp
index cdd76941fc8a615ba9de41f84eb5efa8b96cd58d..633dc08ceea7f38dfc887d29696a7a23124bb439 100644 (file)
--- a/src/file.cpp
+++ b/src/file.cpp
#include "application/editor.h"
#include "inkscape.h"
#include "uri.h"
-#include "extract-uri.h"
+#include "id-clash.h"
#ifdef WITH_GNOME_VFS
# include <libgnomevfs/gnome-vfs.h>
@@ -873,206 +873,6 @@ sp_file_save_a_copy(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*d
## I M P O R T
######################*/
-typedef enum { REF_HREF, REF_STYLE, REF_URL } ID_REF_TYPE;
-
-struct IdReference {
- ID_REF_TYPE type;
- SPObject *elem;
- const char *attr; // property or href-like attribute
-};
-
-typedef std::map<std::string, std::list<IdReference> > refmap_type;
-
-typedef std::pair<SPObject*, std::string> id_changeitem_type;
-typedef std::list<id_changeitem_type> id_changelist_type;
-
-const char *href_like_attributes[] = {
- "inkscape:href",
- "inkscape:path-effect",
- "inkscape:perspectiveID",
- "inkscape:tiled-clone-of",
- "xlink:href",
-};
-#define NUM_HREF_LIKE_ATTRIBUTES (sizeof(href_like_attributes) / sizeof(*href_like_attributes))
-
-const SPIPaint SPStyle::* SPIPaint_members[] = {
- &SPStyle::color,
- &SPStyle::fill,
- &SPStyle::stroke,
-};
-const char* SPIPaint_properties[] = {
- "color",
- "fill",
- "stroke",
-};
-#define NUM_SPIPAINT_PROPERTIES (sizeof(SPIPaint_properties) / sizeof(*SPIPaint_properties))
-
-const char* other_url_properties[] = {
- "clip-path",
- "color-profile",
- "cursor",
- "marker-end",
- "marker-mid",
- "marker-start",
- "mask",
-};
-#define NUM_OTHER_URL_PROPERTIES (sizeof(other_url_properties) / sizeof(*other_url_properties))
-
-/**
- * Build a table of places where ids are referenced, for a given element.
- * FIXME: There are some types of references not yet dealt with here
- * (e.g., ID selectors in CSS stylesheets).
- */
-static void
-find_references(SPObject *elem, refmap_type *refmap)
-{
- Inkscape::XML::Node *repr_elem = SP_OBJECT_REPR(elem);
- SPStyle *style = SP_OBJECT_STYLE(elem);
-
- /* check for xlink:href="#..." and similar */
- for (unsigned i = 0; i < NUM_HREF_LIKE_ATTRIBUTES; ++i) {
- const char *attr = href_like_attributes[i];
- const gchar *val = repr_elem->attribute(attr);
- if (val && val[0] == '#') {
- std::string id(val+1);
- IdReference idref = { REF_HREF, elem, attr };
- (*refmap)[id].push_back(idref);
- }
- }
-
- /* check for url(#...) references in 'fill' or 'stroke' */
- for (unsigned i = 0; i < NUM_SPIPAINT_PROPERTIES; ++i) {
- const SPIPaint SPStyle::*prop = SPIPaint_members[i];
- const SPIPaint *paint = &(style->*prop);
- if (paint->isPaintserver()) {
- const gchar *id = SP_OBJECT_ID(paint->value.href->getObject());
- IdReference idref = { REF_STYLE, elem, SPIPaint_properties[i] };
- (*refmap)[id].push_back(idref);
- }
- }
-
- /* check for url(#...) references in 'filter' */
- const SPIFilter *filter = &(style->filter);
- if (filter->href) {
- const gchar *id = SP_OBJECT_ID(filter->href->getObject());
- IdReference idref = { REF_STYLE, elem, "filter" };
- (*refmap)[id].push_back(idref);
- }
-
- /* check for other url(#...) references */
- for (unsigned i = 0; i < NUM_OTHER_URL_PROPERTIES; ++i) {
- const char *attr = other_url_properties[i];
- const gchar *value = repr_elem->attribute(attr);
- if (value) {
- const gchar *uri = extract_uri(value);
- if (uri && uri[0] == '#') {
- IdReference idref = { REF_URL, elem, attr };
- (*refmap)[uri+1].push_back(idref);
- }
- }
- }
-
- /* recurse */
- for (SPObject *child = sp_object_first_child(elem);
- child; child = SP_OBJECT_NEXT(child) )
- {
- find_references(child, refmap);
- }
-}
-
-/**
- * Change any ids that clash with ids in the current document, and make
- * a list of those changes that will require fixing up references.
- */
-static void
-change_clashing_ids(SPDocument *imported_doc, SPDocument *current_doc,
- SPObject *elem, const refmap_type *refmap,
- id_changelist_type *id_changes)
-{
- const gchar *id = SP_OBJECT_ID(elem);
-
- if (id && current_doc->getObjectById(id)) {
- // Choose a new id.
- // To try to preserve any meaningfulness that the original id
- // may have had, the new id is the old id followed by a hyphen
- // and one or more digits.
- std::string old_id(id);
- std::string new_id(old_id + '-');
- for (;;) {
- new_id += "0123456789"[std::rand() % 10];
- const char *str = new_id.c_str();
- if (current_doc->getObjectById(str) == NULL &&
- imported_doc->getObjectById(str) == NULL) break;
- }
- // Change to the new id
- SP_OBJECT_REPR(elem)->setAttribute("id", new_id.c_str());
- // Make a note of this change, if we need to fix up refs to it
- if (refmap->find(old_id) != refmap->end())
- id_changes->push_back(id_changeitem_type(elem, old_id));
- }
-
- /* recurse */
- for (SPObject *child = sp_object_first_child(elem);
- child; child = SP_OBJECT_NEXT(child) )
- {
- change_clashing_ids(imported_doc, current_doc, child, refmap, id_changes);
- }
-}
-
-/**
- * Fix up references to changed ids.
- */
-static void
-fix_up_refs(const refmap_type *refmap, const id_changelist_type &id_changes)
-{
- id_changelist_type::const_iterator pp;
- const id_changelist_type::const_iterator pp_end = id_changes.end();
- for (pp = id_changes.begin(); pp != pp_end; ++pp) {
- SPObject *obj = pp->first;
- refmap_type::const_iterator pos = refmap->find(pp->second);
- std::list<IdReference>::const_iterator it;
- const std::list<IdReference>::const_iterator it_end = pos->second.end();
- for (it = pos->second.begin(); it != it_end; ++it) {
- if (it->type == REF_HREF) {
- gchar *new_uri = g_strdup_printf("#%s", SP_OBJECT_ID(obj));
- SP_OBJECT_REPR(it->elem)->setAttribute(it->attr, new_uri);
- g_free(new_uri);
- }
- else if (it->type == REF_STYLE) {
- sp_style_set_property_url(it->elem, it->attr, obj, false);
- }
- else if (it->type == REF_URL) {
- gchar *url = g_strdup_printf("url(#%s)", SP_OBJECT_ID(obj));
- SP_OBJECT_REPR(it->elem)->setAttribute(it->attr, url);
- g_free(url);
- }
- else g_assert(0); // shouldn't happen
- }
- }
-}
-
-/**
- * This function resolves ID clashes between the document being imported
- * and the current open document: IDs in the imported document that would
- * clash with IDs in the existing document are changed, and references to
- * those IDs are updated accordingly.
- */
-void
-prevent_id_clashes(SPDocument *imported_doc, SPDocument *current_doc)
-{
- refmap_type *refmap = new refmap_type;
- id_changelist_type id_changes;
- SPObject *imported_root = SP_DOCUMENT_ROOT(imported_doc);
-
- find_references(imported_root, refmap);
- change_clashing_ids(imported_doc, current_doc, imported_root, refmap,
- &id_changes);
- fix_up_refs(refmap, id_changes);
-
- delete refmap;
-}
-
-
/**
* Import a resource. Called by sp_file_import()
*/
diff --git a/src/id-clash.cpp b/src/id-clash.cpp
--- /dev/null
+++ b/src/id-clash.cpp
@@ -0,0 +1,275 @@
+#define __ID_CLASH_C__
+/** \file
+ * Routines for resolving ID clashes when importing or pasting.
+ *
+ * Authors:
+ * Stephen Silver <sasilver@users.sourceforge.net>
+ *
+ * Copyright (C) 2008 authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <cstdlib>
+#include <cstring>
+#include <list>
+#include <map>
+#include <string>
+#include <utility>
+
+#include "extract-uri.h"
+#include "id-clash.h"
+#include "sp-object.h"
+#include "style.h"
+#include "xml/node.h"
+#include "xml/repr.h"
+
+typedef enum { REF_HREF, REF_STYLE, REF_URL, REF_CLIPBOARD } ID_REF_TYPE;
+
+struct IdReference {
+ ID_REF_TYPE type;
+ SPObject *elem;
+ const char *attr; // property or href-like attribute
+};
+
+typedef std::map<std::string, std::list<IdReference> > refmap_type;
+
+typedef std::pair<SPObject*, std::string> id_changeitem_type;
+typedef std::list<id_changeitem_type> id_changelist_type;
+
+const char *href_like_attributes[] = {
+ "inkscape:href",
+ "inkscape:path-effect",
+ "inkscape:perspectiveID",
+ "inkscape:tiled-clone-of",
+ "xlink:href",
+};
+#define NUM_HREF_LIKE_ATTRIBUTES (sizeof(href_like_attributes) / sizeof(*href_like_attributes))
+
+const SPIPaint SPStyle::* SPIPaint_members[] = {
+ &SPStyle::color,
+ &SPStyle::fill,
+ &SPStyle::stroke,
+};
+const char* SPIPaint_properties[] = {
+ "color",
+ "fill",
+ "stroke",
+};
+#define NUM_SPIPAINT_PROPERTIES (sizeof(SPIPaint_properties) / sizeof(*SPIPaint_properties))
+
+const char* other_url_properties[] = {
+ "clip-path",
+ "color-profile",
+ "cursor",
+ "marker-end",
+ "marker-mid",
+ "marker-start",
+ "mask",
+};
+#define NUM_OTHER_URL_PROPERTIES (sizeof(other_url_properties) / sizeof(*other_url_properties))
+
+const char* clipboard_properties[] = {
+ "color",
+ "fill",
+ "filter",
+ "stroke",
+};
+#define NUM_CLIPBOARD_PROPERTIES (sizeof(clipboard_properties) / sizeof(*clipboard_properties))
+
+/**
+ * Build a table of places where IDs are referenced, for a given element.
+ * FIXME: There are some types of references not yet dealt with here
+ * (e.g., ID selectors in CSS stylesheets, and references in scripts).
+ */
+static void
+find_references(SPObject *elem, refmap_type *refmap)
+{
+ Inkscape::XML::Node *repr_elem = SP_OBJECT_REPR(elem);
+ if (repr_elem->type() != Inkscape::XML::ELEMENT_NODE) return;
+
+ /* check for references in inkscape:clipboard elements */
+ if (!std::strcmp(repr_elem->name(), "inkscape:clipboard")) {
+ SPCSSAttr *css = sp_repr_css_attr(repr_elem, "style");
+ if (css) {
+ for (unsigned i = 0; i < NUM_CLIPBOARD_PROPERTIES; ++i) {
+ const char *attr = clipboard_properties[i];
+ const gchar *value = sp_repr_css_property(css, attr, NULL);
+ if (value) {
+ gchar *uri = extract_uri(value);
+ if (uri && uri[0] == '#') {
+ IdReference idref = { REF_CLIPBOARD, elem, attr };
+ (*refmap)[uri+1].push_back(idref);
+ }
+ g_free(uri);
+ }
+ }
+ }
+ return; // nothing more to do for inkscape:clipboard elements
+ }
+
+ /* check for xlink:href="#..." and similar */
+ for (unsigned i = 0; i < NUM_HREF_LIKE_ATTRIBUTES; ++i) {
+ const char *attr = href_like_attributes[i];
+ const gchar *val = repr_elem->attribute(attr);
+ if (val && val[0] == '#') {
+ std::string id(val+1);
+ IdReference idref = { REF_HREF, elem, attr };
+ (*refmap)[id].push_back(idref);
+ }
+ }
+
+ SPStyle *style = SP_OBJECT_STYLE(elem);
+
+ /* check for url(#...) references in 'fill' or 'stroke' */
+ for (unsigned i = 0; i < NUM_SPIPAINT_PROPERTIES; ++i) {
+ const SPIPaint SPStyle::*prop = SPIPaint_members[i];
+ const SPIPaint *paint = &(style->*prop);
+ if (paint->isPaintserver()) {
+ const gchar *id = SP_OBJECT_ID(paint->value.href->getObject());
+ IdReference idref = { REF_STYLE, elem, SPIPaint_properties[i] };
+ (*refmap)[id].push_back(idref);
+ }
+ }
+
+ /* check for url(#...) references in 'filter' */
+ const SPIFilter *filter = &(style->filter);
+ if (filter->href) {
+ const gchar *id = SP_OBJECT_ID(filter->href->getObject());
+ IdReference idref = { REF_STYLE, elem, "filter" };
+ (*refmap)[id].push_back(idref);
+ }
+
+ /* check for other url(#...) references */
+ for (unsigned i = 0; i < NUM_OTHER_URL_PROPERTIES; ++i) {
+ const char *attr = other_url_properties[i];
+ const gchar *value = repr_elem->attribute(attr);
+ if (value) {
+ gchar *uri = extract_uri(value);
+ if (uri && uri[0] == '#') {
+ IdReference idref = { REF_URL, elem, attr };
+ (*refmap)[uri+1].push_back(idref);
+ }
+ g_free(uri);
+ }
+ }
+
+ /* recurse */
+ for (SPObject *child = sp_object_first_child(elem);
+ child; child = SP_OBJECT_NEXT(child) )
+ {
+ find_references(child, refmap);
+ }
+}
+
+/**
+ * Change any IDs that clash with IDs in the current document, and make
+ * a list of those changes that will require fixing up references.
+ */
+static void
+change_clashing_ids(SPDocument *imported_doc, SPDocument *current_doc,
+ SPObject *elem, const refmap_type *refmap,
+ id_changelist_type *id_changes)
+{
+ const gchar *id = SP_OBJECT_ID(elem);
+
+ if (id && current_doc->getObjectById(id)) {
+ // Choose a new ID.
+ // To try to preserve any meaningfulness that the original ID
+ // may have had, the new ID is the old ID followed by a hyphen
+ // and one or more digits.
+ std::string old_id(id);
+ std::string new_id(old_id + '-');
+ for (;;) {
+ new_id += "0123456789"[std::rand() % 10];
+ const char *str = new_id.c_str();
+ if (current_doc->getObjectById(str) == NULL &&
+ imported_doc->getObjectById(str) == NULL) break;
+ }
+ // Change to the new ID
+ SP_OBJECT_REPR(elem)->setAttribute("id", new_id.c_str());
+ // Make a note of this change, if we need to fix up refs to it
+ if (refmap->find(old_id) != refmap->end())
+ id_changes->push_back(id_changeitem_type(elem, old_id));
+ }
+
+ /* recurse */
+ for (SPObject *child = sp_object_first_child(elem);
+ child; child = SP_OBJECT_NEXT(child) )
+ {
+ change_clashing_ids(imported_doc, current_doc, child, refmap, id_changes);
+ }
+}
+
+/**
+ * Fix up references to changed IDs.
+ */
+static void
+fix_up_refs(const refmap_type *refmap, const id_changelist_type &id_changes)
+{
+ id_changelist_type::const_iterator pp;
+ const id_changelist_type::const_iterator pp_end = id_changes.end();
+ for (pp = id_changes.begin(); pp != pp_end; ++pp) {
+ SPObject *obj = pp->first;
+ refmap_type::const_iterator pos = refmap->find(pp->second);
+ std::list<IdReference>::const_iterator it;
+ const std::list<IdReference>::const_iterator it_end = pos->second.end();
+ for (it = pos->second.begin(); it != it_end; ++it) {
+ if (it->type == REF_HREF) {
+ gchar *new_uri = g_strdup_printf("#%s", SP_OBJECT_ID(obj));
+ SP_OBJECT_REPR(it->elem)->setAttribute(it->attr, new_uri);
+ g_free(new_uri);
+ }
+ else if (it->type == REF_STYLE) {
+ sp_style_set_property_url(it->elem, it->attr, obj, false);
+ }
+ else if (it->type == REF_URL) {
+ gchar *url = g_strdup_printf("url(#%s)", SP_OBJECT_ID(obj));
+ SP_OBJECT_REPR(it->elem)->setAttribute(it->attr, url);
+ g_free(url);
+ }
+ else if (it->type == REF_CLIPBOARD) {
+ SPCSSAttr *style = sp_repr_css_attr(SP_OBJECT_REPR(it->elem), "style");
+ gchar *url = g_strdup_printf("url(#%s)", SP_OBJECT_ID(obj));
+ sp_repr_css_set_property(style, it->attr, url);
+ g_free(url);
+ gchar *style_string = sp_repr_css_write_string(style);
+ SP_OBJECT_REPR(it->elem)->setAttribute("style", style_string);
+ g_free(style_string);
+ }
+ else g_assert(0); // shouldn't happen
+ }
+ }
+}
+
+/**
+ * This function resolves ID clashes between the document being imported
+ * and the current open document: IDs in the imported document that would
+ * clash with IDs in the existing document are changed, and references to
+ * those IDs are updated accordingly.
+ */
+void
+prevent_id_clashes(SPDocument *imported_doc, SPDocument *current_doc)
+{
+ refmap_type *refmap = new refmap_type;
+ id_changelist_type id_changes;
+ SPObject *imported_root = SP_DOCUMENT_ROOT(imported_doc);
+
+ find_references(imported_root, refmap);
+ change_clashing_ids(imported_doc, current_doc, imported_root, refmap,
+ &id_changes);
+ fix_up_refs(refmap, id_changes);
+
+ delete refmap;
+}
+
+/*
+ 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/id-clash.h b/src/id-clash.h
--- /dev/null
+++ b/src/id-clash.h
@@ -0,0 +1,19 @@
+#ifndef SEEN_ID_CLASH_H
+#define SEEN_ID_CLASH_H
+
+#include "document.h"
+
+void prevent_id_clashes(SPDocument *imported_doc, SPDocument *current_doc);
+
+#endif /* !SEEN_ID_CLASH_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/ui/clipboard.cpp b/src/ui/clipboard.cpp
index f7967bd40d7546f48b572601872a0825caa44f70..28dc7d937c451d2ae0ae6cd7e5606833b6fd7695 100644 (file)
--- a/src/ui/clipboard.cpp
+++ b/src/ui/clipboard.cpp
#include "tools-switch.h"
#include "libnr/n-art-bpath-2geom.h"
#include "path-chemistry.h"
+#include "id-clash.h"
/// @brief Made up mimetype to represent Gdk::Pixbuf clipboard contents
#define CLIPBOARD_GDK_PIXBUF_TARGET "image/x-gdk-pixbuf"
*target_defs = SP_OBJECT_REPR(SP_DOCUMENT_DEFS(target_document));
Inkscape::XML::Document *target_xmldoc = sp_document_repr_doc(target_document);
+ prevent_id_clashes(clipdoc, target_document);
+
for (Inkscape::XML::Node *def = defs->firstChild() ; def ; def = def->next()) {
- /// @todo TODO: implement def id collision resolution in ClipboardManagerImpl::_pasteDefs()
-
- /*
- // simplistic solution: when a collision occurs, add "a" to id until it's unique
- Glib::ustring pasted_id = def->attribute("id");
- if ( pasted_id.empty() ) continue; // defs without id are useless
- Glib::ustring pasted_id_original = pasted_id;
-
- while(sp_repr_lookup_child(target_defs, "id", pasted_id.data())) {
- pasted_id.append("a");
- }
-
- if ( pasted_id != pasted_id_original ) {
- def->setAttribute("id", pasted_id.data());
- // Update the id in the rest of the document so there are no dangling references
- // How to do that?
- _changeIdReferences(clipdoc, pasted_id_original, pasted_id);
- }
- */
- if (sp_repr_lookup_child(target_defs, "id", def->attribute("id")))
- continue; // skip duplicate defs - temporary non-solution
-
_copyNode(def, target_xmldoc, target_defs);
}
}