7335ab39a89022b081541d41f71bda9f4d21e375
1 /** @file
2 * @brief Singleton class to access the preferences file in a convenient way.
3 */
4 /* Authors:
5 * Krzysztof KosiĆski <tweenk.pl@gmail.com>
6 *
7 * Copyright (C) 2008 Authors
8 *
9 * Released under GNU GPL. Read the file 'COPYING' for more information.
10 */
12 #ifndef INKSCAPE_PREFSTORE_H
13 #define INKSCAPE_PREFSTORE_H
15 #include <glibmm/ustring.h>
16 #include <string>
17 #include <map>
18 #include <vector>
19 #include <climits>
20 #include <cfloat>
21 #include "xml/xml-forward.h"
22 #include "xml/repr.h"
24 class SPCSSAttr;
26 namespace Inkscape {
28 /**
29 * @brief Preference storage class
30 *
31 * This is a singleton that allows one to access the user preferences stored in
32 * the preferences.xml file. The preferences are stored in a file system-like
33 * hierarchy. They are generally typeless - it's up to the programmer to ensure
34 * that a given preference is always accessed as the correct type. The backend
35 * is not guaranteed to be tolerant to type mismatches.
36 *
37 * Preferences are identified by paths similar to file system paths. Components
38 * of the path are separated by a slash (/). As an additional requirement,
39 * the path must start with a slash, and not contain a trailing slash.
40 * An example of a correct path would be "/options/some_group/some_option".
41 *
42 * All preferences are loaded when the first singleton pointer is requested,
43 * or when the static load() method is called. Before loading, the static
44 * variable @c use_gui should be set accordingly. To save the preferences,
45 * the method save() or the static function unload() can be used.
46 *
47 * In future, this will be a virtual base from which specific backends
48 * derive (e.g. GConf, flat XML file...)
49 */
50 class Preferences {
51 public:
52 // #############################
53 // ## inner class definitions ##
54 // #############################
56 class Entry;
57 class Observer;
59 /**
60 * @brief Base class for preference observers
61 *
62 * If you want to watch for changes in the preferences, you'll have to
63 * derive a class from this one and override the notify() method.
64 */
65 class Observer {
66 friend class Preferences;
67 public:
68 /**
69 * @brief Constructor.
70 *
71 * Since each Observer is assigned to a single path, the base
72 * constructor takes this path as an argument. This prevents one from
73 * adding a single observer to multiple paths, but this is intentional
74 * to simplify the implementation of observers and notifications.
75 *
76 * After you add the object with Preferences::addObserver(), you will
77 * receive notifications for everything below the attachment point.
78 * You can also specify a single preference as the watch point.
79 * For example, watching the directory "/foo" will give you notifications
80 * about "/foo/some_pref" as well as "/foo/some_dir/other_pref".
81 * Watching the preference "/options/some_group/some_option" will only
82 * generate notifications when this single preference changes.
83 *
84 * @param path Preference path the observer should watch
85 */
86 Observer(Glib::ustring const &path);
87 virtual ~Observer();
88 /**
89 * @brief Notification about a preference change
90 * @param new_val Entry object containing information about
91 * the modified preference
92 */
93 virtual void notify(Preferences::Entry const &new_val) = 0;
95 Glib::ustring const observed_path; ///< Path which the observer watches
96 private:
97 void *_data; ///< additional data used by the implementation while the observer is active
98 };
101 /**
102 * @brief Data type representing a typeless value of a preference
103 *
104 * This is passed to the observer in the notify() method.
105 * To retrieve useful data from it, use its member functions. Setting
106 * any preference using the Preferences class invalidates this object,
107 * so use its get methods before doing so.
108 */
109 class Entry {
110 friend class Preferences; // Preferences class has to access _value
111 public:
112 ~Entry() {}
113 Entry() : _pref_path(""), _value(NULL) {} // needed to enable use in maps
114 Entry(Entry const &other) : _pref_path(other._pref_path), _value(other._value) {}
115 /**
116 * @brief Check whether the received entry is valid.
117 * @return If false, the default value will be returned by the getters.
118 */
119 bool isValid() const { return _value != NULL; }
121 /**
122 * @brief Interpret the preference as a Boolean value.
123 * @param def Default value if the preference is not set
124 */
125 inline bool getBool(bool def=false) const;
126 /**
127 * @brief Interpret the preference as an integer.
128 * @param def Default value if the preference is not set
129 */
130 inline int getInt(int def=0) const;
131 /**
132 * @brief Interpret the preference as a limited integer.
133 *
134 * This method will return the default value if the interpreted value is
135 * larger than @c max or smaller than @c min. Do not use to store
136 * Boolean values as integers.
137 *
138 * @param def Default value if the preference is not set
139 * @param min Minimum value allowed to return
140 * @param max Maximum value allowed to return
141 */
142 inline int getIntLimited(int def=0, int min=INT_MIN, int max=INT_MAX) const;
143 /**
144 * @brief Interpret the preference as a floating point value.
145 * @param def Default value if the preference is not set
146 */
147 inline double getDouble(double def=0.0) const;
148 /**
149 * @brief Interpret the preference as a limited floating point value.
150 *
151 * This method will return the default value if the interpreted value is
152 * larger than @c max or smaller than @c min.
153 *
154 * @param def Default value if the preference is not set
155 * @param min Minimum value allowed to return
156 * @param max Maximum value allowed to return
157 */
158 inline double getDoubleLimited(double def=0.0, double min=DBL_MIN, double max=DBL_MAX) const;
159 /**
160 * @brief Interpret the preference as an UTF-8 string.
161 *
162 * To store a filename, convert it using Glib::filename_to_utf8().
163 */
164 inline Glib::ustring getString() const;
165 /**
166 * @brief Interpret the preference as a CSS style.
167 * @return A CSS style that has to be unrefed when no longer necessary. Never NULL.
168 */
169 inline SPCSSAttr *getStyle() const;
170 /**
171 * @brief Interpret the preference as a CSS style with directory-based
172 * inheritance
173 *
174 * This function will look up the preferences with the same entry name
175 * in ancestor directories and return the inherited CSS style.
176 *
177 * @return Inherited CSS style that has to be unrefed after use. Never NULL.
178 */
179 inline SPCSSAttr *getInheritedStyle() const;
181 /**
182 * @brief Get the full path of the preference described by this Entry.
183 */
184 Glib::ustring const &getPath() const { return _pref_path; }
185 /**
186 * @brief Get the last component of the preference's path
187 *
188 * E.g. For "/options/some_group/some_option" it will return "some_option".
189 */
190 Glib::ustring getEntryName() const;
191 private:
192 Entry(Glib::ustring const &path, void const *v) : _pref_path(path), _value(v) {}
194 Glib::ustring _pref_path;
195 void const *_value;
196 };
198 // utility methods
200 /**
201 * @name Load stored preferences and save them to the disk.
202 * @{
203 */
205 /**
206 * @brief Load the preferences from the default location.
207 *
208 * Loads the stored user preferences and enables saving them. If there's
209 * no preferences file in the expected location, it creates it. Any changes
210 * made to the preferences before loading will be overridden by the stored
211 * prefs. Not calling load() is sometimes useful, e.g. for testing.
212 *
213 * @param use_gui Whether to use dialogs to notify about errors when
214 * loading the preferences. Set to false in console mode.
215 * @param quiet Whether to output any messages about preference loading.
216 * If this is true, the use_gui parameter is ignored.
217 */
218 void load(bool use_gui=true, bool quiet=false);
219 /**
220 * @brief Save all preferences to the hard disk.
221 *
222 * For some backends, the preferences may be saved as they are modified.
223 * Not calling this method doesn't guarantee the preferences are unmodified
224 * the next time Inkscape runs.
225 */
226 void save();
227 /**
228 * @brief Check whether saving the preferences will have any effect.
229 */
230 bool isWritable() { return _writable; }
231 /*@}*/
233 /**
234 * @name Iterate over directories and entries.
235 * @{
236 */
238 /**
239 * @brief Get all entries from the specified directory
240 *
241 * This method will return a vector populated with preference entries
242 * from the specified directory. Subdirectories will not be represented.
243 */
244 std::vector<Entry> getAllEntries(Glib::ustring const &path);
245 /**
246 * @brief Get all subdirectories of the specified directory
247 *
248 * This will return a vector populated with full paths to the subdirectories
249 * present in the specified @c path.
250 */
251 std::vector<Glib::ustring> getAllDirs(Glib::ustring const &path);
252 /*@}*/
254 /**
255 * @name Retrieve data from the preference storage.
256 * @{
257 */
258 /**
259 * @brief Retrieve a Boolean value
260 * @param pref_path Path to the retrieved preference
261 * @param def The default value to return if the preference is not set
262 */
263 bool getBool(Glib::ustring const &pref_path, bool def=false) {
264 return getEntry(pref_path).getBool(def);
265 }
266 /**
267 * @brief Retrieve an integer
268 * @param pref_path Path to the retrieved preference
269 * @param def The default value to return if the preference is not set
270 */
271 int getInt(Glib::ustring const &pref_path, int def=0) {
272 return getEntry(pref_path).getInt(def);
273 }
274 /**
275 * @brief Retrieve a limited integer
276 *
277 * The default value is returned if the actual value is larger than @c max
278 * or smaller than @c min. Do not use to store Boolean values.
279 *
280 * @param pref_path Path to the retrieved preference
281 * @param def The default value to return if the preference is not set
282 * @param min Minimum value to return
283 * @param max Maximum value to return
284 */
285 int getIntLimited(Glib::ustring const &pref_path, int def=0, int min=INT_MIN, int max=INT_MAX) {
286 return getEntry(pref_path).getIntLimited(def, min, max);
287 }
288 double getDouble(Glib::ustring const &pref_path, double def=0.0) {
289 return getEntry(pref_path).getDouble(def);
290 }
291 /**
292 * @brief Retrieve a limited floating point value
293 *
294 * The default value is returned if the actual value is larger than @c max
295 * or smaller than @c min.
296 *
297 * @param pref_path Path to the retrieved preference
298 * @param def The default value to return if the preference is not set
299 * @param min Minimum value to return
300 * @param max Maximum value to return
301 */
302 double getDoubleLimited(Glib::ustring const &pref_path, double def=0.0, double min=DBL_MIN, double max=DBL_MAX) {
303 return getEntry(pref_path).getDoubleLimited(def, min, max);
304 }
305 /**
306 * @brief Retrieve an UTF-8 string
307 * @param pref_path Path to the retrieved preference
308 */
309 Glib::ustring getString(Glib::ustring const &pref_path) {
310 return getEntry(pref_path).getString();
311 }
312 /**
313 * @brief Retrieve a CSS style
314 * @param pref_path Path to the retrieved preference
315 * @return A CSS style that has to be unrefed after use.
316 */
317 SPCSSAttr *getStyle(Glib::ustring const &pref_path) {
318 return getEntry(pref_path).getStyle();
319 }
320 /**
321 * @brief Retrieve an inherited CSS style
322 *
323 * This method will look up preferences with the same entry name in ancestor
324 * directories and return a style obtained by inheriting properties from
325 * ancestor styles.
326 *
327 * @param pref_path Path to the retrieved preference
328 * @return An inherited CSS style that has to be unrefed after use.
329 */
330 SPCSSAttr *getInheritedStyle(Glib::ustring const &pref_path) {
331 return getEntry(pref_path).getInheritedStyle();
332 }
333 /**
334 * @brief Retrieve a preference entry without specifying its type
335 */
336 Entry const getEntry(Glib::ustring const &pref_path);
337 /*@}*/
339 /**
340 * @name Update preference values.
341 * @{
342 */
344 /**
345 * @brief Set a Boolean value
346 */
347 void setBool(Glib::ustring const &pref_path, bool value);
348 /**
349 * @brief Set an integer value
350 */
351 void setInt(Glib::ustring const &pref_path, int value);
352 /**
353 * @brief Set a floating point value
354 */
355 void setDouble(Glib::ustring const &pref_path, double value);
356 /**
357 * @brief Set an UTF-8 string value
358 */
359 void setString(Glib::ustring const &pref_path, Glib::ustring const &value);
360 /**
361 * @brief Set a CSS style
362 */
363 void setStyle(Glib::ustring const &pref_path, SPCSSAttr *style);
364 /**
365 * @brief Merge a CSS style with the current preference value
366 *
367 * This method is similar to setStyle(), except that it merges the style
368 * rather than replacing it. This means that if @c style doesn't have
369 * a property set, it is left unchanged in the style stored in
370 * the preferences.
371 */
372 void mergeStyle(Glib::ustring const &pref_path, SPCSSAttr *style);
373 /*@}*/
375 /**
376 * @name Receive notifications about preference changes.
377 * @{
378 */
379 /**
380 * @brief Register a preference observer
381 */
382 void addObserver(Observer &);
383 /**
384 * @brief Remove an observer an prevent further notifications to it.
385 */
386 void removeObserver(Observer &);
387 /*@}*/
389 /**
390 * @name Access and manipulate the Preferences object.
391 * @{
392 */
394 /**
395 * @brief Access the singleton Preferences object.
396 */
397 static Preferences *get() {
398 if (!_instance) _instance = new Preferences();
399 return _instance;
400 }
401 /**
402 * @brief Unload all preferences
403 * @param save Whether to save the preferences; defaults to true
404 *
405 * This deletes the singleton object. Calling get() after this function
406 * will reinstate it, so you shouldn't. Pass false as the parameter
407 * to suppress automatic saving.
408 */
409 static void unload(bool save=true);
410 /*@}*/
412 protected:
413 /* helper methods used by Entry
414 * This will enable using the same Entry class with different backends.
415 * For now, however, those methods are not virtual. These methods assume
416 * that v._value is not NULL
417 */
418 bool _extractBool(Entry const &v);
419 int _extractInt(Entry const &v);
420 double _extractDouble(Entry const &v);
421 Glib::ustring _extractString(Entry const &v);
422 SPCSSAttr *_extractStyle(Entry const &v);
423 SPCSSAttr *_extractInheritedStyle(Entry const &v);
425 private:
426 Preferences();
427 ~Preferences();
428 void _loadDefaults();
429 void _getRawValue(Glib::ustring const &path, gchar const *&result);
430 void _setRawValue(Glib::ustring const &path, gchar const *value);
431 void _errorDialog(Glib::ustring const &, Glib::ustring const &);
432 void _keySplit(Glib::ustring const &pref_path, Glib::ustring &node_key, Glib::ustring &attr_key);
433 XML::Node *_getNode(Glib::ustring const &pref_path, bool create=false);
434 XML::Node *_findObserverNode(Glib::ustring const &pref_path, Glib::ustring &node_key, Glib::ustring &attr_key, bool create);
436 // disable copying
437 Preferences(Preferences const &);
438 Preferences operator=(Preferences const &);
440 std::string _prefs_basename; ///< Basename of the prefs file
441 std::string _prefs_dir; ///< Directory in which to look for the prefs file
442 std::string _prefs_filename; ///< Full filename (with directory) of the prefs file
443 XML::Document *_prefs_doc; ///< XML document storing all the preferences
444 bool _use_gui; ///< Use GUI error notifications?
445 bool _quiet; ///< Display any messages about loading?
446 bool _loaded; ///< Was a load attempt made?
447 bool _writable; ///< Will the preferences be saved at exit?
449 /// Wrapper class for XML node observers
450 class PrefNodeObserver;
452 typedef std::map<Observer *, PrefNodeObserver *> _ObsMap;
453 /// Map that keeps track of wrappers assigned to PrefObservers
454 _ObsMap _observer_map;
456 // privilege escalation methods for PrefNodeObserver
457 static Entry const _create_pref_value(Glib::ustring const &, void const *ptr);
458 static void *_get_pref_observer_data(Observer &o) { return o._data; }
460 static Preferences *_instance;
462 friend class PrefNodeObserver;
463 friend class Entry;
464 };
466 /* Trivial inline Preferences::Entry functions.
467 * In fact only the _extract* methods do something, the rest is delegation
468 * to avoid duplication of code. There should be no performance hit if
469 * compiled with -finline-functions.
470 */
472 inline bool Preferences::Entry::getBool(bool def) const
473 {
474 if (!this->isValid()) return def;
475 return Inkscape::Preferences::get()->_extractBool(*this);
476 }
478 inline int Preferences::Entry::getInt(int def) const
479 {
480 if (!this->isValid()) return def;
481 return Inkscape::Preferences::get()->_extractInt(*this);
482 }
484 inline int Preferences::Entry::getIntLimited(int def, int min, int max) const
485 {
486 if (!this->isValid()) return def;
487 int val = Inkscape::Preferences::get()->_extractInt(*this);
488 return ( val >= min && val <= max ? val : def );
489 }
491 inline double Preferences::Entry::getDouble(double def) const
492 {
493 if (!this->isValid()) return def;
494 return Inkscape::Preferences::get()->_extractDouble(*this);
495 }
497 inline double Preferences::Entry::getDoubleLimited(double def, double min, double max) const
498 {
499 if (!this->isValid()) return def;
500 double val = Inkscape::Preferences::get()->_extractDouble(*this);
501 return ( val >= min && val <= max ? val : def );
502 }
504 inline Glib::ustring Preferences::Entry::getString() const
505 {
506 if (!this->isValid()) return "";
507 return Inkscape::Preferences::get()->_extractString(*this);
508 }
510 inline SPCSSAttr *Preferences::Entry::getStyle() const
511 {
512 if (!this->isValid()) return sp_repr_css_attr_new();
513 return Inkscape::Preferences::get()->_extractStyle(*this);
514 }
516 inline SPCSSAttr *Preferences::Entry::getInheritedStyle() const
517 {
518 if (!this->isValid()) return sp_repr_css_attr_new();
519 return Inkscape::Preferences::get()->_extractInheritedStyle(*this);
520 }
522 inline Glib::ustring Preferences::Entry::getEntryName() const
523 {
524 Glib::ustring path_base = _pref_path;
525 path_base.erase(0, path_base.rfind('/') + 1);
526 return path_base;
527 }
529 } // namespace Inkscape
531 #endif // INKSCAPE_PREFSTORE_H
533 /*
534 Local Variables:
535 mode:c++
536 c-file-style:"stroustrup"
537 c-file-offsets:((innamespace . 0)(inline-open . 0))
538 indent-tabs-mode:nil
539 fill-column:75
540 End:
541 */
542 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :