summaryrefslogtreecommitdiffstats
path: root/src/extension/system.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:24:48 +0000
commitcca66b9ec4e494c1d919bff0f71a820d8afab1fa (patch)
tree146f39ded1c938019e1ed42d30923c2ac9e86789 /src/extension/system.cpp
parentInitial commit. (diff)
downloadinkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.tar.xz
inkscape-cca66b9ec4e494c1d919bff0f71a820d8afab1fa.zip
Adding upstream version 1.2.2.upstream/1.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/extension/system.cpp')
-rw-r--r--src/extension/system.cpp710
1 files changed, 710 insertions, 0 deletions
diff --git a/src/extension/system.cpp b/src/extension/system.cpp
new file mode 100644
index 0000000..f4ba2dc
--- /dev/null
+++ b/src/extension/system.cpp
@@ -0,0 +1,710 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * This is file is kind of the junk file. Basically everything that
+ * didn't fit in one of the other well defined areas, well, it's now
+ * here. Which is good in someways, but this file really needs some
+ * definition. Hopefully that will come ASAP.
+ *
+ * Authors:
+ * Ted Gould <ted@gould.cx>
+ * Johan Engelen <johan@shouraizou.nl>
+ * Jon A. Cruz <jon@joncruz.org>
+ * Abhishek Sharma
+ *
+ * Copyright (C) 2006-2007 Johan Engelen
+ * Copyright (C) 2002-2004 Ted Gould
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "ui/interface.h"
+
+#include "system.h"
+#include "preferences.h"
+#include "extension.h"
+#include "db.h"
+#include "input.h"
+#include "output.h"
+#include "effect.h"
+#include "patheffect.h"
+#include "print.h"
+#include "implementation/script.h"
+#include "implementation/xslt.h"
+#include "xml/rebase-hrefs.h"
+#include "io/sys.h"
+#include "inkscape.h"
+#include "document-undo.h"
+#include "loader.h"
+
+#include <glibmm/miscutils.h>
+
+namespace Inkscape {
+namespace Extension {
+
+static void open_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
+static void save_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
+
+/**
+ * \return A new document created from the filename passed in
+ * \brief This is a generic function to use the open function of
+ * a module (including Autodetect)
+ * \param key Identifier of which module to use
+ * \param filename The file that should be opened
+ *
+ * First things first, are we looking at an autodetection? Well if that's the case then the module
+ * needs to be found, and that is done with a database lookup through the module DB. The foreach
+ * function is called, with the parameter being a gpointer array. It contains both the filename
+ * (to find its extension) and where to write the module when it is found.
+ *
+ * If there is no autodetection, then the module database is queried with the key given.
+ *
+ * If everything is cool at this point, the module is loaded, and there is possibility for
+ * preferences. If there is a function, then it is executed to get the dialog to be displayed.
+ * After it is finished the function continues.
+ *
+ * Lastly, the open function is called in the module itself.
+ */
+SPDocument *open(Extension *key, gchar const *filename)
+{
+ Input *imod = nullptr;
+
+ if (key == nullptr) {
+ gpointer parray[2];
+ parray[0] = (gpointer)filename;
+ parray[1] = (gpointer)&imod;
+ db.foreach(open_internal, (gpointer)&parray);
+ } else {
+ imod = dynamic_cast<Input *>(key);
+ }
+
+ bool last_chance_svg = false;
+ if (key == nullptr && imod == nullptr) {
+ last_chance_svg = true;
+ imod = dynamic_cast<Input *>(db.get(SP_MODULE_KEY_INPUT_SVG));
+ }
+
+ if (imod == nullptr) {
+ throw Input::no_extension_found();
+ }
+
+ // Hide pixbuf extensions depending on user preferences.
+ //g_warning("Extension: %s", imod->get_id());
+
+ bool show = true;
+ if (strlen(imod->get_id()) > 21) {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ bool ask = prefs->getBool("/dialogs/import/ask");
+ bool ask_svg = prefs->getBool("/dialogs/import/ask_svg");
+ Glib::ustring id = Glib::ustring(imod->get_id(), 22);
+ if (id.compare("org.inkscape.input.svg") == 0) {
+ if (ask_svg && prefs->getBool("/options/onimport", false)) {
+ show = true;
+ imod->set_gui(true);
+ } else {
+ show = false;
+ imod->set_gui(false);
+ }
+ } else if(strlen(imod->get_id()) > 27) {
+ id = Glib::ustring(imod->get_id(), 28);
+ if (!ask && id.compare( "org.inkscape.input.gdkpixbuf") == 0) {
+ show = false;
+ imod->set_gui(false);
+ }
+ }
+ }
+ imod->set_state(Extension::STATE_LOADED);
+
+ if (!imod->loaded()) {
+ throw Input::open_failed();
+ }
+
+ if (!imod->prefs(filename)) {
+ throw Input::open_cancelled();
+ }
+
+ SPDocument *doc = imod->open(filename);
+
+ if (!doc) {
+ if (last_chance_svg) {
+ if ( INKSCAPE.use_gui() ) {
+ sp_ui_error_dialog(_("Could not detect file format. Tried to open it as an SVG anyway but this also failed."));
+ } else {
+ g_warning("%s", _("Could not detect file format. Tried to open it as an SVG anyway but this also failed."));
+ }
+ }
+ throw Input::open_failed();
+ }
+ // If last_chance_svg is true here, it means we successfully opened a file as an svg
+ // and there's no need to warn the user about it, just do it.
+
+ doc->setDocumentFilename(filename);
+ if (!show) {
+ imod->set_gui(true);
+ }
+
+ return doc;
+}
+
+/**
+ * \return none
+ * \brief This is the function that searches each module to see
+ * if it matches the filename for autodetection.
+ * \param in_plug The module to be tested
+ * \param in_data An array of pointers containing the filename, and
+ * the place to put a successfully found module.
+ *
+ * Basically this function only looks at input modules as it is part of the open function. If the
+ * module is an input module, it then starts to take it apart, and the data that is passed in.
+ * Because the data being passed in is in such a weird format, there are a few casts to make it
+ * easier to use. While it looks like a lot of local variables, they'll all get removed by the
+ * compiler.
+ *
+ * First thing that is checked is if the filename is shorter than the extension itself. There is
+ * no way for a match in that case. If it's long enough then there is a string compare of the end
+ * of the filename (for the length of the extension), and the extension itself. If this passes
+ * then the pointer passed in is set to the current module.
+ */
+static void
+open_internal(Extension *in_plug, gpointer in_data)
+{
+ auto imod = dynamic_cast<Input *>(in_plug);
+ if (imod && !imod->deactivated()) {
+ gpointer *parray = (gpointer *)in_data;
+ gchar const *filename = (gchar const *)parray[0];
+ Input **pimod = (Input **)parray[1];
+
+ // skip all the rest if we already found a function to open it
+ // since they're ordered by preference now.
+ if (!*pimod) {
+ gchar const *ext = imod->get_extension();
+
+ gchar *filenamelower = g_utf8_strdown(filename, -1);
+ gchar *extensionlower = g_utf8_strdown(ext, -1);
+
+ if (g_str_has_suffix(filenamelower, extensionlower)) {
+ *pimod = imod;
+ }
+
+ g_free(filenamelower);
+ g_free(extensionlower);
+ }
+ }
+
+ return;
+}
+
+/**
+ * \return None
+ * \brief This is a generic function to use the save function of
+ * a module (including Autodetect)
+ * \param key Identifier of which module to use
+ * \param doc The document to be saved
+ * \param filename The file that the document should be saved to
+ * \param official (optional) whether to set :output_module and :modified in the
+ * document; is true for normal save, false for temporary saves
+ *
+ * First things first, are we looking at an autodetection? Well if that's the case then the module
+ * needs to be found, and that is done with a database lookup through the module DB. The foreach
+ * function is called, with the parameter being a gpointer array. It contains both the filename
+ * (to find its extension) and where to write the module when it is found.
+ *
+ * If there is no autodetection the module database is queried with the key given.
+ *
+ * If everything is cool at this point, the module is loaded, and there is possibility for
+ * preferences. If there is a function, then it is executed to get the dialog to be displayed.
+ * After it is finished the function continues.
+ *
+ * Lastly, the save function is called in the module itself.
+ */
+void
+save(Extension *key, SPDocument *doc, gchar const *filename, bool check_overwrite, bool official,
+ Inkscape::Extension::FileSaveMethod save_method)
+{
+ Output *omod;
+ if (key == nullptr) {
+ gpointer parray[2];
+ parray[0] = (gpointer)filename;
+ parray[1] = (gpointer)&omod;
+ omod = nullptr;
+ db.foreach(save_internal, (gpointer)&parray);
+
+ /* This is a nasty hack, but it is required to ensure that
+ autodetect will always save with the Inkscape extensions
+ if they are available. */
+ if (omod != nullptr && !strcmp(omod->get_id(), SP_MODULE_KEY_OUTPUT_SVG)) {
+ omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
+ }
+ /* If autodetect fails, save as Inkscape SVG */
+ if (omod == nullptr) {
+ // omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE)); use exception and let user choose
+ }
+ } else {
+ omod = dynamic_cast<Output *>(key);
+ }
+
+ if (!dynamic_cast<Output *>(omod)) {
+ g_warning("Unable to find output module to handle file: %s\n", filename);
+ throw Output::no_extension_found();
+ }
+
+ omod->set_state(Extension::STATE_LOADED);
+ if (!omod->loaded()) {
+ throw Output::save_failed();
+ }
+
+ if (!omod->prefs()) {
+ throw Output::save_cancelled();
+ }
+
+ gchar *fileName = g_strdup(filename);
+
+ if (check_overwrite && !sp_ui_overwrite_file(fileName)) {
+ g_free(fileName);
+ throw Output::no_overwrite();
+ }
+
+ // test if the file exists and is writable
+ // the test only checks the file attributes and might pass where ACL does not allow writes
+ if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS) && !Inkscape::IO::file_is_writable(filename)) {
+ g_free(fileName);
+ throw Output::file_read_only();
+ }
+
+ Inkscape::XML::Node *repr = doc->getReprRoot();
+
+
+ // remember attributes in case this is an unofficial save and/or overwrite fails
+ gchar *saved_filename = g_strdup(doc->getDocumentFilename());
+ gchar *saved_output_extension = nullptr;
+ gchar *saved_dataloss = nullptr;
+ bool saved_modified = doc->isModifiedSinceSave();
+ saved_output_extension = g_strdup(get_file_save_extension(save_method).c_str());
+ saved_dataloss = g_strdup(repr->attribute("inkscape:dataloss"));
+ if (official) {
+ // The document is changing name/uri.
+ doc->changeFilenameAndHrefs(fileName);
+ }
+
+ // Update attributes:
+ {
+ {
+ DocumentUndo::ScopedInsensitive _no_undo(doc);
+ // also save the extension for next use
+ store_file_extension_in_prefs (omod->get_id(), save_method);
+ // set the "dataloss" attribute if the chosen extension is lossy
+ repr->removeAttribute("inkscape:dataloss");
+ if (omod->causes_dataloss()) {
+ repr->setAttribute("inkscape:dataloss", "true");
+ }
+ }
+ doc->setModifiedSinceSave(false);
+ }
+
+ try {
+ omod->save(doc, fileName);
+ }
+ catch(...) {
+ // revert attributes in case of official and overwrite
+ if(check_overwrite && official) {
+ {
+ DocumentUndo::ScopedInsensitive _no_undo(doc);
+ store_file_extension_in_prefs (saved_output_extension, save_method);
+ repr->setAttribute("inkscape:dataloss", saved_dataloss);
+ }
+ doc->changeFilenameAndHrefs(saved_filename);
+ }
+ doc->setModifiedSinceSave(saved_modified);
+ // free used resources
+ g_free(saved_output_extension);
+ g_free(saved_dataloss);
+ g_free(saved_filename);
+
+ g_free(fileName);
+
+ throw;
+ }
+
+ // If it is an unofficial save, set the modified attributes back to what they were.
+ if ( !official) {
+ {
+ DocumentUndo::ScopedInsensitive _no_undo(doc);
+ store_file_extension_in_prefs (saved_output_extension, save_method);
+ repr->setAttribute("inkscape:dataloss", saved_dataloss);
+ }
+ doc->setModifiedSinceSave(saved_modified);
+
+ g_free(saved_output_extension);
+ g_free(saved_dataloss);
+ }
+
+ g_free(fileName);
+ return;
+}
+
+/**
+ * \return none
+ * \brief This is the function that searches each module to see
+ * if it matches the filename for autodetection.
+ * \param in_plug The module to be tested
+ * \param in_data An array of pointers containing the filename, and
+ * the place to put a successfully found module.
+ *
+ * Basically this function only looks at output modules as it is part of the open function. If the
+ * module is an output module, it then starts to take it apart, and the data that is passed in.
+ * Because the data being passed in is in such a weird format, there are a few casts to make it
+ * easier to use. While it looks like a lot of local variables, they'll all get removed by the
+ * compiler.
+ *
+ * First thing that is checked is if the filename is shorter than the extension itself. There is
+ * no way for a match in that case. If it's long enough then there is a string compare of the end
+ * of the filename (for the length of the extension), and the extension itself. If this passes
+ * then the pointer passed in is set to the current module.
+ */
+static void
+save_internal(Extension *in_plug, gpointer in_data)
+{
+ auto omod = dynamic_cast<Output *>(in_plug);
+ if (omod && !omod->deactivated()) {
+ gpointer *parray = (gpointer *)in_data;
+ gchar const *filename = (gchar const *)parray[0];
+ Output **pomod = (Output **)parray[1];
+
+ // skip all the rest if we already found someone to save it
+ // since they're ordered by preference now.
+ if (!*pomod) {
+ gchar const *ext = omod->get_extension();
+
+ gchar *filenamelower = g_utf8_strdown(filename, -1);
+ gchar *extensionlower = g_utf8_strdown(ext, -1);
+
+ if (g_str_has_suffix(filenamelower, extensionlower)) {
+ *pomod = omod;
+ }
+
+ g_free(filenamelower);
+ g_free(extensionlower);
+ }
+ }
+
+ return;
+}
+
+Print *
+get_print(gchar const *key)
+{
+ return dynamic_cast<Print *>(db.get(key));
+}
+
+/**
+ * \return true if extension successfully parsed, false otherwise
+ * A true return value does not guarantee an extension was actually registered,
+ * but indicates no errors occurred while parsing the extension.
+ * \brief Creates a module from a Inkscape::XML::Document describing the module
+ * \param doc The XML description of the module
+ *
+ * This function basically has two segments. The first is that it goes through the Repr tree
+ * provided, and determines what kind of module this is, and what kind of implementation to use.
+ * All of these are then stored in two enums that are defined in this function. This makes it
+ * easier to add additional types (which will happen in the future, I'm sure).
+ *
+ * Second, there is case statements for these enums. The first one is the type of module. This is
+ * the one where the module is actually created. After that, then the implementation is applied to
+ * get the load and unload functions. If there is no implementation then these are not set. This
+ * case could apply to modules that are built in (like the SVG load/save functions).
+ */
+bool
+build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp, std::string* baseDir)
+{
+ ModuleImpType module_implementation_type = MODULE_UNKNOWN_IMP;
+ ModuleFuncType module_functional_type = MODULE_UNKNOWN_FUNC;
+
+ g_return_val_if_fail(doc != nullptr, false);
+
+ Inkscape::XML::Node *repr = doc->root();
+
+ if (strcmp(repr->name(), INKSCAPE_EXTENSION_NS "inkscape-extension")) {
+ g_warning("Extension definition started with <%s> instead of <" INKSCAPE_EXTENSION_NS "inkscape-extension>. Extension will not be created. See http://wiki.inkscape.org/wiki/index.php/Extensions for reference.\n", repr->name());
+ return false;
+ }
+
+ Inkscape::XML::Node *child_repr = repr->firstChild();
+ while (child_repr != nullptr) {
+ char const *element_name = child_repr->name();
+ /* printf("Child: %s\n", child_repr->name()); */
+ if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "input")) {
+ module_functional_type = MODULE_INPUT;
+ } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "output")) {
+ module_functional_type = MODULE_OUTPUT;
+ } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "effect")) {
+ module_functional_type = MODULE_FILTER;
+ } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "print")) {
+ module_functional_type = MODULE_PRINT;
+ } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "path-effect")) {
+ module_functional_type = MODULE_PATH_EFFECT;
+ } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "script")) {
+ module_implementation_type = MODULE_EXTENSION;
+ } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "xslt")) {
+ module_implementation_type = MODULE_XSLT;
+ } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "plugin")) {
+ module_implementation_type = MODULE_PLUGIN;
+ }
+
+ //Inkscape::XML::Node *old_repr = child_repr;
+ child_repr = child_repr->next();
+ //Inkscape::GC::release(old_repr);
+ }
+
+ Implementation::Implementation *imp;
+ if (in_imp == nullptr) {
+ switch (module_implementation_type) {
+ case MODULE_EXTENSION: {
+ Implementation::Script *script = new Implementation::Script();
+ imp = static_cast<Implementation::Implementation *>(script);
+ break;
+ }
+ case MODULE_XSLT: {
+ Implementation::XSLT *xslt = new Implementation::XSLT();
+ imp = static_cast<Implementation::Implementation *>(xslt);
+ break;
+ }
+ case MODULE_PLUGIN: {
+ Inkscape::Extension::Loader loader = Inkscape::Extension::Loader();
+ if( baseDir != nullptr){
+ loader.set_base_directory ( *baseDir );
+ }
+ imp = loader.load_implementation(doc);
+ break;
+ }
+ default: {
+ imp = nullptr;
+ break;
+ }
+ }
+ } else {
+ imp = in_imp;
+ }
+
+ Extension *module = nullptr;
+ try {
+ switch (module_functional_type) {
+ case MODULE_INPUT: {
+ module = new Input(repr, imp, baseDir);
+ break;
+ }
+ case MODULE_OUTPUT: {
+ module = new Output(repr, imp, baseDir);
+ break;
+ }
+ case MODULE_FILTER: {
+ module = new Effect(repr, imp, baseDir);
+ break;
+ }
+ case MODULE_PRINT: {
+ module = new Print(repr, imp, baseDir);
+ break;
+ }
+ case MODULE_PATH_EFFECT: {
+ module = new PathEffect(repr, imp, baseDir);
+ break;
+ }
+ default: {
+ g_warning("Extension of unknown type!"); // TODO: Should not happen! Is this even useful?
+ module = new Extension(repr, imp, baseDir);
+ break;
+ }
+ }
+ } catch (const Extension::extension_no_id& e) {
+ g_warning("Building extension failed. Extension does not have a valid ID");
+ } catch (const Extension::extension_no_name& e) {
+ g_warning("Building extension failed. Extension does not have a valid name");
+ } catch (const Extension::extension_not_compatible& e) {
+ return true; // This is not an actual error; just silently ignore the extension
+ }
+
+ if (module) {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * \brief This function creates a module from a filename of an
+ * XML description.
+ * \param filename The file holding the XML description of the module.
+ *
+ * This function calls build_from_reprdoc with using sp_repr_read_file to create the reprdoc.
+ */
+void
+build_from_file(gchar const *filename)
+{
+ std::string dir = Glib::path_get_dirname(filename);
+
+ Inkscape::XML::Document *doc = sp_repr_read_file(filename, INKSCAPE_EXTENSION_URI);
+ if (!doc) {
+ g_critical("Inkscape::Extension::build_from_file() - XML description loaded from '%s' not valid.", filename);
+ return;
+ }
+
+ if (!build_from_reprdoc(doc, nullptr, &dir)) {
+ g_warning("Inkscape::Extension::build_from_file() - Could not parse extension from '%s'.", filename);
+ }
+
+ Inkscape::GC::release(doc);
+}
+
+/**
+ * \brief This function creates a module from a buffer holding an
+ * XML description.
+ * \param buffer The buffer holding the XML description of the module.
+ *
+ * This function calls build_from_reprdoc with using sp_repr_read_mem to create the reprdoc. It
+ * finds the length of the buffer using strlen.
+ */
+void
+build_from_mem(gchar const *buffer, Implementation::Implementation *in_imp)
+{
+ Inkscape::XML::Document *doc = sp_repr_read_mem(buffer, strlen(buffer), INKSCAPE_EXTENSION_URI);
+ if (!doc) {
+ g_critical("Inkscape::Extension::build_from_mem() - XML description loaded from memory buffer not valid.");
+ return;
+ }
+
+ if (!build_from_reprdoc(doc, in_imp, nullptr)) {
+ g_critical("Inkscape::Extension::build_from_mem() - Could not parse extension from memory buffer.");
+ }
+
+ Inkscape::GC::release(doc);
+}
+
+/*
+ * TODO: Is it guaranteed that the returned extension is valid? If so, we can remove the check for
+ * filename_extension in sp_file_save_dialog().
+ */
+Glib::ustring
+get_file_save_extension (Inkscape::Extension::FileSaveMethod method) {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring extension;
+ switch (method) {
+ case FILE_SAVE_METHOD_SAVE_AS:
+ case FILE_SAVE_METHOD_TEMPORARY:
+ extension = prefs->getString("/dialogs/save_as/default");
+ break;
+ case FILE_SAVE_METHOD_SAVE_COPY:
+ extension = prefs->getString("/dialogs/save_copy/default");
+ break;
+ case FILE_SAVE_METHOD_INKSCAPE_SVG:
+ extension = SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE;
+ break;
+ case FILE_SAVE_METHOD_EXPORT:
+ /// \todo no default extension set for Export? defaults to SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE is ok?
+ break;
+ }
+
+ if(extension.empty()) {
+ extension = SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE;
+ }
+
+ return extension;
+}
+
+Glib::ustring
+get_file_save_path (SPDocument *doc, FileSaveMethod method) {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ Glib::ustring path;
+ bool use_current_dir = true;
+ switch (method) {
+ case FILE_SAVE_METHOD_SAVE_AS:
+ {
+ use_current_dir = prefs->getBool("/dialogs/save_as/use_current_dir", true);
+ if (doc->getDocumentFilename() && use_current_dir) {
+ path = Glib::path_get_dirname(doc->getDocumentFilename());
+ } else {
+ path = prefs->getString("/dialogs/save_as/path");
+ }
+ break;
+ }
+ case FILE_SAVE_METHOD_TEMPORARY:
+ path = prefs->getString("/dialogs/save_as/path");
+ break;
+ case FILE_SAVE_METHOD_SAVE_COPY:
+ use_current_dir = prefs->getBool("/dialogs/save_copy/use_current_dir", prefs->getBool("/dialogs/save_as/use_current_dir", true));
+ if (doc->getDocumentFilename() && use_current_dir) {
+ path = Glib::path_get_dirname(doc->getDocumentFilename());
+ } else {
+ path = prefs->getString("/dialogs/save_copy/path");
+ }
+ break;
+ case FILE_SAVE_METHOD_INKSCAPE_SVG:
+ if (doc->getDocumentFilename()) {
+ path = Glib::path_get_dirname(doc->getDocumentFilename());
+ } else {
+ // FIXME: should we use the save_as path here or something else? Maybe we should
+ // leave this as a choice to the user.
+ path = prefs->getString("/dialogs/save_as/path");
+ }
+ break;
+ case FILE_SAVE_METHOD_EXPORT:
+ /// \todo no default path set for Export?
+ // defaults to g_get_home_dir()
+ break;
+ }
+
+ if(path.empty()) {
+ path = g_get_home_dir(); // Is this the most sensible solution? Note that we should avoid
+ // g_get_current_dir because this leads to problems on OS X where
+ // Inkscape opens the dialog inside application bundle when it is
+ // invoked for the first teim.
+ }
+
+ return path;
+}
+
+void
+store_file_extension_in_prefs (Glib::ustring extension, FileSaveMethod method) {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ switch (method) {
+ case FILE_SAVE_METHOD_SAVE_AS:
+ case FILE_SAVE_METHOD_TEMPORARY:
+ prefs->setString("/dialogs/save_as/default", extension);
+ break;
+ case FILE_SAVE_METHOD_SAVE_COPY:
+ prefs->setString("/dialogs/save_copy/default", extension);
+ break;
+ case FILE_SAVE_METHOD_INKSCAPE_SVG:
+ case FILE_SAVE_METHOD_EXPORT:
+ // do nothing
+ break;
+ }
+}
+
+void
+store_save_path_in_prefs (Glib::ustring path, FileSaveMethod method) {
+ Inkscape::Preferences *prefs = Inkscape::Preferences::get();
+ switch (method) {
+ case FILE_SAVE_METHOD_SAVE_AS:
+ case FILE_SAVE_METHOD_TEMPORARY:
+ prefs->setString("/dialogs/save_as/path", path);
+ break;
+ case FILE_SAVE_METHOD_SAVE_COPY:
+ prefs->setString("/dialogs/save_copy/path", path);
+ break;
+ case FILE_SAVE_METHOD_INKSCAPE_SVG:
+ case FILE_SAVE_METHOD_EXPORT:
+ // do nothing
+ break;
+ }
+}
+
+} } /* namespace Inkscape::Extension */
+
+/*
+ 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 :