Commit a4d0a358 authored by Jon A. Cruz's avatar Jon A. Cruz

Adding initial cut of resource manager.

(bzr r10198)
parent f8b778f9
......@@ -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 \
......
......@@ -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)
{
......
......@@ -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();
......
/** @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 :
......@@ -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:
......
......@@ -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)));
......
......@@ -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);
......
......@@ -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());
......
......@@ -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");
......
/*
* 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() {
}