summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJon A. Cruz <jon@joncruz.org>2011-05-06 06:21:51 +0000
committerJon A. Cruz <jon@joncruz.org>2011-05-06 06:21:51 +0000
commita4d0a358424440128cd4c4fb2915ccc4b86f4587 (patch)
tree818fc169950e0076d262bd8e633976afaafd7783 /src
parentsymbol rendering fix bug:705345 (diff)
downloadinkscape-a4d0a358424440128cd4c4fb2915ccc4b86f4587.tar.gz
inkscape-a4d0a358424440128cd4c4fb2915ccc4b86f4587.zip
Adding initial cut of resource manager.
(bzr r10198)
Diffstat (limited to 'src')
-rw-r--r--src/Makefile_insert1
-rw-r--r--src/desktop.cpp7
-rw-r--r--src/desktop.h1
-rw-r--r--src/dir-util.cpp218
-rw-r--r--src/dir-util.h37
-rw-r--r--src/display/curve-test.h2
-rw-r--r--src/document-undo.h6
-rw-r--r--src/file.cpp28
-rw-r--r--src/inkscape.cpp2
-rw-r--r--src/resource-manager.cpp272
-rw-r--r--src/resource-manager.h49
-rw-r--r--src/sp-image.cpp71
-rw-r--r--src/ui/view/edit-widget-interface.h4
-rw-r--r--src/widgets/desktop-widget.cpp19
-rw-r--r--src/widgets/desktop-widget.h7
-rw-r--r--src/xml/Makefile_insert1
-rw-r--r--src/xml/rebase-hrefs-test.h126
-rw-r--r--src/xml/rebase-hrefs.cpp231
-rw-r--r--src/xml/rebase-hrefs.h27
-rw-r--r--src/xml/repr-action-test.h1
-rw-r--r--src/xml/repr-io.cpp47
-rw-r--r--src/xml/repr.h3
22 files changed, 840 insertions, 320 deletions
diff --git a/src/Makefile_insert b/src/Makefile_insert
index 36c9de34f..3a3862437 100644
--- a/src/Makefile_insert
+++ b/src/Makefile_insert
@@ -123,6 +123,7 @@ ink_common_sources += \
removeoverlap.cpp removeoverlap.h \
rdf.cpp rdf.h \
rect-context.cpp rect-context.h \
+ resource-manager.cpp resource-manager.h \
require-config.h \
round.h \
rubberband.cpp rubberband.h \
diff --git a/src/desktop.cpp b/src/desktop.cpp
index a6224a71c..361ed7fea 100644
--- a/src/desktop.cpp
+++ b/src/desktop.cpp
@@ -87,6 +87,7 @@
#include "device-manager.h"
#include "layer-fns.h"
#include "layer-manager.h"
+#include "resource-manager.h"
#include "event-log.h"
#include "display/canvas-grid.h"
#include "widgets/desktop-widget.h"
@@ -177,6 +178,7 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWid
// Temporary workaround for link order issues:
Inkscape::DeviceManager::getManager().getDevices();
+ Inkscape::ResourceManager::getManager();
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
_guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack()));
@@ -1320,6 +1322,11 @@ SPDesktop::presentWindow()
_widget->present();
}
+bool SPDesktop::showInfoDialog( Glib::ustring const & message )
+{
+ return _widget->showInfoDialog( message );
+}
+
bool
SPDesktop::warnDialog (gchar *text)
{
diff --git a/src/desktop.h b/src/desktop.h
index 947e92fe7..6d1bcd194 100644
--- a/src/desktop.h
+++ b/src/desktop.h
@@ -288,6 +288,7 @@ public:
void setWindowTransient (void* p, int transient_policy=1);
Gtk::Window* getToplevel();
void presentWindow();
+ bool showInfoDialog( Glib::ustring const &message );
bool warnDialog (gchar *text);
void toggleRulers();
void toggleScrollbars();
diff --git a/src/dir-util.cpp b/src/dir-util.cpp
index 67db03628..acec39953 100644
--- a/src/dir-util.cpp
+++ b/src/dir-util.cpp
@@ -1,9 +1,8 @@
-/** @file
- * @brief Utility functions for filenames
+/**
+ * @file
+ * Utility functions for filenames.
*/
-#define DIR_UTIL_C
-
#include <errno.h>
#include <string>
#include <cstring>
@@ -13,49 +12,37 @@
#include <glib/gconvert.h>
#include <glib/gstrfuncs.h>
-/** Returns a form of \a path relative to \a base if that is easy to construct (e.g. if \a path
- appears to be in the directory specified by \a base), otherwise returns \a path.
-
- N.B. The return value is a pointer into the \a path string.
-
- \a base is expected to be either NULL or the absolute path of a directory.
-
- \a path is expected to be an absolute path.
-
- \see inkscape_abs2rel for a more sophisticated version.
- \see prepend_current_dir_if_relative.
-*/
-char const *
-sp_relative_path_from_path(char const *const path, char const *const base)
+std::string sp_relative_path_from_path( std::string const &path, std::string const &base)
{
- if (base == NULL || path == NULL) {
- return path;
- }
+ std::string result;
+ if ( !base.empty() && !path.empty() ) {
+ size_t base_len = base.length();
+ while (base_len != 0
+ && (base[base_len - 1] == G_DIR_SEPARATOR))
+ {
+ --base_len;
+ }
- size_t base_len = strlen(base);
- while (base_len != 0
- && (base[base_len - 1] == G_DIR_SEPARATOR))
- {
- --base_len;
- }
+ if ( (path.substr(0, base_len) == base.substr(0, base_len))
+ && (path[base_len] == G_DIR_SEPARATOR))
+ {
+ size_t retPos = base_len + 1;
+ while ( (retPos < path.length()) && (path[retPos] == G_DIR_SEPARATOR) ) {
+ retPos++;
+ }
+ if ( (retPos + 1) < path.length() ) {
+ result = path.substr(retPos);
+ }
+ }
- if ((memcmp(path, base, base_len) == 0)
- && (path[base_len] == G_DIR_SEPARATOR))
- {
- char const *ret = path + base_len + 1;
- while (*ret == G_DIR_SEPARATOR) {
- ++ret;
- }
- if (*ret != '\0') {
- return ret;
- }
- }
-
- return path;
+ }
+ if ( result.empty() ) {
+ result = path;
+ }
+ return result;
}
-char const *
-sp_extension_from_path(char const *const path)
+char const *sp_extension_from_path(char const *const path)
{
if (path == NULL) {
return NULL;
@@ -77,25 +64,7 @@ static char const dots[] = {'.', '.', G_DIR_SEPARATOR, '\0'};
static char const *const parent = dots;
static char const *const current = dots + 1;
-/**
- * \brief Convert a relative path name into absolute. If path is already absolute, does nothing except copying path to result.
- *
- * \param path relative path
- * \param base base directory (must be absolute path)
- * \param result result buffer
- * \param size size of result buffer
- * \return != NULL: absolute path
- * == NULL: error
-
-\comment
- based on functions by Shigio Yamaguchi.
- FIXME:TODO: force it to also do path normalization of the entire resulting path,
- i.e. get rid of any .. and . in any place, even if 'path' is already absolute
- (now it returns it unchanged in this case)
-
- */
-char *
-inkscape_rel2abs (const char *path, const char *base, char *result, const size_t size)
+char *inkscape_rel2abs(const char *path, const char *base, char *result, const size_t size)
{
const char *pp, *bp;
/* endp points the last position which is safe in the result buffer. */
@@ -181,79 +150,77 @@ erange:
return (NULL);
}
-char *
-inkscape_abs2rel (const char *path, const char *base, char *result, const size_t size)
+char *inkscape_abs2rel(const char *path, const char *base, char *result, const size_t size)
{
- const char *pp, *bp, *branch;
- /* endp points the last position which is safe in the result buffer. */
- const char *endp = result + size - 1;
- char *rp;
+ const char *pp, *bp, *branch;
+ // endp points the last position which is safe in the result buffer.
+ const char *endp = result + size - 1;
+ char *rp;
- if (*path != G_DIR_SEPARATOR)
+ if (*path != G_DIR_SEPARATOR)
{
- if (strlen (path) >= size)
- goto erange;
- strcpy (result, path);
- goto finish;
+ if (strlen (path) >= size)
+ goto erange;
+ strcpy (result, path);
+ goto finish;
}
- else if (*base != G_DIR_SEPARATOR || !size)
+ else if (*base != G_DIR_SEPARATOR || !size)
{
- errno = EINVAL;
- return (NULL);
+ errno = EINVAL;
+ return (NULL);
}
- else if (size == 1)
- goto erange;
- /* seek to branched point. */
- branch = path;
- for (pp = path, bp = base; *pp && *bp && *pp == *bp; pp++, bp++)
- if (*pp == G_DIR_SEPARATOR)
- branch = pp;
- if (((*pp == 0) || ((*pp == G_DIR_SEPARATOR) && (*(pp + 1) == 0))) &&
- ((*bp == 0) || ((*bp == G_DIR_SEPARATOR) && (*(bp + 1) == 0))))
+ else if (size == 1)
+ goto erange;
+ /* seek to branched point. */
+ branch = path;
+ for (pp = path, bp = base; *pp && *bp && *pp == *bp; pp++, bp++)
+ if (*pp == G_DIR_SEPARATOR)
+ branch = pp;
+ if (((*pp == 0) || ((*pp == G_DIR_SEPARATOR) && (*(pp + 1) == 0))) &&
+ ((*bp == 0) || ((*bp == G_DIR_SEPARATOR) && (*(bp + 1) == 0))))
{
- rp = result;
- *rp++ = '.';
- if (*pp == G_DIR_SEPARATOR || *(pp - 1) == G_DIR_SEPARATOR)
- *rp++ = G_DIR_SEPARATOR;
- if (rp > endp)
- goto erange;
- *rp = 0;
- goto finish;
+ rp = result;
+ *rp++ = '.';
+ if (*pp == G_DIR_SEPARATOR || *(pp - 1) == G_DIR_SEPARATOR)
+ *rp++ = G_DIR_SEPARATOR;
+ if (rp > endp)
+ goto erange;
+ *rp = 0;
+ goto finish;
}
- if (((*pp == 0) && (*bp == G_DIR_SEPARATOR)) || ((*pp == G_DIR_SEPARATOR) && (*bp == 0)))
- branch = pp;
- /* up to root. */
- rp = result;
- for (bp = base + (branch - path); *bp; bp++)
- if (*bp == G_DIR_SEPARATOR && *(bp + 1) != 0)
- {
- if (rp + 3 > endp)
- goto erange;
- *rp++ = '.';
- *rp++ = '.';
- *rp++ = G_DIR_SEPARATOR;
- }
- if (rp > endp)
- goto erange;
- *rp = 0;
- /* down to leaf. */
- if (*branch)
+ if (((*pp == 0) && (*bp == G_DIR_SEPARATOR)) || ((*pp == G_DIR_SEPARATOR) && (*bp == 0)))
+ branch = pp;
+ /* up to root. */
+ rp = result;
+ for (bp = base + (branch - path); *bp; bp++)
+ if (*bp == G_DIR_SEPARATOR && *(bp + 1) != 0)
+ {
+ if (rp + 3 > endp)
+ goto erange;
+ *rp++ = '.';
+ *rp++ = '.';
+ *rp++ = G_DIR_SEPARATOR;
+ }
+ if (rp > endp)
+ goto erange;
+ *rp = 0;
+ /* down to leaf. */
+ if (*branch)
{
- if (rp + strlen (branch + 1) > endp)
- goto erange;
- strcpy (rp, branch + 1);
+ if (rp + strlen (branch + 1) > endp)
+ goto erange;
+ strcpy (rp, branch + 1);
}
- else
- *--rp = 0;
+ else
+ *--rp = 0;
finish:
- return result;
+ return result;
erange:
- errno = ERANGE;
- return (NULL);
+ errno = ERANGE;
+ return (NULL);
}
-gchar *
-prepend_current_dir_if_relative(gchar const *uri)
+gchar *prepend_current_dir_if_relative(gchar const *uri)
{
if (!uri) {
return NULL;
@@ -278,4 +245,13 @@ prepend_current_dir_if_relative(gchar const *uri)
return ret;
}
-
+/*
+ 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:
+*/
+// vi: set autoindent shiftwidth=4 tabstop=8 filetype=cpp expandtab softtabstop=4 encoding=utf-8 textwidth=99 :
diff --git a/src/dir-util.h b/src/dir-util.h
index 7d04b3007..f7700cfa3 100644
--- a/src/dir-util.h
+++ b/src/dir-util.h
@@ -12,14 +12,47 @@
#include <stdlib.h>
#include <glib/gtypes.h>
-char const *sp_relative_path_from_path(char const *path, char const *base);
+/**
+ * Returns a form of \a path relative to \a base if that is easy to construct (eg if \a path
+ * appears to be in the directory specified by \a base), otherwise returns \a path.
+ *
+ * @param path is expected to be an absolute path.
+ * @param base is expected to be either empty or the absolute path of a directory.
+ *
+ * @return a relative version of the path, if reasonable.
+ *
+ * @see inkscape_abs2rel for a more sophisticated version.
+ * @see prepend_current_dir_if_relative.
+*/
+std::string sp_relative_path_from_path(std::string const &path, std::string const &base);
+
char const *sp_extension_from_path(char const *path);
+
+/**
+ * Convert a relative path name into absolute. If path is already absolute, does nothing except copying path to result.
+ *
+ * @param path relative path.
+ * @param base base directory (must be absolute path).
+ * @param result result buffer.
+ * @param size size of result buffer.
+ *
+ * @return != NULL: absolute path
+ * == NULL: error
+ *
+ * based on functions by Shigio Yamaguchi.
+ * FIXME:TODO: force it to also do path normalization of the entire resulting path,
+ * i.e. get rid of any .. and . in any place, even if 'path' is already absolute
+ * (now it returns it unchanged in this case)
+ *
+ */
char *inkscape_rel2abs(char const *path, char const *base, char *result, size_t const size);
+
char *inkscape_abs2rel(char const *path, char const *base, char *result, size_t const size);
+
gchar *prepend_current_dir_if_relative(gchar const *filename);
-#endif /* !SEEN_DIR_UTIL_H */
+#endif // !SEEN_DIR_UTIL_H
/*
Local Variables:
diff --git a/src/display/curve-test.h b/src/display/curve-test.h
index d89cb4c99..3d698ca07 100644
--- a/src/display/curve-test.h
+++ b/src/display/curve-test.h
@@ -21,7 +21,7 @@ public:
path1.close();
// Closed path (ClosingSegment is zero length)
path2.append(Geom::LineSegment(Geom::Point(2,0),Geom::Point(3,0)));
- path2.append(Geom::BezierCurve<3>(Geom::Point(3,0),Geom::Point(2,1),Geom::Point(1,1),Geom::Point(2,0)));
+ // TODO fix path2.append(Geom::BezierCurve<3>(Geom::Point(3,0),Geom::Point(2,1),Geom::Point(1,1),Geom::Point(2,0)));
path2.close();
// Open path
path3.append(Geom::SVGEllipticalArc(Geom::Point(4,0),1,2,M_PI,false,false,Geom::Point(5,1)));
diff --git a/src/document-undo.h b/src/document-undo.h
index 9be260fa2..e4f0d15a8 100644
--- a/src/document-undo.h
+++ b/src/document-undo.h
@@ -15,10 +15,10 @@ public:
* Since undo sensitivity needs to be nested, setting undo sensitivity
* should be done like this:
*\verbatim
- bool saved = sp_document_get_undo_sensitive(document);
- sp_document_set_undo_sensitive(document, false);
+ bool saved = DocumentUndo::getUndoSensitive(document);
+ DocumentUndo::setUndoSensitive(document, false);
... do stuff ...
- sp_document_set_undo_sensitive(document, saved); \endverbatim
+ DocumentUndo::setUndoSensitive(document, saved); \endverbatim
*/
static void setUndoSensitive(SPDocument *doc, bool sensitive);
diff --git a/src/file.cpp b/src/file.cpp
index c93188358..a1fc23117 100644
--- a/src/file.cpp
+++ b/src/file.cpp
@@ -54,6 +54,7 @@
#include "path-prefix.h"
#include "preferences.h"
#include "print.h"
+#include "resource-manager.h"
#include "rdf.h"
#include "selection-chemistry.h"
#include "selection.h"
@@ -209,14 +210,14 @@ sp_file_exit()
* \param replace_empty if true, and the current desktop is empty, this document
* will replace the empty one.
*/
-bool
-sp_file_open(const Glib::ustring &uri,
- Inkscape::Extension::Extension *key,
- bool add_to_recent, bool replace_empty)
+bool sp_file_open(const Glib::ustring &uri,
+ Inkscape::Extension::Extension *key,
+ bool add_to_recent, bool replace_empty)
{
SPDesktop *desktop = SP_ACTIVE_DESKTOP;
- if (desktop)
+ if (desktop) {
desktop->setWaitingCursor();
+ }
SPDocument *doc = NULL;
try {
@@ -227,27 +228,30 @@ sp_file_open(const Glib::ustring &uri,
doc = NULL;
}
- if (desktop)
+ if (desktop) {
desktop->clearWaitingCursor();
+ }
if (doc) {
SPDocument *existing = desktop ? sp_desktop_document(desktop) : NULL;
if (existing && existing->virgin && replace_empty) {
// If the current desktop is empty, open the document there
- doc->ensureUpToDate();
+ doc->ensureUpToDate(); // TODO this will trigger broken link warnings, etc.
desktop->change_document(doc);
doc->emitResizedSignal(doc->getWidth(), doc->getHeight());
} else {
// create a whole new desktop and window
- SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
+ SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL)); // TODO this will trigger broken link warnings, etc.
sp_create_window(dtw, TRUE);
desktop = static_cast<SPDesktop*>(dtw->view);
}
doc->virgin = FALSE;
+
// everyone who cares now has a reference, get rid of ours
doc->doUnref();
+
// resize the window to match the document properties
sp_namedview_window_from_document(desktop);
sp_namedview_update_layers_from_document(desktop);
@@ -256,6 +260,14 @@ sp_file_open(const Glib::ustring &uri,
sp_file_add_recent( doc->getURI() );
}
+ if ( inkscape_use_gui() ) {
+ // Perform a fixup pass for hrefs.
+ if ( Inkscape::ResourceManager::getManager().fixupBrokenLinks(doc) ) {
+ Glib::ustring msg = _("Broken links have been changed to point to existing files.");
+ desktop->showInfoDialog(msg);
+ }
+ }
+
return TRUE;
} else {
gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str());
diff --git a/src/inkscape.cpp b/src/inkscape.cpp
index 1007c315a..91e3b0c5f 100644
--- a/src/inkscape.cpp
+++ b/src/inkscape.cpp
@@ -59,6 +59,7 @@ using Inkscape::Extension::Internal::PrintWin32;
#include "io/sys.h"
#include "message-stack.h"
#include "preferences.h"
+#include "resource-manager.h"
#include "selection.h"
#include "ui/dialog/debug.h"
#include "xml/repr.h"
@@ -820,6 +821,7 @@ inkscape_application_init (const gchar *argv0, gboolean use_gui)
inkscape_load_menus(inkscape);
Inkscape::DeviceManager::getManager().loadConfig();
}
+ Inkscape::ResourceManager::getManager();
/* set language for user interface according setting in preferences */
Glib::ustring ui_language = prefs->getString("/ui/language");
diff --git a/src/resource-manager.cpp b/src/resource-manager.cpp
new file mode 100644
index 000000000..a68b2c7ae
--- /dev/null
+++ b/src/resource-manager.cpp
@@ -0,0 +1,272 @@
+/*
+ * Inkscape::ResourceManager - tracks external resources such as image and css files.
+ *
+ * Copyright 2011 Jon A. Cruz <jon@joncruz.org>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <string>
+#include <vector>
+#include <glibmm/i18n.h>
+#include <glibmm/convert.h>
+#include <glibmm/fileutils.h>
+#include <glibmm/miscutils.h>
+#include <glibmm/uriutils.h>
+
+#include "resource-manager.h"
+
+#include "document.h"
+#include "sp-object.h"
+#include "xml/node.h"
+#include "document-undo.h"
+
+namespace Inkscape {
+
+
+
+class ResourceManagerImpl : public ResourceManager {
+public:
+ ResourceManagerImpl();
+ virtual ~ResourceManagerImpl();
+
+ virtual bool fixupBrokenLinks(SPDocument *doc);
+
+
+ /**
+ * Walk all links in a document and create a listing of unique broken links.
+ *
+ * @return a list of all broken links.
+ */
+ std::vector<Glib::ustring> findBrokenLinks(SPDocument *doc);
+
+ /**
+ * Resolve broken links as a whole and return a map for those that can be found.
+ *
+ * Note: this will allow for future enhancements including relinking to new locations
+ * with the most broken files found, etc.
+ *
+ * @return a map of found links.
+ */
+ std::map<Glib::ustring, Glib::ustring> locateLinks(Glib::ustring const & docbase, std::vector<Glib::ustring> const & brokenLinks);
+
+ bool extractFilepath( Glib::ustring const &href, std::string &uri );
+
+protected:
+};
+
+
+ResourceManagerImpl::ResourceManagerImpl()
+ : ResourceManager()
+{
+}
+
+ResourceManagerImpl::~ResourceManagerImpl()
+{
+}
+
+bool ResourceManagerImpl::extractFilepath( Glib::ustring const &href, std::string &uri )
+{
+ bool isFile = false;
+
+ uri.clear();
+
+ std::string scheme = Glib::uri_parse_scheme(href);
+ if ( !scheme.empty() ) {
+ // TODO debug g_message("Scheme is now [%s]", scheme.c_str());
+ if ( scheme == "file" ) {
+ // TODO debug g_message("--- is a file URI [%s]", href.c_str());
+
+ // throws Glib::ConvertError:
+ uri = Glib::filename_from_uri(href); // TODO see if we can get this to throw
+ // TODO debug g_message(" [%s]", uri.c_str());
+ isFile = true;
+ }
+ } else {
+ // No scheme. Assuming it is a file path (absolute or relative).
+ // throws Glib::ConvertError:
+ uri = Glib::filename_from_utf8( href );
+ isFile = true;
+ }
+
+ return isFile;
+}
+
+
+std::vector<Glib::ustring> ResourceManagerImpl::findBrokenLinks( SPDocument *doc )
+{
+ std::vector<Glib::ustring> result;
+ std::set<Glib::ustring> uniques;
+
+ if ( doc ) {
+ GSList const *images = doc->getResourceList("image");
+ for (GSList const *it = images; it; it = it->next) {
+ Inkscape::XML::Node *ir = static_cast<SPObject *>(it->data)->getRepr();
+
+ gchar const *href = ir->attribute("xlink:href");
+ if ( href && ( uniques.find(href) == uniques.end() ) ) {
+ std::string uri;
+ if ( extractFilepath( href, uri ) ) {
+ if ( Glib::path_is_absolute(uri) ) {
+ if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) {
+ result.push_back(href);
+ uniques.insert(href);
+ }
+ } else {
+ std::string combined = Glib::build_filename(doc->getBase(), uri);
+ if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) {
+ result.push_back(href);
+ uniques.insert(href);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+
+std::map<Glib::ustring, Glib::ustring> ResourceManagerImpl::locateLinks(Glib::ustring const & docbase, std::vector<Glib::ustring> const & brokenLinks)
+{
+ std::map<Glib::ustring, Glib::ustring> result;
+
+ // At the moment we expect this list to contain file:// references, or simple relative or absolute paths.
+ for ( std::vector<Glib::ustring>::const_iterator it = brokenLinks.begin(); it != brokenLinks.end(); ++it ) {
+ // TODO debug g_message("========{%s}", it->c_str());
+
+ std::string uri;
+ if ( extractFilepath( *it, uri ) ) {
+ // We were able to get some path. Check it
+
+ if ( !Glib::path_is_absolute(uri) ) {
+ uri = Glib::build_filename(docbase, uri);
+ // TODO debug g_message(" not absolute. Fixing up as [%s]", uri.c_str());
+ }
+
+ if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) {
+ // TODO debug g_message(" DOES NOT EXIST.");
+ std::string tmp = uri;
+ std::string prior;
+ std::string remainder;
+ bool exists = false;
+ while ( (tmp != prior) && !exists) {
+ prior = tmp;
+ std::string basename = Glib::path_get_basename(tmp);
+ tmp = Glib::path_get_dirname(tmp);
+ if ( remainder.empty() ) {
+ remainder = basename;
+ } else {
+ remainder = Glib::build_filename(basename, remainder);
+ }
+
+ std::string rebuild = Glib::build_filename(docbase, remainder);
+ exists = Glib::file_test(rebuild, Glib::FILE_TEST_EXISTS);
+
+ // TODO debug g_message(" [%s] [%s]%s", tmp.c_str(), remainder.c_str(), exists ? " XXXX" : "");
+ if ( exists ) {
+ Glib::ustring replacement = Glib::filename_to_utf8( remainder );
+ result[*it] = replacement;
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+bool ResourceManagerImpl::fixupBrokenLinks(SPDocument *doc)
+{
+ bool changed = false;
+ if ( doc ) {
+ // TODO debug g_message("FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP");
+ // TODO debug g_message(" base is [%s]", doc->getBase());
+
+ std::vector<Glib::ustring> brokenHrefs = findBrokenLinks(doc);
+ if ( !brokenHrefs.empty() ) {
+ // TODO debug g_message(" FOUND SOME LINKS %d", brokenHrefs.size());
+ for ( std::vector<Glib::ustring>::iterator it = brokenHrefs.begin(); it != brokenHrefs.end(); ++it ) {
+ // TODO debug g_message(" [%s]", it->c_str());
+ }
+ }
+
+ std::map<Glib::ustring, Glib::ustring> mapping = locateLinks(doc->getBase(), brokenHrefs);
+ for ( std::map<Glib::ustring, Glib::ustring>::iterator it = mapping.begin(); it != mapping.end(); ++it )
+ {
+ // TODO debug g_message(" [%s] ==> {%s}", it->first.c_str(), it->second.c_str());
+ }
+
+ bool savedUndoState = DocumentUndo::getUndoSensitive(doc);
+ DocumentUndo::setUndoSensitive(doc, true);
+
+ GSList const *images = doc->getResourceList("image");
+ for (GSList const *it = images; it; it = it->next) {
+ Inkscape::XML::Node *ir = static_cast<SPObject *>(it->data)->getRepr();
+
+ gchar const *href = ir->attribute("xlink:href");
+ if ( href ) {
+ // TODO debug g_message(" consider [%s]", href);
+
+ if ( mapping.find(href) != mapping.end() ) {
+ // TODO debug g_message(" Found a replacement");
+
+ ir->setAttribute( "xlink:href", mapping[href].c_str() );
+ if ( ir->attribute( "sodipodi:absref" ) ) {
+ ir->setAttribute( "sodipodi:absref", 0 ); // Remove this attribute
+ }
+
+ SPObject *updated = doc->getObjectByRepr(ir);
+ if (updated) {
+ // force immediate update of dependant attributes
+ updated->updateRepr();
+ }
+
+ changed = true;
+ }
+ }
+ }
+ if ( changed ) {
+ DocumentUndo::done( doc, SP_VERB_DIALOG_XML_EDITOR, _("Fixup broken links") );
+ }
+ DocumentUndo::setUndoSensitive(doc, savedUndoState);
+ }
+
+ return changed;
+}
+
+
+
+
+static ResourceManagerImpl* theInstance = 0;
+
+ResourceManager::ResourceManager()
+ : Glib::Object()
+{
+}
+
+ResourceManager::~ResourceManager() {
+}
+
+ResourceManager& ResourceManager::getManager() {
+ if ( !theInstance ) {
+ theInstance = new ResourceManagerImpl();
+ }
+
+ return *theInstance;
+}
+
+
+} // namespace Inkscape
+
+/*
+ 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/resource-manager.h b/src/resource-manager.h
new file mode 100644
index 000000000..8f01c23a0
--- /dev/null
+++ b/src/resource-manager.h
@@ -0,0 +1,49 @@
+/*
+ * Inkscape::ResourceManager - Manages external resources such as image and css files.
+ *
+ * Copyright 2011 Jon A. Cruz <jon@joncruz.org>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef SEEN_INKSCAPE_RESOURCE_MANAGER_H
+#define SEEN_INKSCAPE_RESOURCE_MANAGER_H
+
+#include <glibmm/object.h>
+
+class SPDocument;
+
+namespace Inkscape {
+
+class ResourceManager : public Glib::Object {
+
+public:
+ static ResourceManager& getManager();
+
+ virtual bool fixupBrokenLinks(SPDocument *doc) = 0;
+
+protected:
+ ResourceManager();
+ virtual ~ResourceManager();
+
+private:
+ ResourceManager(ResourceManager const &); // no copy
+ void operator=(ResourceManager const &); // no assign
+};
+
+
+
+} // namespace Inkscape
+
+#endif // SEEN_INKSCAPE_RESOURCE_MANAGER_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:fileencoding=utf-8:textwidth=99 :
diff --git a/src/sp-image.cpp b/src/sp-image.cpp
index 8bd1bfadd..746cd97d9 100644
--- a/src/sp-image.cpp
+++ b/src/sp-image.cpp
@@ -5,6 +5,7 @@
* Lauris Kaplinski <lauris@kaplinski.com>
* Edward Flick (EAF)
* Abhishek Sharma
+ * Jon A. Cruz <jon@joncruz.org>
*
* Copyright (C) 1999-2005 Authors
* Copyright (C) 2000-2001 Ximian, Inc.
@@ -107,7 +108,6 @@ extern "C"
void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length );
void user_write_data( png_structp png_ptr, png_bytep data, png_size_t length );
void user_flush_data( png_structp png_ptr );
-
}
@@ -557,8 +557,7 @@ GdkPixbuf* pixbuf_new_from_file( const char *filename, GError **error )
}
}
-GType
-sp_image_get_type (void)
+GType sp_image_get_type(void)
{
static GType image_type = 0;
if (!image_type) {
@@ -579,8 +578,7 @@ sp_image_get_type (void)
return image_type;
}
-static void
-sp_image_class_init (SPImageClass * klass)
+static void sp_image_class_init( SPImageClass * klass )
{
GObjectClass * gobject_class;
SPObjectClass * sp_object_class;
@@ -635,8 +633,7 @@ static void sp_image_init( SPImage *image )
image->lastMod = 0;
}
-static void
-sp_image_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+static void sp_image_build( SPObject *object, SPDocument *document, Inkscape::XML::Node *repr )
{
if (((SPObjectClass *) parent_class)->build) {
((SPObjectClass *) parent_class)->build (object, document, repr);
@@ -654,8 +651,7 @@ sp_image_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *rep
document->addResource("image", object);
}
-static void
-sp_image_release (SPObject *object)
+static void sp_image_release( SPObject *object )
{
SPImage *image = SP_IMAGE(object);
@@ -695,8 +691,7 @@ sp_image_release (SPObject *object)
}
}
-static void
-sp_image_set (SPObject *object, unsigned int key, const gchar *value)
+static void sp_image_set( SPObject *object, unsigned int key, const gchar *value )
{
SPImage *image = SP_IMAGE (object);
@@ -818,8 +813,7 @@ sp_image_set (SPObject *object, unsigned int key, const gchar *value)
sp_image_set_curve(image); //creates a curve at the image's boundary for snapping
}
-static void
-sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
+static void sp_image_update( SPObject *object, SPCtx *ctx, unsigned int flags )
{
SPImage *image = SP_IMAGE(object);
SPDocument *doc = object->document;
@@ -1026,8 +1020,7 @@ sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags)
sp_image_update_canvas_image ((SPImage *) object);
}
-static void
-sp_image_modified (SPObject *object, unsigned int flags)
+static void sp_image_modified( SPObject *object, unsigned int flags )
{
SPImage *image = SP_IMAGE (object);
@@ -1042,8 +1035,7 @@ sp_image_modified (SPObject *object, unsigned int flags)
}
}
-static Inkscape::XML::Node *
-sp_image_write (SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags)
+static Inkscape::XML::Node *sp_image_write( SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags )
{
SPImage *image = SP_IMAGE (object);
@@ -1081,8 +1073,7 @@ sp_image_write (SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XM
return repr;
}
-static void
-sp_image_bbox(SPItem const *item, NRRect *bbox, Geom::Affine const &transform, unsigned const /*flags*/)
+static void sp_image_bbox( SPItem const *item, NRRect *bbox, Geom::Affine const &transform, unsigned const /*flags*/ )
{
SPImage const &image = *SP_IMAGE(item);
@@ -1099,8 +1090,7 @@ sp_image_bbox(SPItem const *item, NRRect *bbox, Geom::Affine const &transform, u
}
}
-static void
-sp_image_print (SPItem *item, SPPrintContext *ctx)
+static void sp_image_print( SPItem *item, SPPrintContext *ctx )
{
SPImage *image = SP_IMAGE(item);
@@ -1135,8 +1125,7 @@ sp_image_print (SPItem *item, SPPrintContext *ctx)
}
}
-static gchar *
-sp_image_description(SPItem *item)
+static gchar *sp_image_description( SPItem *item )
{
SPImage *image = SP_IMAGE(item);
char *href_desc;
@@ -1159,8 +1148,7 @@ sp_image_description(SPItem *item)
return ret;
}
-static NRArenaItem *
-sp_image_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/)
+static NRArenaItem *sp_image_show( SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/ )
{
SPImage * image = SP_IMAGE(item);
NRArenaItem *ai = NRArenaImage::create(arena);
@@ -1286,8 +1274,7 @@ GdkPixbuf *sp_image_repr_read_image( time_t& modTime, char*& pixPath, const gcha
return pixbuf;
}
-static GdkPixbuf *
-sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf)
+static GdkPixbuf *sp_image_pixbuf_force_rgba( GdkPixbuf * pixbuf )
{
GdkPixbuf* result;
if (gdk_pixbuf_get_has_alpha(pixbuf)) {
@@ -1301,8 +1288,7 @@ sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf)
/* We assert that realpixbuf is either NULL or identical size to pixbuf */
-static void
-sp_image_update_canvas_image (SPImage *image)
+static void sp_image_update_canvas_image( SPImage *image )
{
SPItem *item = SP_ITEM(image);
@@ -1342,7 +1328,7 @@ sp_image_update_canvas_image (SPImage *image)
}
}
-static void sp_image_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const */*snapprefs*/)
+static void sp_image_snappoints( SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const */*snapprefs*/ )
{
/* An image doesn't have any nodes to snap, but still we want to be able snap one image
to another. Therefore we will create some snappoints at the corner, similar to a rect. If
@@ -1376,8 +1362,7 @@ static void sp_image_snappoints(SPItem const *item, std::vector<Inkscape::SnapCa
* Transform x, y, set x, y, clear translation
*/
-static Geom::Affine
-sp_image_set_transform(SPItem *item, Geom::Affine const &xform)
+static Geom::Affine sp_image_set_transform( SPItem *item, Geom::Affine const &xform )
{
SPImage *image = SP_IMAGE(item);
@@ -1417,8 +1402,7 @@ sp_image_set_transform(SPItem *item, Geom::Affine const &xform)
return ret;
}
-static GdkPixbuf *
-sp_image_repr_read_dataURI (const gchar * uri_data)
+static GdkPixbuf *sp_image_repr_read_dataURI( const gchar * uri_data )
{
GdkPixbuf * pixbuf = NULL;
@@ -1474,8 +1458,7 @@ sp_image_repr_read_dataURI (const gchar * uri_data)
return pixbuf;
}
-static GdkPixbuf *
-sp_image_repr_read_b64 (const gchar * uri_data)
+static GdkPixbuf *sp_image_repr_read_b64( const gchar * uri_data )
{
GdkPixbuf * pixbuf = NULL;
@@ -1551,8 +1534,7 @@ sp_image_repr_read_b64 (const gchar * uri_data)
return pixbuf;
}
-static void
-sp_image_set_curve(SPImage *image)
+static void sp_image_set_curve( SPImage *image )
{
//create a curve at the image's boundary for snapping
if ((image->height.computed < MAGIC_EPSILON_TOO) || (image->width.computed < MAGIC_EPSILON_TOO) || (image->clip_ref->getObject())) {
@@ -1580,8 +1562,7 @@ sp_image_set_curve(SPImage *image)
/**
* Return duplicate of curve (if any exists) or NULL if there is no curve
*/
-SPCurve *
-sp_image_get_curve (SPImage *image)
+SPCurve *sp_image_get_curve( SPImage *image )
{
SPCurve *result = 0;
if (image->curve) {
@@ -1590,8 +1571,7 @@ sp_image_get_curve (SPImage *image)
return result;
}
-void
-sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb, Glib::ustring const &mime_in)
+void sp_embed_image( Inkscape::XML::Node *image_node, GdkPixbuf *pb, Glib::ustring const &mime_in )
{
Glib::ustring format, mime;
if (mime_in == "image/jpeg") {
@@ -1602,8 +1582,8 @@ sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb, Glib::ustring con
format = "png";
}
- gchar *data;
- gsize length;
+ gchar *data = 0;
+ gsize length = 0;
gdk_pixbuf_save_to_buffer(pb, &data, &length, format.data(), NULL, NULL);
// Save base64 encoded data in image node
@@ -1614,7 +1594,8 @@ sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb, Glib::ustring con
gchar *buffer = (gchar *) g_malloc(needed_size), *buf_work = buffer;
buf_work += g_sprintf(buffer, "data:%s;base64,", mime.data());
- gint state = 0, save = 0;
+ gint state = 0;
+ gint save = 0;
gsize written = 0;
written += g_base64_encode_step((guchar*) data, length, TRUE, buf_work, &state, &save);
written += g_base64_encode_close(TRUE, buf_work + written, &state, &save);
diff --git a/src/ui/view/edit-widget-interface.h b/src/ui/view/edit-widget-interface.h
index 919b570dd..4ff4f92f9 100644
--- a/src/ui/view/edit-widget-interface.h
+++ b/src/ui/view/edit-widget-interface.h
@@ -137,6 +137,10 @@ struct EditWidgetInterface
/// Message widget will get no content
virtual void setMessage (Inkscape::MessageType type, gchar const* msg) = 0;
+
+ /** Show an info dialog with the given message */
+ virtual bool showInfoDialog( Glib::ustring const &message ) = 0;
+
/// Open yes/no dialog with warning text and confirmation question.
virtual bool warnDialog (gchar*) = 0;
diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp
index 63fdc5930..87ce9053f 100644
--- a/src/widgets/desktop-widget.cpp
+++ b/src/widgets/desktop-widget.cpp
@@ -1150,6 +1150,25 @@ SPDesktopWidget::presentWindow()
gtk_window_present (w);
}
+bool SPDesktopWidget::showInfoDialog( Glib::ustring const &message )
+{
+ bool result = false;
+ GtkWindow *window = GTK_WINDOW( gtk_widget_get_toplevel( GTK_WIDGET(this) ) );
+ if (window)
+ {
+ GtkWidget *dialog = gtk_message_dialog_new(
+ window,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_OK,
+ "%s", message.c_str());
+ gtk_window_set_title( GTK_WINDOW(dialog), _("Note:")); // probably want to take this as a parameter.
+ gint response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ }
+ return result;
+}
+
bool
SPDesktopWidget::warnDialog (gchar* text)
{
diff --git a/src/widgets/desktop-widget.h b/src/widgets/desktop-widget.h
index 0102897e5..53d9dd1bc 100644
--- a/src/widgets/desktop-widget.h
+++ b/src/widgets/desktop-widget.h
@@ -200,8 +200,14 @@ struct SPDesktopWidget {
{ _dtw->setCoordinateStatus (p); }
virtual void setMessage (Inkscape::MessageType type, gchar const* msg)
{ _dtw->setMessage (type, msg); }
+
+ virtual bool showInfoDialog( Glib::ustring const &message ) {
+ return _dtw->showInfoDialog( message );
+ }
+
virtual bool warnDialog (gchar* text)
{ return _dtw->warnDialog (text); }
+
virtual Inkscape::UI::Widget::Dock* getDock ()
{ return _dtw->getDock(); }
};
@@ -218,6 +224,7 @@ struct SPDesktopWidget {
void setWindowSize (gint w, gint h);
void setWindowTransient (void *p, int transient_policy);
void presentWindow();
+ bool showInfoDialog( Glib::ustring const &message );
bool warnDialog (gchar *text);
void setToolboxFocusTo (gchar const *);
void setToolboxAdjustmentValue (gchar const * id, double value);
diff --git a/src/xml/Makefile_insert b/src/xml/Makefile_insert
index 7190b7948..b10f2448b 100644
--- a/src/xml/Makefile_insert
+++ b/src/xml/Makefile_insert
@@ -47,5 +47,6 @@ ink_common_sources += \
# ### CxxTest stuff ####
# ######################
CXXTEST_TESTSUITES += \
+ $(srcdir)/xml/rebase-hrefs-test.h \
$(srcdir)/xml/repr-action-test.h \
$(srcdir)/xml/quote-test.h
diff --git a/src/xml/rebase-hrefs-test.h b/src/xml/rebase-hrefs-test.h
new file mode 100644
index 000000000..e00337836
--- /dev/null
+++ b/src/xml/rebase-hrefs-test.h
@@ -0,0 +1,126 @@
+#include <cxxtest/TestSuite.h>
+
+#include <cstdlib>
+#include <glib.h>
+
+#include "uri.h"
+
+
+class RebaseHrefsTest : public CxxTest::TestSuite
+{
+ Inkscape::XML::Document *document;
+ Inkscape::XML::Node *a, *b, *c, *root;
+
+public:
+
+ RebaseHrefsTest()
+ {
+ Inkscape::GC::init();
+
+ document = sp_repr_document_new("test");
+ root = document->root();
+
+ a = document->createElement("a");
+ b = document->createElement("b");
+ c = document->createElement("c");
+ }
+ virtual ~RebaseHrefsTest() {}
+
+// createSuite and destroySuite get us per-suite setup and teardown
+// without us having to worry about static initialization order, etc.
+ static RebaseHrefsTest *createSuite() { return new RebaseHrefsTest(); }
+ static void destroySuite( RebaseHrefsTest *suite ) { delete suite; }
+
+
+ void dump_str(gchar const *str, gchar const *prefix)
+ {
+ Glib::ustring tmp;
+ tmp = prefix;
+ tmp += " [";
+ size_t const total = strlen(str);
+ for (unsigned i = 0; i < total; i++) {
+ gchar *const tmp2 = g_strdup_printf(" %02x", (0x0ff & str[i]));
+ tmp += tmp2;
+ g_free(tmp2);
+ }
+
+ tmp += "]";
+ g_message("%s", tmp.c_str());
+ }
+
+ void testFlipples()
+ {
+ using Inkscape::URI;
+ using Inkscape::MalformedURIException;
+
+ gchar const* things[] = {
+ "data:foo,bar",
+ "http://www.google.com/image.png",
+ "ftp://ssd.com/doo",
+ "/foo/dee/bar.svg",
+ "foo.svg",
+ "file:/foo/dee/bar.svg",
+ "file:///foo/dee/bar.svg",
+ "file:foo.svg",
+ "/foo/bar\xe1\x84\x92.svg",
+ "file:///foo/bar\xe1\x84\x92.svg",
+ "file:///foo/bar%e1%84%92.svg",
+ "/foo/bar%e1%84%92.svg",
+ "bar\xe1\x84\x92.svg",
+ "bar%e1%84%92.svg",
+ NULL
+ };
+ g_message("+------");
+ for ( int i = 0; things[i]; i++ )
+ {
+ try
+ {
+ URI uri(things[i]);
+ gboolean isAbs = g_path_is_absolute( things[i] );
+ gchar *str = uri.toString();
+ g_message( "abs:%d isRel:%d scheme:[%s] path:[%s][%s] uri[%s] / [%s]", (int)isAbs,
+ (int)uri.isRelative(),
+ uri.getScheme(),
+ uri.getPath(),
+ uri.getOpaque(),
+ things[i],
+ str );
+ g_free(str);
+ }
+ catch ( MalformedURIException err )
+ {
+ dump_str( things[i], "MalformedURIException" );
+ xmlChar *redo = xmlURIEscape((xmlChar const *)things[i]);
+ g_message(" gone from [%s] to [%s]", things[i], redo );
+ if ( redo == NULL )
+ {
+ URI again = URI::fromUtf8( things[i] );
+ g_message(" uri from [%s] to [%s]", things[i], again.toString() );
+ gboolean isAbs = g_path_is_absolute( things[i] );
+ gchar *str = again.toString();
+ g_message( "abs:%d isRel:%d scheme:[%s] path:[%s][%s] uri[%s] / [%s]", (int)isAbs,
+ (int)again.isRelative(),
+ again.getScheme(),
+ again.getPath(),
+ again.getOpaque(),
+ things[i],
+ str );
+ g_free(str);
+ g_message(" ----");
+ }
+ }
+ }
+ g_message("+------");
+ }
+};
+
+/*
+ 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:fileencoding=utf-8:textwidth=99 :
diff --git a/src/xml/rebase-hrefs.cpp b/src/xml/rebase-hrefs.cpp
index 33b31685d..71e1cfb87 100644
--- a/src/xml/rebase-hrefs.cpp
+++ b/src/xml/rebase-hrefs.cpp
@@ -10,68 +10,64 @@
#include <glib/gmem.h>
#include <glib/gurifuncs.h>
#include <glib/gutils.h>
-using Inkscape::XML::AttributeRecord;
+#include <glibmm/miscutils.h>
+#include <glibmm/convert.h>
+#include <glibmm/uriutils.h>
+using Inkscape::XML::AttributeRecord;
/**
- * \pre href.
+ * Determine if a href needs rebasing.
*/
-static bool
-href_needs_rebasing(char const *const href)
+static bool href_needs_rebasing(std::string const &href)
{
- g_return_val_if_fail(href, false);
+ bool ret = true;
- if (!*href || *href == '#') {
- return false;
+ if ( href.empty() || (href[0] == '#') ) {
+ ret = false;
/* False (no change) is the right behaviour even when the base URI differs from the
* document URI: RFC 3986 defines empty string relative URL as referring to the containing
* document, rather than referring to the base URI. */
- }
-
- /* Don't change data or http hrefs. */
- {
- char *const scheme = g_uri_parse_scheme(href);
- if (scheme) {
+ } else {
+ /* Don't change data or http hrefs. */
+ std::string scheme = Glib::uri_parse_scheme(href);
+ if ( !scheme.empty() ) {
/* Assume it shouldn't be changed. This is probably wrong if the scheme is `file'
* (or if the scheme of the new base is non-file, though I believe that never
* happens at the time of writing), but that's rare, and we won't try too hard to
* handle this now: wait until after the freeze, then add liburiparser (or similar)
* as a dependency and do it properly. For now we'll just try to be simple (while
* at least still correctly handling data hrefs). */
- free(scheme);
- return false;
+ ret = false;
+ } else if (Glib::path_is_absolute(href)) {
+ /* If absolute then keep it as is.
+ *
+ * Even in the following borderline cases:
+ *
+ * - We keep it absolute even if it is in new_base (directly or indirectly).
+ *
+ * - We assume that if xlink:href is absolute then we honour it in preference to
+ * sodipodi:absref even if sodipodi:absref points to an existing file while xlink:href
+ * doesn't. This is because we aren't aware of any bugs in xlink:href handling when
+ * it's absolute, so we assume that it's the best value to use even in this case.)
+ */
+ /* No strong preference on what we do for sodipodi:absref. Once we're
+ * confident of our handling of xlink:href and xlink:base, we should clear it.
+ * Though for the moment we do the simple thing: neither clear nor set it. */
+ ret = false;
}
}
- /* If absolute then keep it as is.
- *
- * Even in the following borderline cases:
- *
- * - We keep it absolute even if it is in new_base (directly or indirectly).
- *
- * - We assume that if xlink:href is absolute then we honour it in preference to
- * sodipodi:absref even if sodipodi:absref points to an existing file while xlink:href
- * doesn't. This is because we aren't aware of any bugs in xlink:href handling when
- * it's absolute, so we assume that it's the best value to use even in this case.)
- */
- if (g_path_is_absolute(href)) {
- /* No strong preference on what we do for sodipodi:absref. Once we're
- * confident of our handling of xlink:href and xlink:base, we should clear it.
- * Though for the moment we do the simple thing: neither clear nor set it. */
- return false;
- }
-
- return true;
+ return ret;
}
-static gchar *
-calc_abs_href(gchar const *const abs_base_dir, gchar const *const href,
- gchar const *const sp_absref)
+static std::string calc_abs_href(std::string const &abs_base_dir, std::string const &href,
+ gchar const *const sp_absref)
{
- gchar *ret = g_build_filename(abs_base_dir, href, NULL);
+ std::string ret = Glib::build_filename(abs_base_dir, href);
if ( sp_absref
- && !Inkscape::IO::file_test(ret, G_FILE_TEST_EXISTS)
+ && !Inkscape::IO::file_test(ret.c_str(), G_FILE_TEST_EXISTS)
&& Inkscape::IO::file_test(sp_absref, G_FILE_TEST_EXISTS) )
{
/* sodipodi:absref points to an existing file while xlink:href doesn't.
@@ -93,18 +89,12 @@ calc_abs_href(gchar const *const abs_base_dir, gchar const *const href,
* effic: Once we no longer consult sodipodi:absref, we can do
* `if (base unchanged) { return; }' at the start of rebase_hrefs.
*/
- g_free(ret);
- ret = g_strdup(sp_absref);
+ ret = sp_absref;
}
return ret;
}
-/**
- * Change relative xlink:href attributes to be relative to \a new_abs_base instead of old_abs_base.
- *
- * Note that old_abs_base and new_abs_base must each be non-NULL, absolute directory paths.
- */
Inkscape::Util::List<AttributeRecord const>
Inkscape::XML::rebase_href_attrs(gchar const *const old_abs_base,
gchar const *const new_abs_base,
@@ -115,6 +105,7 @@ Inkscape::XML::rebase_href_attrs(gchar const *const old_abs_base,
using Inkscape::Util::ptr_shared;
using Inkscape::Util::share_string;
+
if (old_abs_base == new_abs_base) {
return attributes;
}
@@ -133,7 +124,7 @@ Inkscape::XML::rebase_href_attrs(gchar const *const old_abs_base,
for (List<AttributeRecord const> ai(attributes); ai; ++ai) {
if (ai->key == href_key) {
old_href = ai->value;
- if (!href_needs_rebasing(old_href)) {
+ if (!href_needs_rebasing(static_cast<char const *>(old_href))) {
return attributes;
}
} else if (ai->key == absref_key) {
@@ -153,23 +144,33 @@ Inkscape::XML::rebase_href_attrs(gchar const *const old_abs_base,
* reversed.) */
}
- gchar *const abs_href(calc_abs_href(old_abs_base, old_href, sp_absref));
- gchar const *const new_href = sp_relative_path_from_path(abs_href, new_abs_base);
- ret = cons(AttributeRecord(href_key, share_string(new_href)), ret);
+ std::string abs_href = calc_abs_href(old_abs_base, static_cast<char const *>(old_href), sp_absref);
+ std::string new_href = sp_relative_path_from_path(abs_href, new_abs_base);
+ ret = cons(AttributeRecord(href_key, share_string(new_href.c_str())), ret); // Check if this is safe/copied or if it is only held.
if (sp_absref) {
/* We assume that if there wasn't previously a sodipodi:absref attribute
* then we shouldn't create one. */
- ret = cons(AttributeRecord(absref_key, ( streq(abs_href, sp_absref)
+ ret = cons(AttributeRecord(absref_key, ( streq(abs_href.c_str(), sp_absref)
? sp_absref
- : share_string(abs_href) )),
+ : share_string(abs_href.c_str()) )),
ret);
}
- g_free(abs_href);
+
return ret;
}
-gchar *
-Inkscape::XML::calc_abs_doc_base(gchar const *const doc_base)
+// std::string Inkscape::XML::rebase_href_attrs( std::string const &oldAbsBase, std::string const &newAbsBase, gchar const * /*href*/, gchar const */*absref*/ )
+// {
+// std::string ret;
+// //g_message( "XX need to flip from [%s] to [%s]", oldAbsBase.c_str(), newAbsBase.c_str() );
+
+// if ( oldAbsBase != newAbsBase ) {
+// }
+
+// return ret;
+// }
+
+std::string Inkscape::XML::calc_abs_doc_base(gchar const *doc_base)
{
/* Note that we don't currently try to handle the case of doc_base containing
* `..' or `.' path components. This non-handling means that sometimes
@@ -179,34 +180,27 @@ Inkscape::XML::calc_abs_doc_base(gchar const *const doc_base)
* relative URL/IRI href processing (with liburiparser).
*
* (Note that one possibile difficulty with `..' is symlinks.) */
+ std::string ret;
if (!doc_base) {
- return g_get_current_dir();
- } else if (g_path_is_absolute(doc_base)) {
- return g_strdup(doc_base);
+ ret = Glib::get_current_dir();
+ } else if (Glib::path_is_absolute(doc_base)) {
+ ret = doc_base;
} else {
- gchar *const cwd = g_get_current_dir();
- gchar *const ret = g_build_filename(cwd, doc_base, NULL);
- g_free(cwd);
- return ret;
+ ret = Glib::build_filename( Glib::get_current_dir(), doc_base );
}
+
+ return ret;
}
-/**
- * Change relative hrefs in doc to be relative to \a new_base instead of doc.base.
- *
- * (NULL doc base or new_base is interpreted as current working directory.)
- *
- * \param spns True iff doc should contain sodipodi:absref attributes.
- */
void Inkscape::XML::rebase_hrefs(SPDocument *const doc, gchar const *const new_base, bool const spns)
{
if (!doc->getBase()) {
return;
}
- gchar *const old_abs_base = calc_abs_doc_base(doc->getBase());
- gchar *const new_abs_base = calc_abs_doc_base(new_base);
+ std::string old_abs_base = calc_abs_doc_base(doc->getBase());
+ std::string new_abs_base = calc_abs_doc_base(new_base);
/* TODO: Should handle not just image but also:
*
@@ -232,22 +226,26 @@ void Inkscape::XML::rebase_hrefs(SPDocument *const doc, gchar const *const new_b
for (GSList const *l = images; l != NULL; l = l->next) {
Inkscape::XML::Node *ir = static_cast<SPObject *>(l->data)->getRepr();
- gchar * uri = g_strdup(ir->attribute("xlink:href"));
- if (!uri) {
- continue;
+ std::string uri;
+ {
+ gchar const *tmp = ir->attribute("xlink:href");
+ if ( !tmp ) {
+ continue;
+ }
+ uri = tmp;
}
- if (!strncmp(uri, "file://", 7)) {
- uri = g_strdup(g_filename_from_uri(ir->attribute("xlink:href"), NULL, NULL));
+ if ( uri.substr(0, 7) == "file://" ) {
+ uri = Glib::filename_from_uri(uri);
}
// The following two cases are for absolute hrefs that can be converted to relative.
// Imported images, first time rebased, need an old base.
- gchar * href = uri;
- if (g_path_is_absolute(href)) {
- href = (gchar *) sp_relative_path_from_path(uri, old_abs_base);
+ std::string href = uri;
+ if ( Glib::path_is_absolute(href) ) {
+ href = sp_relative_path_from_path(uri, old_abs_base);
}
// Files moved from a absolute path need a new one.
- if (g_path_is_absolute(href)) {
- href = (gchar *) sp_relative_path_from_path(uri, new_abs_base);
+ if ( Glib::path_is_absolute(href) ) {
+ href = sp_relative_path_from_path(uri, new_abs_base);
}
// Other bitmaps are either really absolute, or already relative.
@@ -264,52 +262,41 @@ void Inkscape::XML::rebase_hrefs(SPDocument *const doc, gchar const *const new_b
* changing non-file hrefs), which breaks if href starts with a scheme or if href contains
* any escaping. */
- if (!href || !href_needs_rebasing(href)) {
- g_free(uri);
- continue;
- }
-
- gchar *const abs_href(calc_abs_href(old_abs_base, href, ir->attribute("sodipodi:absref")));
-
- /* todo: One difficult case once we support writing to non-file locations is where
- * existing hrefs in the document point to local files. In this case, we should
- * probably copy those referenced files to the new location at the same time. It's
- * less clear what to do when copying from one non-file location to another. We may
- * need to ask the user in some way (even if it's as a checkbox), but we'd like to
- * bother the user as little as possible yet also want to warn the user about the case
- * of file hrefs. */
-
- gchar const *const new_href = sp_relative_path_from_path(abs_href, new_abs_base);
- ir->setAttribute("sodipodi:absref", ( spns
- ? abs_href
- : NULL ));
- if (!g_path_is_absolute(new_href)) {
+ if ( href_needs_rebasing(href) ) {
+ std::string abs_href = calc_abs_href(old_abs_base, href, ir->attribute("sodipodi:absref"));
+
+ /* todo: One difficult case once we support writing to non-file locations is where
+ * existing hrefs in the document point to local files. In this case, we should
+ * probably copy those referenced files to the new location at the same time. It's
+ * less clear what to do when copying from one non-file location to another. We may
+ * need to ask the user in some way (even if it's as a checkbox), but we'd like to
+ * bother the user as little as possible yet also want to warn the user about the case
+ * of file hrefs. */
+
+ std::string new_href = sp_relative_path_from_path(abs_href, new_abs_base);
+ ir->setAttribute("sodipodi:absref", ( spns
+ ? abs_href.c_str()
+ : NULL ));
+ if (!Glib::path_is_absolute(new_href)) {
#ifdef WIN32
- /* Native Windows path separators are replaced with / so that the href
- * also works on Gnu/Linux and OSX */
- ir->setAttribute("xlink:href", g_strdelimit((gchar *) new_href, "\\", '/'));
+ /* Native Windows path separators are replaced with / so that the href
+ * also works on Gnu/Linux and OSX */
+ ir->setAttribute("xlink:href", g_strdelimit(new_href.c_str(), "\\", '/'));
#else
- ir->setAttribute("xlink:href", new_href);
+ ir->setAttribute("xlink:href", new_href.c_str());
#endif
- } else {
- ir->setAttribute("xlink:href", g_filename_to_uri((gchar *) new_href, NULL, NULL));
- }
+ } else {
+ ir->setAttribute("xlink:href", g_filename_to_uri(new_href.c_str(), NULL, NULL));
+ }
- /* impl: I assume that if !spns then any existing sodipodi:absref is about to get
- * cleared (or is already cleared) anyway, in which case it doesn't matter whether we
- * clear or leave any existing sodipodi:absref value. If that assumption turns out to
- * be wrong, then leaving it means risking leaving the wrong value (if xlink:href
- * referred to a different file than sodipodi:absref) while clearing it means risking
- * losing information. */
-
- g_free(uri);
- // (No need to free href, it's guaranteed to point into uri.)
- g_free(abs_href);
- // (No need to free new_href, it's guaranteed to point into abs_href.)
+ /* impl: I assume that if !spns then any existing sodipodi:absref is about to get
+ * cleared (or is already cleared) anyway, in which case it doesn't matter whether we
+ * clear or leave any existing sodipodi:absref value. If that assumption turns out to
+ * be wrong, then leaving it means risking leaving the wrong value (if xlink:href
+ * referred to a different file than sodipodi:absref) while clearing it means risking
+ * losing information. */
+ }
}
-
- g_free(new_abs_base);
- g_free(old_abs_base);
}
diff --git a/src/xml/rebase-hrefs.h b/src/xml/rebase-hrefs.h
index b4f288c4d..4cbdec9a5 100644
--- a/src/xml/rebase-hrefs.h
+++ b/src/xml/rebase-hrefs.h
@@ -9,17 +9,36 @@ struct SPDocument;
namespace Inkscape {
namespace XML {
-gchar *calc_abs_doc_base(gchar const *doc_base);
-
+std::string calc_abs_doc_base(gchar const *doc_base);
+
+/**
+ * Change relative hrefs in doc to be relative to \a new_base instead of doc.base.
+ *
+ * (NULL doc base or new_base is interpreted as current working directory.)
+ *
+ * @param spns True if doc should contain sodipodi:absref attributes.
+ */
void rebase_hrefs(SPDocument *doc, gchar const *new_base, bool spns);
+/**
+ * Change relative xlink:href attributes to be relative to \a new_abs_base instead of old_abs_base.
+ *
+ * Note that old_abs_base and new_abs_base must each be non-NULL, absolute directory paths.
+ */
Inkscape::Util::List<AttributeRecord const> rebase_href_attrs(
gchar const *old_abs_base,
gchar const *new_abs_base,
Inkscape::Util::List<AttributeRecord const> attributes);
-}
-}
+
+// /**
+// * .
+// * @return a non-empty replacement href if needed, empty otherwise.
+// */
+// std::string rebase_href_attrs( std::string const &oldAbsBase, std::string const &newAbsBase, gchar const *href, gchar const *absref = 0 );
+
+} // namespace XML
+} // namespace Inkscape
#endif /* !REBASE_HREFS_H_SEEN */
diff --git a/src/xml/repr-action-test.h b/src/xml/repr-action-test.h
index afc9b2c46..ae4291397 100644
--- a/src/xml/repr-action-test.h
+++ b/src/xml/repr-action-test.h
@@ -88,7 +88,6 @@ public:
}
/* lots more tests needed ... */
-
};
/*
diff --git a/src/xml/repr-io.cpp b/src/xml/repr-io.cpp
index 5f7654ba8..2a0bb6ce8 100644
--- a/src/xml/repr-io.cpp
+++ b/src/xml/repr-io.cpp
@@ -52,6 +52,7 @@ static void sp_repr_write_stream_root_element(Node *repr, Writer &out,
int inlineattrs, int indent,
gchar const *old_href_abs_base,
gchar const *new_href_abs_base);
+
static void sp_repr_write_stream_element(Node *repr, Writer &out,
gint indent_level, bool add_whitespace,
Glib::QueryQuark elide_prefix,
@@ -644,7 +645,7 @@ sp_repr_save_rebased_file(Document *doc, gchar const *const filename, gchar cons
return false;
}
- gchar *old_href_abs_base = NULL;
+ std::string old_href_abs_base;
gchar *new_href_abs_base = NULL;
if (for_filename) {
old_href_abs_base = calc_abs_doc_base(old_base);
@@ -662,9 +663,8 @@ sp_repr_save_rebased_file(Document *doc, gchar const *const filename, gchar cons
* to using sodipodi:absref instead of the xlink:href value,
* then we should do `if streq() { free them and set both to NULL; }'. */
}
- sp_repr_save_stream(doc, file, default_ns, compress, old_href_abs_base, new_href_abs_base);
+ sp_repr_save_stream(doc, file, default_ns, compress, old_href_abs_base.c_str(), new_href_abs_base);
- g_free(old_href_abs_base);
g_free(new_href_abs_base);
if (fclose (file) != 0) {
@@ -879,17 +879,16 @@ void sp_repr_write_stream( Node *repr, Writer &out, gint indent_level,
}
-static void
-sp_repr_write_stream_element (Node * repr, Writer & out, gint indent_level,
- bool add_whitespace,
- Glib::QueryQuark elide_prefix,
- List<AttributeRecord const> attributes,
- int inlineattrs, int indent,
- gchar const *const old_href_base,
- gchar const *const new_href_base)
+void sp_repr_write_stream_element( Node * repr, Writer & out,
+ gint indent_level, bool add_whitespace,
+ Glib::QueryQuark elide_prefix,
+ List<AttributeRecord const> attributes,
+ int inlineattrs, int indent,
+ gchar const *old_href_base,
+ gchar const *new_href_base )
{
- Node *child;
- bool loose;
+ Node *child = 0;
+ bool loose = false;
g_return_if_fail (repr != NULL);
@@ -921,6 +920,28 @@ sp_repr_write_stream_element (Node * repr, Writer & out, gint indent_level,
add_whitespace = false;
}
+
+ {
+ GQuark const href_key = g_quark_from_static_string("xlink:href");
+ GQuark const absref_key = g_quark_from_static_string("sodipodi:absref");
+
+ gchar const *xxHref = 0;
+ gchar const *xxAbsref = 0;
+ for ( List<AttributeRecord const> ai(attributes); ai; ++ai ) {
+ if ( ai->key == href_key ) {
+ xxHref = ai->value;
+ } else if ( ai->key == absref_key ) {
+ xxAbsref = ai->value;
+ }
+ }
+
+ // Might add a special case for absref but no href.
+ if ( old_href_base && new_href_base && xxHref ) {
+ //g_message("href rebase test with [%s] and [%s]", xxHref, xxAbsref);
+ //std::string newOne = rebase_href_attrs( old_href_base, new_href_base, xxHref, xxAbsref );
+ }
+ }
+
for ( List<AttributeRecord const> iter = rebase_href_attrs(old_href_base, new_href_base,
attributes);
iter ; ++iter )
diff --git a/src/xml/repr.h b/src/xml/repr.h
index bde3e533f..5fa9387c7 100644
--- a/src/xml/repr.h
+++ b/src/xml/repr.h
@@ -79,10 +79,13 @@ void sp_repr_write_stream(Inkscape::XML::Node *repr, Inkscape::IO::Writer &out,
gchar const *new_href_base = NULL);
Inkscape::XML::Document *sp_repr_read_buf (const Glib::ustring &buf, const gchar *default_ns);
Glib::ustring sp_repr_save_buf(Inkscape::XML::Document *doc);
+
+// TODO convert to std::string
void sp_repr_save_stream(Inkscape::XML::Document *doc, FILE *to_file,
gchar const *default_ns = NULL, bool compress = false,
gchar const *old_href_base = NULL,
gchar const *new_href_base = NULL);
+
bool sp_repr_save_file(Inkscape::XML::Document *doc, gchar const *filename, gchar const *default_ns=NULL);
bool sp_repr_save_rebased_file(Inkscape::XML::Document *doc, gchar const *filename_utf8,
gchar const *default_ns,