diff options
| author | Krzysztof Kosi??ski <tweenk.pl@gmail.com> | 2008-09-16 17:15:22 +0000 |
|---|---|---|
| committer | tweenk <tweenk@users.sourceforge.net> | 2008-09-16 17:15:22 +0000 |
| commit | 9d87d30b72145fdee954992a9dc70f8c60174d7d (patch) | |
| tree | 194a94ece6ed668ad7dc529de2cdd09b7565c6fe /src/preferences.cpp | |
| parent | fix leak of the arena and arenaitem (diff) | |
| download | inkscape-9d87d30b72145fdee954992a9dc70f8c60174d7d.tar.gz inkscape-9d87d30b72145fdee954992a9dc70f8c60174d7d.zip | |
Refactored preferences handling into a new version of
the Inkscape::Preferences class. Removed all use of
prefs_get_string_attribute(), pref_path_get_nth_child() and
create_pref() in favor of the new API. Replaced some "0 or 1" integer
preferences with booleans.
(bzr r6823)
Diffstat (limited to 'src/preferences.cpp')
| -rw-r--r-- | src/preferences.cpp | 472 |
1 files changed, 421 insertions, 51 deletions
diff --git a/src/preferences.cpp b/src/preferences.cpp index fedfa8568..8bac7cf9d 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -1,85 +1,455 @@ -/** \file - * \brief Prefs handling implementation +/** @file + * @brief Singleton class to access the preferences file - implementation * * Authors: - * Ralf Stephan <ralf@ark.in-berlin.de> + * Krzysztof KosiĆski <tweenk.pl@gmail.com> * - * Copyright (C) 2005 Authors + * Copyright (C) 2008 Authors * * Released under GNU GPL. Read the file 'COPYING' for more information. */ -#ifdef HAVE_CONFIG_H -# include <config.h> -#endif - -#include <glibmm/i18n.h> - +#include "preferences.h" #include "preferences-skeleton.h" -#include "xml/repr.h" -#include "dialogs/input.h" #include "inkscape.h" -#include "preferences.h" - -#define PREFERENCES_FILE "preferences.xml" +#include "xml/repr.h" +#include "xml/node-observer.h" +#include <glibmm/fileutils.h> +#include <glibmm/i18n.h> +#include <glib.h> +#include <glib/gstdio.h> +#include <gtkmm/messagedialog.h> -static Inkscape::XML::Document *_preferences; -static bool _save_preferences; +#define PREFERENCES_FILE_NAME "preferences.xml" namespace Inkscape { -void -Preferences::loadSkeleton() +Preferences::Preferences() : + _prefs_basename(PREFERENCES_FILE_NAME), + _prefs_dir(""), + _prefs_filename(""), + _writable(false), + _prefs_doc(NULL) { - _preferences = sp_repr_read_mem (preferences_skeleton, PREFERENCES_SKELETON_SIZE, 0); + // profile_path essentailly returns the argument prefixed by the profile directory. + gchar *path = profile_path(NULL); + _prefs_dir = path; + g_free(path); + + path = profile_path(_prefs_basename.data()); + _prefs_filename = path; + g_free(path); + + _load(); } -Inkscape::XML::Document* -Preferences::get() +Preferences::~Preferences() { - return _preferences; + // when the preferences are unloaded, save them + save(); + Inkscape::GC::release(_prefs_doc); } /** - * Attempts to load the preferences file indicated by the global PREFERENCES_FILE - * parameter. If it cannot load it, the default preferences_skeleton will be used - * instead. + * @brief Load internal defaults + * + * In the future this will try to load the system-wide file before falling + * back to the internal defaults. */ -void -Preferences::load() +void Preferences::_loadDefaults() { - /// \todo this still uses old Gtk+ code which should be somewhere else - if (inkscape_load_config (PREFERENCES_FILE, - _preferences, - preferences_skeleton, - PREFERENCES_SKELETON_SIZE, - _("%s is not a regular file.\n%s"), - _("%s not a valid XML file, or\n" - "you don't have read permissions on it.\n%s"), - _("%s is not a valid preferences file.\n%s"), - _("Inkscape will run with default settings.\n" - "New settings will not be saved."))) - { - sp_input_load_from_preferences(); - _save_preferences = true; - } else - _save_preferences = false; + _prefs_doc = sp_repr_read_mem(preferences_skeleton, PREFERENCES_SKELETON_SIZE, NULL); } -void -Preferences::save() +/** + * @brief Load the user's customized preferences + * + * Tries to load the user's preferences.xml file. If there is none, creates it. + * Displays dialog boxes on any errors. + */ +void Preferences::_load() { - if (!_preferences || ! _save_preferences) + _loadDefaults(); + + Glib::ustring not_saved = _("Inkscape will run with default settings, " + "and new settings will not be saved. "); + + // 1. Does the file exist? + if (!g_file_test(_prefs_filename.data(), G_FILE_TEST_EXISTS)) { + // No - we need to create one. + // Does the profile directory exist? + if (!g_file_test(_prefs_dir.data(), G_FILE_TEST_EXISTS)) { + // No - create the profile directory + if (g_mkdir(_prefs_dir.data(), 0755)) { + // the creation failed + _errorDialog(Glib::ustring::compose(_("Cannot create profile directory %1."), + Glib::filename_to_utf8(_prefs_dir)), not_saved); + return; + } + // create some subdirectories for user stuff + char const *user_dirs[] = {"keys", "templates", "icons", "extensions", "palettes", NULL}; + for(int i=0; user_dirs[i]; ++i) { + char *dir = profile_path(user_dirs[i]); + g_mkdir(dir, 0755); + g_free(dir); + } + + } else if (!g_file_test(_prefs_dir.data(), G_FILE_TEST_IS_DIR)) { + // The profile dir is not actually a directory + _errorDialog(Glib::ustring::compose(_("%1 is not a valid directory."), + Glib::filename_to_utf8(_prefs_dir)), not_saved); + return; + } + // The profile dir exists and is valid. + if (!g_file_set_contents(_prefs_filename.data(), preferences_skeleton, PREFERENCES_SKELETON_SIZE, NULL)) { + // The write failed. + _errorDialog(Glib::ustring::compose(_("Failed to create the preferences file %1."), + Glib::filename_to_utf8(_prefs_filename)), not_saved); + return; + } + + // The prefs file was just created. + // We can return now and skip the rest of the load process. + _writable = true; + return; + } + + // Yes, the pref file exists. + // 2. Is it a regular file? + if (!g_file_test(_prefs_filename.data(), G_FILE_TEST_IS_REGULAR)) { + _errorDialog(Glib::ustring::compose(_("The preferences file %1 is not a regular file."), + Glib::filename_to_utf8(_prefs_filename)), not_saved); + return; + } + + // 3. Is the file readable? + gchar *prefs_xml = NULL; gsize len = 0; + if (!g_file_get_contents(_prefs_filename.data(), &prefs_xml, &len, NULL)) { + _errorDialog(Glib::ustring::compose(_("The preferences file %1 could not be read."), + Glib::filename_to_utf8(_prefs_filename)), not_saved); return; + } + // 4. Is it valid XML? + Inkscape::XML::Document *prefs_read = sp_repr_read_mem(prefs_xml, len, NULL); + g_free(prefs_xml); + if (!prefs_read) { + _errorDialog(Glib::ustring::compose(_("The preferences file %1 is not a valid XML document."), + Glib::filename_to_utf8(_prefs_filename)), not_saved); + return; + } + // 5. Basic sanity check: does the root element have a correct name? + if (strcmp(prefs_read->root()->name(), "inkscape")) { + _errorDialog(Glib::ustring::compose(_("The file %1 is not a valid Inkscape preferences file."), + Glib::filename_to_utf8(_prefs_filename)), not_saved); + Inkscape::GC::release(prefs_read); + return; + } + + // Merge the loaded prefs with defaults. + _prefs_doc->root()->mergeFrom(prefs_read->root(), "id"); + Inkscape::GC::release(prefs_read); + _writable = true; +} + +/** + * @brief Flush all pref changes to the XML file + */ +void Preferences::save() +{ + if (!_writable) return; // no-op if the prefs file is not writable + + // sp_repr_save_file uses utf-8 instead of the glib filename encoding. + // I don't know why filenames are kept in utf-8 in Inkscape and then + // converted to filename encoding when necessary through sepcial functions + // - wouldn't it be easier to keep things in the encoding they are supposed + // to be in? + Glib::ustring utf8name = Glib::filename_from_utf8(_prefs_filename); + if (utf8name.empty()) return; + sp_repr_save_file(_prefs_doc, utf8name.data()); +} - gchar *fn = profile_path (PREFERENCES_FILE); - (void) sp_repr_save_file (_preferences, fn); - g_free (fn); +void Preferences::addPrefsObserver(Inkscape::XML::NodeObserver *observer) +{ + _prefs_doc->addSubtreeObserver(*observer); } -} // namespace Inkscape +// Now for the meat. +// Most of the logic is similar to former prefs-utils.cpp + + +/** + * @brief Check for the existence of a given pref key + * @param pref_key Preference key to check + * @return True if the key exists, false otherwise + */ +bool Preferences::exists(Glib::ustring const &pref_key) +{ + return _getNode(pref_key) != NULL; +} + +/** + * @brief Get the number of sub-preferences of a given pref + * @param pref_key Preference key to check + * @return Number of sub-preferences + * + * Note: This does not count attributes, only child preferences. + */ +unsigned int Preferences::childCount(Glib::ustring const &pref_key) +{ + Inkscape::XML::Node *node = _getNode(pref_key); + return ( node ? node->childCount() : 0 ); +} + +/** + * @brief Get the key of the n-th sub-preference of the specified pref + * @param father_key Parent key + * @param n The zero-based index of the pref key to retrieve + * @return The key of the n-th sub-preference + */ +Glib::ustring Preferences::getNthChild(Glib::ustring const &father_key, unsigned int n) +{ + Inkscape::XML::Node *node = _getNode(father_key), *child; + if (!node) return ""; + child = node->nthChild(n); + if (!child) return ""; + if (child->attribute("id")) { + Glib::ustring child_key = father_key; + child_key += '.'; + child_key += child->attribute("id"); + return child_key; + } + return ""; +} + + +/** + * @brief Create the preference with the specified key + * @return True if the node was created, false if it already existed + * + * This method is redundant, because the setters automatically create prefs + * if they don't already exist. It is only left to accomodate some legacy code + * which manipulates the DOM of the preferences file directly. + */ +bool Preferences::create(Glib::ustring const &pref_key) +{ + if (_getNode(pref_key)) return false; + _getNode(pref_key, true); + return true; +} + +// getter methods + +/** + * @brief Get a boolean attribute of a preference + * @param pref_key Key of he preference to retrieve + * @param attr Attribute to retrieve + * @param def The default value to return if the preference is not set + * @return The retrieved value + */ +bool Preferences::getBool(Glib::ustring const &pref_key, Glib::ustring const &attr, bool def) +{ + Inkscape::XML::Node *node = _getNode(pref_key); + if (!node) return def; + gchar const *rawstr = node->attribute(attr.data()); + if(!rawstr || !rawstr[0]) return def; + Glib::ustring str = rawstr; + + // This is to handle legacy preferences using ints as booleans + if (str == "true" || str == "1") return true; + return false; +} + + +/** + * @brief Get an integer attribute of a preference + * @param pref_key Key of he preference to retrieve + * @param attr Attribute to retrieve + * @param def The default value to return if the preference is not set + * @return The retrieved value + */ +int Preferences::getInt(Glib::ustring const &pref_key, Glib::ustring const &attr, int def) +{ + Inkscape::XML::Node *node = _getNode(pref_key); + if (!node) return def; + gchar const *rawstr = node->attribute(attr.data()); + if (!rawstr || !rawstr[0]) return def; + Glib::ustring str = rawstr; + // Protection against leftover getInt calls when the value is in fact a boolean + if (str == "true") return 1; + if (str == "false") return 0; + return atoi(str.data()); +} + +int Preferences::getIntLimited(Glib::ustring const &pref_key, Glib::ustring const &attr, int def, int min, int max) +{ + int value = getInt(pref_key, attr, def); + return ( value >= min && value <= max ? value : def); +} + +/** + * @brief Get a floating point attribute of a preference + * @param pref_key Key of he preference to retrieve + * @param attr Attribute to retrieve + * @param def The default value to return if the preference is not set + * @return The retrieved value + */ +double Preferences::getDouble(Glib::ustring const &pref_key, Glib::ustring const &attr, double def) +{ + Inkscape::XML::Node *node = _getNode(pref_key); + if (!node) return def; + gchar const *str = node->attribute(attr.data()); + if (!str) return def; + return g_ascii_strtod(str, NULL); +} + +double Preferences::getDoubleLimited(Glib::ustring const &pref_key, Glib::ustring const &attr, double def, double min, double max) +{ + double value = getDouble(pref_key, attr, def); + return ( value >= min && value <= max ? value : def); +} + +/** + * @brief Get a string attribute of a preference + * @param pref_key Key of he preference to retrieve + * @param attr Attribute to retrieve + * @param def The default value to return if the preference is not set + * @return The retrieved value + */ +Glib::ustring Preferences::getString(Glib::ustring const &pref_key, Glib::ustring const &attr) +{ + Inkscape::XML::Node *node = _getNode(pref_key); + if (!node) return ""; + gchar const *str = node->attribute(attr.data()); + if (!str) return ""; + return Glib::ustring(str); +} + + +// setter methods + +/** + * @brief Set a boolean attribute of a preference + * @param pref_key Key of the preference to modify + * @param attr Attribute to set + * @param value The new value of the pref attribute + */ +void Preferences::setBool(Glib::ustring const &pref_key, Glib::ustring const &attr, bool value) +{ + Inkscape::XML::Node *node = _getNode(pref_key, true); + node->setAttribute(attr.data(), ( value ? "true" : "false" )); +} + +/** + * @brief Set an integer attribute of a preference + * @param pref_key Key of the preference to modify + * @param attr Attribute to set + * @param value The new value of the pref attribute + */ +void Preferences::setInt(Glib::ustring const &pref_key, Glib::ustring const &attr, int value) +{ + Inkscape::XML::Node *node = _getNode(pref_key, true); + gchar intstr[32]; + g_snprintf(intstr, 32, "%d", value); + node->setAttribute(attr.data(), intstr); +} + +/** + * @brief Set a floating point attribute of a preference + * @param pref_key Key of the preference to modify + * @param attr Attribute to set + * @param value The new value of the pref attribute + */ +void Preferences::setDouble(Glib::ustring const &pref_key, Glib::ustring const &attr, double value) +{ + Inkscape::XML::Node *node = _getNode(pref_key, true); + sp_repr_set_svg_double(node, attr.data(), value); + /* + gchar dblstr[32]; + g_snprintf(dblstr, 32, "%g", value); + node->setAttribute(attr, dblstr); + */ +} + +/** + * @brief Set a string attribute of a preference + * @param pref_key Key of the preference to modify + * @param attr Attribute to set + * @param value The new value of the pref attribute + */ +void Preferences::setString(Glib::ustring const &pref_key, Glib::ustring const &attr, Glib::ustring const &value) +{ + Inkscape::XML::Node *node = _getNode(pref_key, true); + node->setAttribute(attr.data(), value.data()); +} + +/** + * @brief Get the XML node corresponding to the given pref key + * @param pref_key Preference key (path) to get + * @param create Whether to create the corresponding node if it doesn't exist + * @return XML node corresponding to the specified key + * + * The separator for key components is '.' (a dot). Derived from former + * inkscape_get_repr(). + */ +Inkscape::XML::Node *Preferences::_getNode(Glib::ustring const &pref_key, bool create) +{ + Inkscape::XML::Node *node = _prefs_doc->root(), *child = NULL; + gchar **splits = g_strsplit(pref_key.data(), ".", 0); + int part_i = 0; + + while(splits[part_i]) { + for (child = node->firstChild(); child; child = child->next()) + if (!strcmp(splits[part_i], child->attribute("id"))) break; + + // If the previous loop found a matching key, child now contains the node + // matching the processed key part. If no node was found then it is NULL. + if (!child) { + if (create) { + // create the rest of the key + while(splits[part_i]) { + child = node->document()->createElement("group"); + child->setAttribute("id", splits[part_i]); + node->appendChild(child); + + ++part_i; + node = child; + } + g_strfreev(splits); + return node; + } else { + return NULL; + } + } + + ++part_i; + node = child; + } + g_strfreev(splits); + return node; +} + + +void Preferences::_errorDialog(Glib::ustring const &msg, Glib::ustring const &secondary) +{ + if (Preferences::use_gui) { + Gtk::MessageDialog err( + msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_OK, true); + err.set_secondary_text(secondary); + err.run(); + } else { + g_message("%s", msg.data()); + g_message("%s", secondary.data()); + } +} + +bool Preferences::use_gui = true; +Preferences *Preferences::_instance = NULL; + + +} // namespace Inkscape + /* Local Variables: mode:c++ |
