Code

Translations. Indonesian translation update by Waluyo Adi Siswanto.
[inkscape.git] / src / preferences.cpp
1 /** @file
2  * @brief  Singleton class to access the preferences file - implementation
3  */
4 /* Authors:
5  *   Krzysztof KosiƄski <tweenk.pl@gmail.com>
6  *   Jon A. Cruz <jon@joncruz.org>
7  *
8  * Copyright (C) 2008,2009 Authors
9  *
10  * Released under GNU GPL.  Read the file 'COPYING' for more information.
11  */
13 #include <cstring>
14 #include <sstream>
15 #include <glibmm/fileutils.h>
16 #include <glibmm/i18n.h>
17 #include <glib.h>
18 #include <glib/gstdio.h>
19 #include <gtk/gtk.h>
20 #include "preferences.h"
21 #include "preferences-skeleton.h"
22 #include "inkscape.h"
23 #include "xml/node-observer.h"
24 #include "xml/node-iterators.h"
25 #include "xml/attribute-record.h"
27 #define PREFERENCES_FILE_NAME "preferences.xml"
29 namespace Inkscape {
31 static Inkscape::XML::Document *loadImpl( std::string const& prefsFilename, Glib::ustring & errMsg );
32 static void migrateDetails( Inkscape::XML::Document *from, Inkscape::XML::Document *to );
34 static Inkscape::XML::Document *migrateFromDoc = 0;
36 // TODO clean up. Function copied from file.cpp:
37 // what gets passed here is not actually an URI... it is an UTF-8 encoded filename (!)
38 static void file_add_recent(gchar const *uri)
39 {
40     if (!uri) {
41         g_warning("file_add_recent: uri == NULL");
42     } else {
43         GtkRecentManager *recent = gtk_recent_manager_get_default();
44         gchar *fn = g_filename_from_utf8(uri, -1, NULL, NULL, NULL);
45         if (fn) {
46             if (g_file_test(fn, G_FILE_TEST_EXISTS)) {
47                 gchar *uriToAdd = g_filename_to_uri(fn, NULL, NULL);
48                 if (uriToAdd) {
49                     gtk_recent_manager_add_item(recent, uriToAdd);
50                     g_free(uriToAdd);
51                 }
52             }
53             g_free(fn);
54         }
55     }
56 }
59 // private inner class definition
61 /**
62  * @brief XML - prefs observer bridge
63  *
64  * This is an XML node observer that watches for changes in the XML document storing the preferences.
65  * It is used to implement preference observers.
66  */
67 class Preferences::PrefNodeObserver : public XML::NodeObserver {
68 public:
69     PrefNodeObserver(Observer &o, Glib::ustring const &filter) :
70         _observer(o),
71         _filter(filter)
72     {}
73     virtual ~PrefNodeObserver() {}
74     virtual void notifyAttributeChanged(XML::Node &node, GQuark name, Util::ptr_shared<char>, Util::ptr_shared<char>);
75 private:
76     Observer &_observer;
77     Glib::ustring const _filter;
78 };
80 Preferences::Preferences() :
81     _prefs_basename(PREFERENCES_FILE_NAME),
82     _prefs_dir(""),
83     _prefs_filename(""),
84     _prefs_doc(0),
85     _errorHandler(0),
86     _writable(false),
87     _hasError(false)
88 {
89     // profile_path essentailly returns the argument prefixed by the profile directory.
90     gchar *path = profile_path(NULL);
91     _prefs_dir = path;
92     g_free(path);
94     path = profile_path(_prefs_basename.c_str());
95     _prefs_filename = path;
96     g_free(path);
98     _loadDefaults();
99     _load();
102 Preferences::~Preferences()
104     // delete all PrefNodeObservers
105     for (_ObsMap::iterator i = _observer_map.begin(); i != _observer_map.end(); ) {
106         delete (*i++).second; // avoids reference to a deleted key
107     }
108     // unref XML document
109     Inkscape::GC::release(_prefs_doc);
112 /**
113  * @brief Load internal defaults
114  *
115  * In the future this will try to load the system-wide file before falling
116  * back to the internal defaults.
117  */
118 void Preferences::_loadDefaults()
120     _prefs_doc = sp_repr_read_mem(preferences_skeleton, PREFERENCES_SKELETON_SIZE, NULL);
123 /**
124  * @brief Load the user's customized preferences
125  *
126  * Tries to load the user's preferences.xml file. If there is none, creates it.
127  */
128 void Preferences::_load()
130     Glib::ustring const not_saved = _("Inkscape will run with default settings, "
131                                       "and new settings will not be saved. ");
133     // NOTE: After we upgrade to Glib 2.16, use Glib::ustring::compose
135     // 1. Does the file exist?
136     if (!g_file_test(_prefs_filename.c_str(), G_FILE_TEST_EXISTS)) {
137         // No - we need to create one.
138         // Does the profile directory exist?
139         if (!g_file_test(_prefs_dir.c_str(), G_FILE_TEST_EXISTS)) {
140             // No - create the profile directory
141             if (g_mkdir(_prefs_dir.c_str(), 0755)) {
142                 // the creation failed
143                 //_reportError(Glib::ustring::compose(_("Cannot create profile directory %1."),
144                 //    Glib::filename_to_utf8(_prefs_dir)), not_saved);
145                 gchar *msg = g_strdup_printf(_("Cannot create profile directory %s."),
146                     Glib::filename_to_utf8(_prefs_dir).c_str());
147                 _reportError(msg, not_saved);
148                 g_free(msg);
149                 return;
150             }
151             // create some subdirectories for user stuff
152             char const *user_dirs[] = {"keys", "templates", "icons", "extensions", "palettes", NULL};
153             for (int i=0; user_dirs[i]; ++i) {
154                 char *dir = profile_path(user_dirs[i]);
155                 g_mkdir(dir, 0755);
156                 g_free(dir);
157             }
159         } else if (!g_file_test(_prefs_dir.c_str(), G_FILE_TEST_IS_DIR)) {
160             // The profile dir is not actually a directory
161             //_reportError(Glib::ustring::compose(_("%1 is not a valid directory."),
162             //    Glib::filename_to_utf8(_prefs_dir)), not_saved);
163             gchar *msg = g_strdup_printf(_("%s is not a valid directory."),
164                 Glib::filename_to_utf8(_prefs_dir).c_str());
165             _reportError(msg, not_saved);
166             g_free(msg);
167             return;
168         }
169         // The profile dir exists and is valid.
170         if (!g_file_set_contents(_prefs_filename.c_str(), preferences_skeleton, PREFERENCES_SKELETON_SIZE, NULL)) {
171             // The write failed.
172             //_reportError(Glib::ustring::compose(_("Failed to create the preferences file %1."),
173             //    Glib::filename_to_utf8(_prefs_filename)), not_saved);
174             gchar *msg = g_strdup_printf(_("Failed to create the preferences file %s."),
175                 Glib::filename_to_utf8(_prefs_filename).c_str());
176             _reportError(msg, not_saved);
177             g_free(msg);
178             return;
179         }
181         if ( migrateFromDoc ) {
182             migrateDetails( migrateFromDoc, _prefs_doc );
183         }
185         // The prefs file was just created.
186         // We can return now and skip the rest of the load process.
187         _writable = true;
188         return;
189     }
191     // Yes, the pref file exists.
192     Glib::ustring errMsg;
193     Inkscape::XML::Document *prefs_read = loadImpl( _prefs_filename, errMsg );
195     if ( prefs_read ) {
196         // Merge the loaded prefs with defaults.
197         _prefs_doc->root()->mergeFrom(prefs_read->root(), "id");
198         Inkscape::GC::release(prefs_read);
199         _writable = true;
200     } else {
201         _reportError(errMsg, not_saved);
202     }
205 //_reportError(msg, not_saved);
206 static Inkscape::XML::Document *loadImpl( std::string const& prefsFilename, Glib::ustring & errMsg )
208     // 2. Is it a regular file?
209     if (!g_file_test(prefsFilename.c_str(), G_FILE_TEST_IS_REGULAR)) {
210         gchar *msg = g_strdup_printf(_("The preferences file %s is not a regular file."),
211             Glib::filename_to_utf8(prefsFilename).c_str());
212         errMsg = msg;
213         g_free(msg);
214         return 0;
215     }
217     // 3. Is the file readable?
218     gchar *prefs_xml = NULL; gsize len = 0;
219     if (!g_file_get_contents(prefsFilename.c_str(), &prefs_xml, &len, NULL)) {
220         gchar *msg = g_strdup_printf(_("The preferences file %s could not be read."),
221             Glib::filename_to_utf8(prefsFilename).c_str());
222         errMsg = msg;
223         g_free(msg);
224         return 0;
225     }
227     // 4. Is it valid XML?
228     Inkscape::XML::Document *prefs_read = sp_repr_read_mem(prefs_xml, len, NULL);
229     g_free(prefs_xml);
230     if (!prefs_read) {
231         gchar *msg = g_strdup_printf(_("The preferences file %s is not a valid XML document."),
232             Glib::filename_to_utf8(prefsFilename).c_str());
233         errMsg = msg;
234         g_free(msg);
235         return 0;
236     }
238     // 5. Basic sanity check: does the root element have a correct name?
239     if (strcmp(prefs_read->root()->name(), "inkscape")) {
240         gchar *msg = g_strdup_printf(_("The file %s is not a valid Inkscape preferences file."),
241             Glib::filename_to_utf8(prefsFilename).c_str());
242         errMsg = msg;
243         g_free(msg);
244         Inkscape::GC::release(prefs_read);
245         return 0;
246     }
248     return prefs_read;
251 static void migrateDetails( Inkscape::XML::Document *from, Inkscape::XML::Document *to )
253     // TODO pull in additional prefs with more granularity
254     to->root()->mergeFrom(from->root(), "id");
257 /**
258  * @brief Flush all pref changes to the XML file
259  */
260 void Preferences::save()
262     // no-op if the prefs file is not writable
263     if (_writable) {
264         // sp_repr_save_file uses utf-8 instead of the glib filename encoding.
265         // I don't know why filenames are kept in utf-8 in Inkscape and then
266         // converted to filename encoding when necessary through special functions
267         // - wouldn't it be easier to keep things in the encoding they are supposed
268         // to be in?
270         // No, it would not. There are many reasons, one key reason being that the
271         // rest of GTK+ is explicitly UTF-8. From an engineering standpoint, keeping
272         // the filesystem encoding would change things from a one-to-many problem to
273         // instead be a many-to-many problem. Also filesystem encoding can change
274         // from one run of the program to the next, so can not be stored.
275         // There are many other factors, so ask if you would like to learn them. - JAC
276         Glib::ustring utf8name = Glib::filename_to_utf8(_prefs_filename);
277         if (!utf8name.empty()) {
278             sp_repr_save_file(_prefs_doc, utf8name.c_str());
279         }
280     }
283 bool Preferences::getLastError( Glib::ustring& primary, Glib::ustring& secondary )
285     bool result = _hasError;
286     if ( _hasError ) {
287         primary = _lastErrPrimary;
288         secondary = _lastErrSecondary;
289         _hasError = false;
290         _lastErrPrimary.clear();
291         _lastErrSecondary.clear();
292     } else {
293         primary.clear();
294         secondary.clear();
295     }
296     return result;
299 void Preferences::migrate( std::string const& legacyDir, std::string const& prefdir )
301     int mode = S_IRWXU;
302 #ifdef S_IRGRP
303     mode |= S_IRGRP;
304 #endif
305 #ifdef S_IXGRP
306     mode |= S_IXGRP;
307 #endif
308 #ifdef S_IXOTH
309     mode |= S_IXOTH;
310 #endif
311     if ( g_mkdir_with_parents(prefdir.c_str(), mode) == -1 ) {
312     } else {
313     }
315     gchar * oldPrefFile = g_build_filename(legacyDir.c_str(), PREFERENCES_FILE_NAME, NULL);
316     if (oldPrefFile) {
317         if (g_file_test(oldPrefFile, G_FILE_TEST_EXISTS)) {
318             Glib::ustring errMsg;
319             Inkscape::XML::Document *oldPrefs = loadImpl( oldPrefFile, errMsg );
320             if (oldPrefs) {
321                 Glib::ustring docId("documents");
322                 Glib::ustring recentId("recent");
323                 Inkscape::XML::Node *node = oldPrefs->root();
324                 Inkscape::XML::Node *child = 0;
325                 Inkscape::XML::Node *recentNode = 0;
326                 if (node->attribute("version")) {
327                     node->setAttribute("version", "1");
328                 }
329                 for (child = node->firstChild(); child; child = child->next()) {
330                     if (docId == child->attribute("id")) {
331                         for (child = child->firstChild(); child; child = child->next()) {
332                             if (recentId == child->attribute("id")) {
333                                 recentNode = child;
334                                 for (child = child->firstChild(); child; child = child->next()) {
335                                     gchar const* uri = child->attribute("uri");
336                                     if (uri) {
337                                         file_add_recent(uri);
338                                     }
339                                 }
340                                 break;
341                             }
342                         }
343                         break;
344                     }
345                 }
347                 if (recentNode) {
348                     while (recentNode->firstChild()) {
349                         recentNode->removeChild(recentNode->firstChild());
350                     }
351                 }
352                 migrateFromDoc = oldPrefs;
353                 //Inkscape::GC::release(oldPrefs);
354                 oldPrefs = 0;
355             } else {
356                 g_warning( "%s", errMsg.c_str() );
357             }
358         }
359         g_free(oldPrefFile);
360         oldPrefFile = 0;
361     }
364 // Now for the meat.
366 /**
367  * @brief Get names of all entries in the specified path
368  * @param path Preference path to query
369  * @return A vector containing all entries in the given directory
370  */
371 std::vector<Preferences::Entry> Preferences::getAllEntries(Glib::ustring const &path)
373     std::vector<Entry> temp;
374     Inkscape::XML::Node *node = _getNode(path, false);
375     if (node) {
376         // argh - purge this Util::List nonsense from XML classes fast
377         Inkscape::Util::List<Inkscape::XML::AttributeRecord const> alist = node->attributeList();
378         for (; alist; ++alist) {
379             temp.push_back( Entry(path + '/' + g_quark_to_string(alist->key), static_cast<void const*>(alist->value.pointer())) );
380         }
381     }
382     return temp;
385 /**
386  * @brief Get the paths to all subdirectories of the specified path
387  * @param path Preference path to query
388  * @return A vector containing absolute paths to all subdirectories in the given path
389  */
390 std::vector<Glib::ustring> Preferences::getAllDirs(Glib::ustring const &path)
392     std::vector<Glib::ustring> temp;
393     Inkscape::XML::Node *node = _getNode(path, false);
394     if (node) {
395         for (Inkscape::XML::NodeSiblingIterator i = node->firstChild(); i; ++i) {
396             temp.push_back(path + '/' + i->attribute("id"));
397         }
398     }
399     return temp;
402 // getter methods
404 Preferences::Entry const Preferences::getEntry(Glib::ustring const &pref_path)
406     gchar const *v;
407     _getRawValue(pref_path, v);
408     return Entry(pref_path, v);
411 // setter methods
413 /**
414  * @brief Set a boolean attribute of a preference
415  * @param pref_path Path of the preference to modify
416  * @param value The new value of the pref attribute
417  */
418 void Preferences::setBool(Glib::ustring const &pref_path, bool value)
420     /// @todo Boolean values should be stored as "true" and "false",
421     /// but this is not possible due to an interaction with event contexts.
422     /// Investigate this in depth.
423     _setRawValue(pref_path, ( value ? "1" : "0" ));
426 /**
427  * @brief Set an integer attribute of a preference
428  * @param pref_path Path of the preference to modify
429  * @param value The new value of the pref attribute
430  */
431 void Preferences::setInt(Glib::ustring const &pref_path, int value)
433     gchar intstr[32];
434     g_snprintf(intstr, 32, "%d", value);
435     _setRawValue(pref_path, intstr);
438 /**
439  * @brief Set a floating point attribute of a preference
440  * @param pref_path Path of the preference to modify
441  * @param value The new value of the pref attribute
442  */
443 void Preferences::setDouble(Glib::ustring const &pref_path, double value)
445     gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
446     g_ascii_dtostr(buf, G_ASCII_DTOSTR_BUF_SIZE, value);
447     _setRawValue(pref_path, buf);
450 void Preferences::setColor(Glib::ustring const &pref_path, guint32 value)
452     gchar buf[16];
453     g_snprintf(buf, 16, "#%08x", value);
454     _setRawValue(pref_path, buf);
457 /**
458  * @brief Set a string attribute of a preference
459  * @param pref_path Path of the preference to modify
460  * @param value The new value of the pref attribute
461  */
462 void Preferences::setString(Glib::ustring const &pref_path, Glib::ustring const &value)
464     _setRawValue(pref_path, value.c_str());
467 void Preferences::setStyle(Glib::ustring const &pref_path, SPCSSAttr *style)
469     gchar *css_str = sp_repr_css_write_string(style);
470     _setRawValue(pref_path, css_str);
471     g_free(css_str);
474 void Preferences::mergeStyle(Glib::ustring const &pref_path, SPCSSAttr *style)
476     SPCSSAttr *current = getStyle(pref_path);
477     sp_repr_css_merge(current, style);
478     gchar *css_str = sp_repr_css_write_string(current);
479     _setRawValue(pref_path, css_str);
480     g_free(css_str);
481     sp_repr_css_attr_unref(current);
485 // Observer stuff
486 namespace {
488 /**
489  * @brief Structure that holds additional information for registered Observers
490  */
491 struct _ObserverData {
492     Inkscape::XML::Node *_node; ///< Node at which the wrapping PrefNodeObserver is registered
493     bool _is_attr; ///< Whether this Observer watches a single attribute
494 };
496 } // anonymous namespace
498 Preferences::Observer::Observer(Glib::ustring const &path) :
499     observed_path(path)
503 Preferences::Observer::~Observer()
505     // on destruction remove observer to prevent invalid references
506     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
507     prefs->removeObserver(*this);
510 void Preferences::PrefNodeObserver::notifyAttributeChanged(XML::Node &node, GQuark name, Util::ptr_shared<char>, Util::ptr_shared<char> new_value)
512     // filter out attributes we don't watch
513     gchar const *attr_name = g_quark_to_string(name);
514     if ( _filter.empty() || (_filter == attr_name) ) {
515         _ObserverData *d = static_cast<_ObserverData*>(Preferences::_get_pref_observer_data(_observer));
516         Glib::ustring notify_path = _observer.observed_path;
518         if (!d->_is_attr) {
519             std::vector<gchar const *> path_fragments;
520             notify_path.reserve(256); // this will make appending operations faster
522             // walk the XML tree, saving each of the id attributes in a vector
523             // we terminate when we hit the observer's attachment node, because the path to this node
524             // is already stored in notify_path
525             for (XML::NodeParentIterator n = &node; static_cast<XML::Node*>(n) != d->_node; ++n) {
526                 path_fragments.push_back(n->attribute("id"));
527             }
528             // assemble the elements into a path
529             for (std::vector<gchar const *>::reverse_iterator i = path_fragments.rbegin(); i != path_fragments.rend(); ++i) {
530                 notify_path.push_back('/');
531                 notify_path.append(*i);
532             }
534             // append attribute name
535             notify_path.push_back('/');
536             notify_path.append(attr_name);
537         }
539         Entry const val = Preferences::_create_pref_value(notify_path, static_cast<void const*>(new_value.pointer()));
540         _observer.notify(val);
541     }
544 /**
545  * @brief Find the XML node to observe
546  */
547 XML::Node *Preferences::_findObserverNode(Glib::ustring const &pref_path, Glib::ustring &node_key, Glib::ustring &attr_key, bool create)
549     // first assume that the last path element is an entry.
550     _keySplit(pref_path, node_key, attr_key);
552     // find the node corresponding to the "directory".
553     Inkscape::XML::Node *node = _getNode(node_key, create), *child;
554     for (child = node->firstChild(); child; child = child->next()) {
555         // If there is a node with id corresponding to the attr key,
556         // this means that the last part of the path is actually a key (folder).
557         // Change values accordingly.
558         if (attr_key == child->attribute("id")) {
559             node = child;
560             attr_key = "";
561             node_key = pref_path;
562             break;
563         }
564     }
565     return node;
568 void Preferences::addObserver(Observer &o)
570     // prevent adding the same observer twice
571     if ( _observer_map.find(&o) == _observer_map.end() ) {
572         Glib::ustring node_key, attr_key;
573         Inkscape::XML::Node *node;
574         node = _findObserverNode(o.observed_path, node_key, attr_key, false);
575         if (node) {
576             // set additional data
577             _ObserverData *priv_data = new _ObserverData;
578             priv_data->_node = node;
579             priv_data->_is_attr = !attr_key.empty();
580             o._data = static_cast<void*>(priv_data);
582             _observer_map[&o] = new PrefNodeObserver(o, attr_key);
584             // if we watch a single pref, we want to receive notifications only for a single node
585             if (priv_data->_is_attr) {
586                 node->addObserver( *(_observer_map[&o]) );
587             } else {
588                 node->addSubtreeObserver( *(_observer_map[&o]) );
589             }
590         }
591     }
594 void Preferences::removeObserver(Observer &o)
596     // prevent removing an observer which was not added
597     if ( _observer_map.find(&o) != _observer_map.end() ) {
598         Inkscape::XML::Node *node = static_cast<_ObserverData*>(o._data)->_node;
599         _ObserverData *priv_data = static_cast<_ObserverData*>(o._data);
600         o._data = NULL;
602         if (priv_data->_is_attr) {
603             node->removeObserver( *(_observer_map[&o]) );
604         } else {
605             node->removeSubtreeObserver( *(_observer_map[&o]) );
606         }
608         delete priv_data;
609         delete _observer_map[&o];
610         _observer_map.erase(&o);
611     }
615 /**
616  * @brief Get the XML node corresponding to the given pref key
617  * @param pref_key Preference key (path) to get
618  * @param create Whether to create the corresponding node if it doesn't exist
619  * @param separator The character used to separate parts of the pref key
620  * @return XML node corresponding to the specified key
621  *
622  * Derived from former inkscape_get_repr(). Private because it assumes that the backend is
623  * a flat XML file, which may not be the case e.g. if we are using GConf (in future).
624  */
625 Inkscape::XML::Node *Preferences::_getNode(Glib::ustring const &pref_key, bool create)
627     // verify path
628     g_assert( pref_key.at(0) == '/' );
629     // No longer necessary, can cause problems with input devices which have a dot in the name
630     // g_assert( pref_key.find('.') == Glib::ustring::npos );
632     Inkscape::XML::Node *node = _prefs_doc->root();
633     Inkscape::XML::Node *child = NULL;
634     gchar **splits = g_strsplit(pref_key.c_str(), "/", 0);
636     if ( splits ) {
637         for (int part_i = 0; splits[part_i]; ++part_i) {
638             // skip empty path segments
639             if (!splits[part_i][0]) {
640                 continue;
641             }
643             for (child = node->firstChild(); child; child = child->next()) {
644                 if (!strcmp(splits[part_i], child->attribute("id"))) {
645                     break;
646                 }
647             }
649             // If the previous loop found a matching key, child now contains the node
650             // matching the processed key part. If no node was found then it is NULL.
651             if (!child) {
652                 if (create) {
653                     // create the rest of the key
654                     while(splits[part_i]) {
655                         child = node->document()->createElement("group");
656                         child->setAttribute("id", splits[part_i]);
657                         node->appendChild(child);
659                         ++part_i;
660                         node = child;
661                     }
662                     g_strfreev(splits);
663                     return node;
664                 } else {
665                     return NULL;
666                 }
667             }
669             node = child;
670         }
671         g_strfreev(splits);
672     }
673     return node;
676 void Preferences::_getRawValue(Glib::ustring const &path, gchar const *&result)
678     // create node and attribute keys
679     Glib::ustring node_key, attr_key;
680     _keySplit(path, node_key, attr_key);
682     // retrieve the attribute
683     Inkscape::XML::Node *node = _getNode(node_key, false);
684     if ( node == NULL ) {
685         result = NULL;
686     } else {
687         gchar const *attr = node->attribute(attr_key.c_str());
688         if ( attr == NULL ) {
689             result = NULL;
690         } else {
691             result = attr;
692         }
693     }
696 void Preferences::_setRawValue(Glib::ustring const &path, gchar const *value)
698     // create node and attribute keys
699     Glib::ustring node_key, attr_key;
700     _keySplit(path, node_key, attr_key);
702     // set the attribute
703     Inkscape::XML::Node *node = _getNode(node_key, true);
704     node->setAttribute(attr_key.c_str(), value);
707 // The _extract* methods are where the actual wrok is done - they define how preferences are stored
708 // in the XML file.
710 bool Preferences::_extractBool(Entry const &v)
712     gchar const *s = static_cast<gchar const *>(v._value);
713     if ( !s[0] || !strcmp(s, "0") || !strcmp(s, "false") ) {
714         return false;
715     } else {
716         return true;
717     }
720 int Preferences::_extractInt(Entry const &v)
722     gchar const *s = static_cast<gchar const *>(v._value);
723     if ( !strcmp(s, "true") ) {
724         return true;
725     } else if ( !strcmp(s, "false") ) {
726         return false;
727     } else {
728         return atoi(s);
729     }
732 double Preferences::_extractDouble(Entry const &v)
734     gchar const *s = static_cast<gchar const *>(v._value);
735     return g_ascii_strtod(s, NULL);
738 Glib::ustring Preferences::_extractString(Entry const &v)
740     return Glib::ustring(static_cast<gchar const *>(v._value));
743 guint32 Preferences::_extractColor(Entry const &v)
745     gchar const *s = static_cast<gchar const *>(v._value);
746     std::istringstream hr(s);
747     guint32 color;
748     if (s[0] == '#') {
749         hr.ignore(1);
750         hr >> std::hex >> color;
751     } else {
752         hr >> color;
753     }
754     return color;
757 SPCSSAttr *Preferences::_extractStyle(Entry const &v)
759     SPCSSAttr *style = sp_repr_css_attr_new();
760     sp_repr_css_attr_add_from_string(style, static_cast<gchar const*>(v._value));
761     return style;
764 SPCSSAttr *Preferences::_extractInheritedStyle(Entry const &v)
766     // This is the dirtiest extraction method. Generally we ignore whatever was in v._value
767     // and just get the style using sp_repr_css_attr_inherited. To implement this in GConf,
768     // we'll have to walk up the tree and call sp_repr_css_attr_add_from_string
769     Glib::ustring node_key, attr_key;
770     _keySplit(v._pref_path, node_key, attr_key);
772     Inkscape::XML::Node *node = _getNode(node_key, false);
773     return sp_repr_css_attr_inherited(node, attr_key.c_str());
776 // XML backend helper: Split the path into a node key and an attribute key.
777 void Preferences::_keySplit(Glib::ustring const &pref_path, Glib::ustring &node_key, Glib::ustring &attr_key)
779     // everything after the last slash
780     attr_key = pref_path.substr(pref_path.rfind('/') + 1, Glib::ustring::npos);
781     // everything before the last slash
782     node_key = pref_path.substr(0, pref_path.rfind('/'));
785 void Preferences::_reportError(Glib::ustring const &msg, Glib::ustring const &secondary)
787     _hasError = true;
788     _lastErrPrimary = msg;
789     _lastErrSecondary = secondary;
790     if (_errorHandler) {
791         _errorHandler->handleError(msg, secondary);
792     }
795 Preferences::Entry const Preferences::_create_pref_value(Glib::ustring const &path, void const *ptr)
797     return Entry(path, ptr);
800 void Preferences::setErrorHandler(ErrorReporter* handler)
802     _errorHandler = handler;
805 void Preferences::unload(bool save)
807     if (_instance)
808     {
809         if (save) {
810             _instance->save();
811         }
812         delete _instance;
813         _instance = NULL;
814     }
817 Preferences *Preferences::_instance = NULL;
820 } // namespace Inkscape
822 /*
823   Local Variables:
824   mode:c++
825   c-file-style:"stroustrup"
826   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
827   indent-tabs-mode:nil
828   fill-column:99
829   End:
830 */
831 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :