18#include <glib/gstdio.h>
19#include <glibmm/convert.h>
20#include <glibmm/i18n.h>
21#include <glibmm/stringutils.h>
37#define PREFERENCES_FILE_NAME "preferences.xml"
56 PrefNodeObserver(Observer &o, Glib::ustring filter) :
58 _filter(std::move(filter))
60 ~PrefNodeObserver()
override =
default;
64 Glib::ustring
const _filter;
93 setBool(
"/options/desktopintegration/value", 1);
95#if defined(GDK_WINDOWING_QUARTZ)
97 setInt(
"/options/defaultwindowsize/value", -1);
109 Glib::ustring
const not_saved = _(
"Inkscape will run with default settings, "
110 "and new settings will not be saved. ");
119 if (!g_file_test(_prefs_dir.c_str(), G_FILE_TEST_EXISTS)) {
121 if (g_mkdir_with_parents(_prefs_dir.c_str(), 0755)) {
125 gchar *
msg = g_strdup_printf(_(
"Cannot create profile directory %s."), _prefs_dir.c_str());
130 }
else if (!g_file_test(_prefs_dir.c_str(), G_FILE_TEST_IS_DIR)) {
134 gchar *
msg = g_strdup_printf(_(
"%s is not a valid directory."), _prefs_dir.c_str());
140 char const *user_dirs[] = {
"extensions",
"fonts",
"icons",
"keys",
"palettes",
"templates",
nullptr};
141 for (
int i=0; user_dirs[i]; ++i) {
144 if (!g_file_test(dir.c_str(), G_FILE_TEST_EXISTS))
145 g_mkdir(dir.c_str(), 0755);
152 gchar *
msg = g_strdup_printf(_(
"Failed to create the preferences file %s."),
170 Glib::ustring errMsg;
187 if (!g_file_test(prefsFilename.c_str(), G_FILE_TEST_IS_REGULAR)) {
188 gchar *
msg = g_strdup_printf(_(
"The preferences file %s is not a regular file."),
189 Glib::filename_to_utf8(prefsFilename).c_str());
196 gchar *prefs_xml =
nullptr; gsize
len = 0;
197 if (!g_file_get_contents(prefsFilename.c_str(), &prefs_xml, &
len,
nullptr)) {
198 gchar *
msg = g_strdup_printf(_(
"The preferences file %s could not be read."),
199 Glib::filename_to_utf8(prefsFilename).c_str());
209 gchar *
msg = g_strdup_printf(_(
"The preferences file %s is not a valid XML document."),
210 Glib::filename_to_utf8(prefsFilename).c_str());
217 if (strcmp(prefs_read->
root()->
name(),
"inkscape")) {
218 gchar *
msg = g_strdup_printf(_(
"The file %s is not a valid Inkscape preferences file."),
219 Glib::filename_to_utf8(prefsFilename).c_str());
255 if (!utf8name.empty()) {
266 time_t sptime = time (
nullptr);
267 struct tm *sptm = localtime (&sptime);
269 strftime(sptstr, 256,
"%Y_%m_%d_%H_%M_%S", sptm);
271 char *new_name = g_strdup_printf(
"%s_%s.xml",
_prefs_filename.c_str(), sptstr);
277 if (retcode == 0) g_warning(
"%s %s.", _(
"Preferences file was backed up to"), new_name);
278 else g_warning(
"%s", _(
"There was an error trying to reset the preferences file."));
316 std::vector<Entry> temp;
320 temp.emplace_back(path +
'/' + g_quark_to_string(iter.key), iter.value.pointer());
334 std::vector<Glib::ustring> temp;
338 if (i->attribute(
"id") ==
nullptr) {
341 temp.push_back(path +
'/' + i->attribute(
"id"));
365 auto const &cacheResult = it->second;
438 static constexpr auto digits10 = std::numeric_limits<double>::digits10;
451 static constexpr auto digits10 = std::numeric_limits<double>::digits10;
474 Glib::ustring css_str;
484 Glib::ustring css_str;
503 g_assert( pref_path.at(0) ==
'/' );
509 gchar **splits = g_strsplit(pref_path.c_str(),
"/", 0);
511 for (
int part_i = 0; splits[part_i]; ++part_i) {
513 if (!splits[part_i][0]) {
522 if (!strcmp(splits[part_i],
child->attribute(
"id"))) {
536class Preferences::_ObserverData
546 observed_path(
std::move(path))
560 gchar
const *attr_name = g_quark_to_string(
name);
561 if ( _filter.empty() || (_filter == attr_name) ) {
563 Glib::ustring notify_path = _observer.observed_path;
566 std::vector<gchar const *> path_fragments;
567 notify_path.reserve(256);
573 path_fragments.push_back(n->attribute(
"id"));
576 for (std::vector<gchar const *>::reverse_iterator i = path_fragments.rbegin(); i != path_fragments.rend(); ++i) {
577 notify_path.push_back(
'/');
578 notify_path.append(*i);
582 notify_path.push_back(
'/');
583 notify_path.append(attr_name);
585 std::optional<Glib::ustring> new_value_converted = std::nullopt;
586 if (new_value.
pointer() !=
nullptr) {
587 new_value_converted = Glib::ustring(new_value.
pointer());
589 _observer.notify(Preferences::Entry(notify_path, new_value_converted));
599 _keySplit(pref_path, node_key, attr_key);
611 if (attr_key ==
child->attribute(
"id")) {
614 node_key = pref_path;
625 Glib::ustring node_key, attr_key;
630 o.
_data.reset(
new _ObserverData(
node, !attr_key.empty()));
635 if (o.
_data->_is_attr) {
641 g_warning(
"Failed to add a preference observer because the key does not exist: %s",
653 _ObserverData *priv_data = o.
_data.get();
655 if (priv_data->_is_attr) {
680 g_assert( pref_key.empty() || pref_key.at(0) ==
'/' );
689 gchar **splits = g_strsplit(pref_key.c_str(),
"/", 0);
692 for (
int part_i = 0; splits[part_i]; ++part_i) {
694 if (!splits[part_i][0]) {
699 if (
child->attribute(
"id") ==
nullptr) {
702 if (!strcmp(splits[part_i],
child->attribute(
"id"))) {
712 while(splits[part_i]) {
743 Glib::ustring node_key, attr_key;
748 if (
node ==
nullptr ) {
752 if ( attr ==
nullptr ) {
761 Glib::ustring node_key, attr_key;
783 auto const &s = _value.value().raw();
786 return (s ==
"1" || s ==
"0" || s ==
"true" || s ==
"false");
795 auto const &s = _value.value().raw();
799 if (s ==
"true" || s ==
"false") {
801 g_warning(
"Integer preference value are set as boolean: '%s', treating it as %d: %s", s.c_str(),
802 s ==
"true" ? 1 : 0, _pref_path.c_str());
808 const char* cstr = s.c_str();
809 char* endPtr =
nullptr;
810 long value = strtol(cstr, &endPtr, 0);
811 if (endPtr == cstr) {
817 if (errno == ERANGE || value < INT_MIN || value > INT_MAX) {
834 auto const &s = _value.value().raw();
837 const char* cstr = s.c_str();
838 char* end_ptr =
nullptr;
839 unsigned long value = strtoul(cstr, &end_ptr, 0);
840 if (end_ptr == cstr) {
843 if (errno == ERANGE || value > UINT_MAX) {
856 auto const &value_str = _value.value().raw();
857 std::string::size_type end_index = 0;
860 Glib::Ascii::strtod(value_str, end_index, 0);
861 }
catch (std::runtime_error
const &e) {
870 auto unit = value_str.substr(end_index);
896 return Colors::Color::parse(_value.value().raw()).has_value();
912 auto const &s = _value.value().raw();
914 if (s ==
"1" || s ==
"true") {
916 }
else if (s ==
"0" || s ==
"false") {
921 g_warning(
"Bool preference value has invalid format. '%s' (raw value: %s)", _pref_path.c_str(), s.c_str());
932 if (
auto res = Colors::Color::parse(_value.value().raw())) {
936 if (
auto res = Colors::Color::parse(def)) {
953 auto const &s = _value.value().raw();
955 g_warning(
"Integer preference value is set as true, treating it as 1: %s", _pref_path.c_str());
957 }
else if (s ==
"false") {
958 g_warning(
"Integer preference value is set as false, treating it as 0: %s", _pref_path.c_str());
967 val = (int)strtol(s.c_str(),
nullptr, 0);
968 if (errno == ERANGE) {
970 val = (int)strtoul(s.c_str(),
nullptr, 0);
971 if (errno == ERANGE) {
972 g_warning(
"Integer preference out of range: '%s' (raw value: %s)", _pref_path.c_str(), s.c_str());
984 return (val >= min && val <= max ? val : def);
999 unsigned int val = 0;
1000 auto const &s = _value.value();
1002 val = (
unsigned int)strtoul(s.c_str(),
nullptr, 0);
1003 if (errno == ERANGE) {
1004 g_warning(
"Unsigned integer preference out of range: '%s' (raw value: %s)", _pref_path.c_str(), s.c_str());
1015 g_assert(_value.has_value());
1016 if (cached_double) {
1017 return value_double;
1019 cached_double =
true;
1021 value_double = Glib::Ascii::strtod(_value.value().raw());
1022 }
catch (
const std::runtime_error &e) {
1024 g_warning(
"Double preference out of range: '%s' (raw value: %s)", _pref_path.c_str(), _value.value().c_str());
1026 return value_double;
1035 double val = _getDoubleAssumeExisting();
1036 if (requested_unit.length() == 0) {
1046 return (val >= min && val <= max ? val : def);
1051 return _value.value_or(def);
1065 auto const &pref_value_str = _value.value().raw();
1066 std::string::size_type end_index = 0;
1068 Glib::Ascii::strtod(pref_value_str, end_index, 0);
1069 }
catch (
const std::runtime_error &e) {
1072 if (end_index == 0) {
1079 g_warning(
"Double preference value has invalid format. Failed to extract unit for '%s' (raw value: %s)",
1080 _pref_path.c_str(), pref_value_str.c_str());
1083 value_unit = pref_value_str.substr(end_index, pref_value_str.size());
1117 Glib::ustring path_base = _pref_path;
1118 path_base.erase(0, path_base.rfind(
'/') + 1);
1124 Glib::ustring node_key, attr_key;
1125 _keySplit(prefPath, node_key, attr_key);
1135 attr_key = pref_path.substr(pref_path.rfind(
'/') + 1, Glib::ustring::npos);
1137 node_key = pref_path.substr(0, pref_path.rfind(
'/'));
1175 Glib::ustring path, std::function<
void (
const Preferences::Entry& new_value)> callback) {
1185 prefs->addObserver(*
this);
1194 _callback(prefs->getEntry(observed_path));
TODO: insert short description here.
void sp_attribute_purge_default_style(SPCSSAttr *css, unsigned int flags)
Remove CSS style properties with default values.
Utility functions related to parsing and validation of XML attributes.
@ SP_ATTRCLEAN_DEFAULT_REMOVE
Two-dimensional point that doubles as a vector.
std::string toString(bool opacity=true) const
Format the color as a css string and return it.
virtual void handleError(Glib::ustring const &primary, Glib::ustring const &secondary) const =0
Data type representing a typeless value of a preference.
unsigned int getUInt(unsigned int def=0) const
Interpret the preference as an unsigned integer.
bool isValidBool() const
Check if the preference value can be interpreted as a Boolean.
double getDouble(double def=0.0, Glib::ustring const &unit="") const
Interpret the preference as a floating point value.
double _getDoubleAssumeExisting() const
get double value, assert that the value is set
bool isValidUInt() const
Check if the preference value can be interpreted as an unsigned integer.
Glib::ustring getEntryName() const
Get the last component of the preference's path.
Glib::ustring getString(Glib::ustring const &def="") const
Interpret the preference as an UTF-8 string.
bool isValidDouble() const
Check if the preference value can be interpreted as a floating point value.
int getIntLimited(int def=0, int min=INT_MIN, int max=INT_MAX) const
Interpret the preference as a limited integer.
Glib::ustring getUnit() const
Interpret the preference as a number followed by a unit (without space), and return this unit string.
bool isConvertibleTo(Glib::ustring const &unit) const
Check if the preference value can be converted to a particular unit.
bool isValidColor() const
Check if the preference value can be interpreted as a color.
bool getBool(bool def=false) const
Interpret the preference as a Boolean value.
SPCSSAttr * getInheritedStyle() const
Interpret the preference as a CSS style with directory-based inheritance.
int getInt(int def=0) const
Interpret the preference as an integer.
Colors::Color getColor(std::string const &def) const
Interpret the preference as a css color value.
double getDoubleLimited(double def=0.0, double min=DBL_MIN, double max=DBL_MAX, Glib::ustring const &unit="") const
Interpret the preference as a limited floating point value.
SPCSSAttr * getStyle() const
Interpret the preference as a CSS style.
bool isValidInt() const
Check if the preference value can be interpreted as an integer without any overflow.
Base class for preference observers.
std::unique_ptr< _ObserverData > _data
additional data used by the implementation while the observer is active
Glib::ustring const observed_path
Path which the observer watches.
Observer(Glib::ustring path)
Constructor.
Callback-based preferences observer.
void call()
Manually call the observer with the original, unchanged value.
PreferencesObserver(Glib::ustring path, std::function< void(const Preferences::Entry &new_value)> callback)
void notify(Preferences::Entry const &new_val) override
Notification about a preference change.
static std::unique_ptr< PreferencesObserver > create(Glib::ustring path, std::function< void(const Preferences::Entry &new_value)> callback)
Preference storage class.
void _load()
Load the user's customized preferences.
void setDoubleUnit(Glib::ustring const &pref_path, double value, Glib::ustring const &unit_abbr)
Set a floating point value with unit.
Glib::ustring getUnit(Glib::ustring const &pref_path)
Retrieve the unit string.
double getDouble(Glib::ustring const &pref_path, double def=0.0, Glib::ustring const &unit="")
Retrieve a floating point value.
std::vector< Entry > getAllEntries(Glib::ustring const &path)
Get all entries from the specified directory.
void reset()
Deletes the preferences.xml file.
void setStyle(Glib::ustring const &pref_path, SPCSSAttr *style)
Set a CSS style.
void _keySplit(Glib::ustring const &pref_path, Glib::ustring &node_key, Glib::ustring &attr_key)
static Preferences * get()
Access the singleton Preferences object.
bool getLastError(Glib::ustring &primary, Glib::ustring &secondary)
Return details of the last encountered error, if any.
Colors::Color getColor(Glib::ustring const &pref_path, std::string const &def="black")
Glib::ustring getPrefsFilename() const
Get the preferences file name in UTF-8.
void setUInt(Glib::ustring const &pref_path, unsigned int value)
Set an unsigned integer value.
ErrorReporter * _errorHandler
Pointer to object reporting errors.
Entry const getEntry(Glib::ustring const &pref_path)
Retrieve a preference entry without specifying its type.
SPCSSAttr * getStyle(Glib::ustring const &pref_path)
Retrieve a CSS style.
XML::Node * _findObserverNode(Glib::ustring const &pref_path, Glib::ustring &node_key, Glib::ustring &attr_key, bool create)
Find the XML node to observe.
static Preferences * _instance
std::optional< Glib::ustring > _getRawValue(Glib::ustring const &path)
Get raw value for preference path, without any caching.
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
static _ObserverData * _get_pref_observer_data(Observer &o)
void _setRawValue(Glib::ustring const &path, Glib::ustring const &value)
std::unique_ptr< PreferencesObserver > createObserver(Glib::ustring path, std::function< void(const Preferences::Entry &new_value)> callback)
Create an observer watching preference 'path' and calling provided function when preference changes.
void setColor(Glib::ustring const &pref_path, Colors::Color const &color)
Set an RGBA color value.
void setString(Glib::ustring const &pref_path, Glib::ustring const &value)
Set an UTF-8 string value.
Glib::ustring _lastErrPrimary
Last primary error message, if any.
Glib::ustring _lastErrSecondary
Last secondary error message, if any.
void setErrorHandler(ErrorReporter *handler)
void removeObserver(Observer &)
Remove an observer an prevent further notifications to it.
friend class PrefNodeObserver
SPCSSAttr * _getInheritedStyleForPath(Glib::ustring const &prefPath)
Interpret the preference as a CSS style with directory-based inheritance.
void _loadDefaults()
Load internal defaults.
XML::Document * _prefs_doc
XML document storing all the preferences.
XML::Node * _getNode(Glib::ustring const &pref_path, bool create=false)
Get the XML node corresponding to the given pref key.
void _reportError(Glib::ustring const &, Glib::ustring const &)
std::vector< Glib::ustring > getAllDirs(Glib::ustring const &path)
Get all subdirectories of the specified directory.
void setPoint(Glib::ustring const &pref_path, Geom::Point value)
Set a point value.
bool _writable
Will the preferences be saved at exit?
bool _hasError
Indication that some error has occurred;.
void setDouble(Glib::ustring const &pref_path, double value)
Set a floating point value.
void setInt(Glib::ustring const &pref_path, int value)
Set an integer value.
static void unload(bool save=true)
Unload all preferences.
void addObserver(Observer &)
Register a preference observer.
void setBool(Glib::ustring const &pref_path, bool value)
Set a Boolean value.
bool _initialized
Is this instance fully initialized? Caching should be avoided before.
void mergeStyle(Glib::ustring const &pref_path, SPCSSAttr *style)
Merge a CSS style with the current preference value.
std::unordered_map< std::string, Entry > cachedEntry
Cache for getEntry()
std::string _prefs_filename
Full filename (with directory) of the prefs file.
void save()
Save all preferences to the hard disk.
_ObsMap _observer_map
Map that keeps track of wrappers assigned to PrefObservers.
void remove(Glib::ustring const &pref_path)
Remove a node from prefs.
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
bool hasUnit(Glib::ustring const &name) const
Remove a unit definition from the given unit type table * / DISABLED, unsafe with the current passing...
Unit const * getUnit(Glib::ustring const &name) const
Retrieve a given unit based on its string identifier.
bool compatibleWith(Unit const *u) const
Checks if a unit is compatible with the specified unit.
char const * pointer() const
Interface for XML node observers.
Interface for refcounted XML nodes.
virtual Node * parent()=0
Get the parent of this node.
virtual void addSubtreeObserver(NodeObserver &observer)=0
Add an object that will be notified of the changes to this node and its descendants.
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
virtual char const * name() const =0
Get the name of the element node.
virtual const AttributeVector & attributeList() const =0
Get a list of the node's attributes.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
virtual void removeSubtreeObserver(NodeObserver &observer)=0
Remove an object from the subtree observers list.
virtual Node * firstChild()=0
Get the first child of this node.
virtual void mergeFrom(Node const *src, char const *key, bool extension=false, bool clean=false)=0
Merge all children of another node with the current.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
void removeAttribute(Inkscape::Util::const_char_ptr key)
Remove an attribute of this node.
virtual void addObserver(NodeObserver &observer)=0
Add an object that will be notified of the changes to this node.
virtual Document * document()=0
Get the node's associated document.
virtual void removeChild(Node *child)=0
Remove a child of this node.
virtual void removeObserver(NodeObserver &observer)=0
Remove an object from the list of observers.
virtual Node * root()=0
Get the root node of this node's document.
static char const *const current
Inkscape::XML::Node * node
static R & release(R &r)
Decrements the reference count of a anchored object.
std::string profile_path()
Glib::ustring format_classic(T const &... args)
Helper class to stream background task notifications as a series of messages.
static Inkscape::XML::Document * migrateFromDoc
static Inkscape::XML::Document * loadImpl(std::string const &prefsFilename, Glib::ustring &errMsg)
std::unique_ptr< Preferences::PreferencesObserver > PrefObserver
static void migrateDetails(Inkscape::XML::Document *from, Inkscape::XML::Document *to)
Interface for XML node observers.
TODO: insert short description here.
static char const preferences_skeleton[]
Singleton class to access the preferences file in a convenient way.
SPCSSAttr * sp_repr_css_attr_new()
Creates an empty SPCSSAttr (a class for manipulating CSS style properties).
void sp_repr_css_write_string(SPCSSAttr *css, Glib::ustring &str)
Write a style attribute string from a list of properties stored in an SPCSAttr object.
void sp_repr_css_merge(SPCSSAttr *dst, SPCSSAttr *src)
Merges two SPCSSAttr's.
SPCSSAttr * sp_repr_css_attr_inherited(Node const *repr, gchar const *attr)
Creates a new SPCSSAttr with one attribute whose value is determined by cascading.
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
void sp_repr_css_attr_add_from_string(SPCSSAttr *css, gchar const *p)
Use libcroco to parse a string for CSS properties and then merge them into an existing SPCSSAttr.
Document * sp_repr_read_mem(const gchar *buffer, gint length, const gchar *default_ns)
Reads and parses XML from a buffer, returning it as an Document.
bool sp_repr_save_file(Document *doc, gchar const *const filename, gchar const *default_ns)
Returns true iff file successfully saved.
Inkscape::IO::Resource - simple resource API.
Interface for XML documents.
virtual Node * createElement(char const *name)=0