1
0

Settings.h 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. /*
  2. * Settings.h
  3. * ----------
  4. * Purpose: Header file for application setting handling framework.
  5. * Notes : (currently none)
  6. * Authors: Joern Heusipp
  7. * OpenMPT Devs
  8. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  9. */
  10. #pragma once
  11. #include "openmpt/all/BuildSettings.hpp"
  12. #include "../common/misc_util.h"
  13. #include "mpt/mutex/mutex.hpp"
  14. #include <map>
  15. #include <set>
  16. #include <variant>
  17. OPENMPT_NAMESPACE_BEGIN
  18. enum SettingType
  19. {
  20. SettingTypeNone,
  21. SettingTypeBool,
  22. SettingTypeInt,
  23. SettingTypeFloat,
  24. SettingTypeString,
  25. SettingTypeBinary,
  26. };
  27. // SettingValue is a variant type that stores any type that can natively be represented in a config backend.
  28. // Any other type that should be stored must provide a matching ToSettingValue and FromSettingValue.
  29. // Other types can optionally also set a type tag which would get checked in debug builds.
  30. class SettingValue
  31. {
  32. private:
  33. std::variant<std::monostate, bool, int32, double, mpt::ustring, std::vector<std::byte>> value;
  34. std::string typeTag;
  35. public:
  36. bool operator == (const SettingValue &other) const
  37. {
  38. return value == other.value && typeTag == other.typeTag;
  39. }
  40. bool operator != (const SettingValue &other) const
  41. {
  42. return !(*this == other);
  43. }
  44. SettingValue()
  45. {
  46. }
  47. SettingValue(const SettingValue &other)
  48. {
  49. *this = other;
  50. }
  51. SettingValue & operator = (const SettingValue &other)
  52. {
  53. if(this == &other)
  54. {
  55. return *this;
  56. }
  57. MPT_ASSERT(value.index() == 0 || (value.index() == other.value.index() && typeTag == other.typeTag));
  58. value = other.value;
  59. typeTag = other.typeTag;
  60. return *this;
  61. }
  62. SettingValue(bool val)
  63. : value(val)
  64. {
  65. }
  66. SettingValue(int32 val)
  67. : value(val)
  68. {
  69. }
  70. SettingValue(double val)
  71. : value(val)
  72. {
  73. }
  74. SettingValue(const mpt::ustring &val)
  75. : value(val)
  76. {
  77. }
  78. SettingValue(const std::vector<std::byte> &val)
  79. : value(val)
  80. {
  81. }
  82. SettingValue(bool val, const std::string &typeTag_)
  83. : value(val)
  84. , typeTag(typeTag_)
  85. {
  86. }
  87. SettingValue(int32 val, const std::string &typeTag_)
  88. : value(val)
  89. , typeTag(typeTag_)
  90. {
  91. }
  92. SettingValue(double val, const std::string &typeTag_)
  93. : value(val)
  94. , typeTag(typeTag_)
  95. {
  96. }
  97. SettingValue(const mpt::ustring &val, const std::string &typeTag_)
  98. : value(val)
  99. , typeTag(typeTag_)
  100. {
  101. }
  102. SettingValue(const std::vector<std::byte> &val, const std::string &typeTag_)
  103. : value(val)
  104. , typeTag(typeTag_)
  105. {
  106. }
  107. // these need to be explicitly deleted because otherwise the bool overload will catch the pointers
  108. SettingValue(const char *val) = delete;
  109. SettingValue(const wchar_t *val) = delete;
  110. SettingValue(const char *val, const std::string &typeTag_) = delete;
  111. SettingValue(const wchar_t *val, const std::string &typeTag_) = delete;
  112. SettingType GetType() const
  113. {
  114. SettingType result = SettingTypeNone;
  115. if(std::holds_alternative<bool>(value))
  116. {
  117. result = SettingTypeBool;
  118. }
  119. if(std::holds_alternative<int32>(value))
  120. {
  121. result = SettingTypeInt;
  122. }
  123. if(std::holds_alternative<double>(value))
  124. {
  125. result = SettingTypeFloat;
  126. }
  127. if(std::holds_alternative<mpt::ustring>(value))
  128. {
  129. result = SettingTypeString;
  130. }
  131. if(std::holds_alternative<std::vector<std::byte>>(value))
  132. {
  133. result = SettingTypeBinary;
  134. }
  135. return result;
  136. }
  137. bool HasTypeTag() const
  138. {
  139. return !typeTag.empty();
  140. }
  141. std::string GetTypeTag() const
  142. {
  143. return typeTag;
  144. }
  145. template <typename T>
  146. T as() const
  147. {
  148. return *this;
  149. }
  150. operator bool () const
  151. {
  152. MPT_ASSERT(std::holds_alternative<bool>(value));
  153. return std::get<bool>(value);
  154. }
  155. operator int32 () const
  156. {
  157. MPT_ASSERT(std::holds_alternative<int32>(value));
  158. return std::get<int32>(value);
  159. }
  160. operator double () const
  161. {
  162. MPT_ASSERT(std::holds_alternative<double>(value));
  163. return std::get<double>(value);
  164. }
  165. operator mpt::ustring () const
  166. {
  167. MPT_ASSERT(std::holds_alternative<mpt::ustring>(value));
  168. return std::get<mpt::ustring>(value);
  169. }
  170. operator std::vector<std::byte> () const
  171. {
  172. MPT_ASSERT(std::holds_alternative<std::vector<std::byte>>(value));
  173. return std::get<std::vector<std::byte>>(value);
  174. }
  175. mpt::ustring FormatTypeAsString() const;
  176. mpt::ustring FormatValueAsString() const;
  177. void SetFromString(const AnyStringLocale &newVal);
  178. };
  179. template<typename T>
  180. std::vector<std::byte> EncodeBinarySetting(const T &val)
  181. {
  182. std::vector<std::byte> result(sizeof(T));
  183. std::memcpy(result.data(), &val, sizeof(T));
  184. return result;
  185. }
  186. template<typename T>
  187. T DecodeBinarySetting(const std::vector<std::byte> &val)
  188. {
  189. T result = T();
  190. if(val.size() >= sizeof(T))
  191. {
  192. std::memcpy(&result, val.data(), sizeof(T));
  193. }
  194. return result;
  195. }
  196. template<typename T>
  197. inline SettingValue ToSettingValue(const T &val)
  198. {
  199. return SettingValue(val);
  200. }
  201. template<typename T>
  202. inline T FromSettingValue(const SettingValue &val)
  203. {
  204. return val.as<T>();
  205. }
  206. // To support settings.Read<Tcustom> and settings.Write<Tcustom>,
  207. // just provide specializations of ToSettingsValue<Tcustom> and FromSettingValue<Tcustom>.
  208. // You may use the SettingValue(value, typeTag) constructor in ToSettingValue
  209. // and check the typeTag FromSettingsValue to implement runtime type-checking for custom types.
  210. template<> inline SettingValue ToSettingValue(const std::string &val) { return SettingValue(mpt::ToUnicode(mpt::Charset::Locale, val)); }
  211. template<> inline std::string FromSettingValue(const SettingValue &val) { return mpt::ToCharset(mpt::Charset::Locale, val.as<mpt::ustring>()); }
  212. template<> inline SettingValue ToSettingValue(const mpt::lstring &val) { return SettingValue(mpt::ToUnicode(val)); }
  213. template<> inline mpt::lstring FromSettingValue(const SettingValue &val) { return mpt::ToLocale(val.as<mpt::ustring>()); }
  214. #if !MPT_USTRING_MODE_WIDE
  215. template<> inline SettingValue ToSettingValue(const std::wstring &val) { return SettingValue(mpt::ToUnicode(val)); }
  216. template<> inline std::wstring FromSettingValue(const SettingValue &val) { return mpt::ToWide(val.as<mpt::ustring>()); }
  217. #endif
  218. template<> inline SettingValue ToSettingValue(const CString &val) { return SettingValue(mpt::ToUnicode(val)); }
  219. template<> inline CString FromSettingValue(const SettingValue &val) { return mpt::ToCString(val.as<mpt::ustring>()); }
  220. template<> inline SettingValue ToSettingValue(const mpt::PathString &val) { return SettingValue(val.ToUnicode()); }
  221. template<> inline mpt::PathString FromSettingValue(const SettingValue &val) { return mpt::PathString::FromUnicode(val); }
  222. template<> inline SettingValue ToSettingValue(const float &val) { return SettingValue(double(val)); }
  223. template<> inline float FromSettingValue(const SettingValue &val) { return float(val.as<double>()); }
  224. template<> inline SettingValue ToSettingValue(const int64 &val) { return SettingValue(mpt::ufmt::dec(val), "int64"); }
  225. template<> inline int64 FromSettingValue(const SettingValue &val) { return ConvertStrTo<int64>(val.as<mpt::ustring>()); }
  226. template<> inline SettingValue ToSettingValue(const uint64 &val) { return SettingValue(mpt::ufmt::dec(val), "uint64"); }
  227. template<> inline uint64 FromSettingValue(const SettingValue &val) { return ConvertStrTo<uint64>(val.as<mpt::ustring>()); }
  228. template<> inline SettingValue ToSettingValue(const uint32 &val) { return SettingValue(int32(val)); }
  229. template<> inline uint32 FromSettingValue(const SettingValue &val) { return uint32(val.as<int32>()); }
  230. template<> inline SettingValue ToSettingValue(const uint16 &val) { return SettingValue(int32(val)); }
  231. template<> inline uint16 FromSettingValue(const SettingValue &val) { return uint16(val.as<int32>()); }
  232. template<> inline SettingValue ToSettingValue(const uint8 &val) { return SettingValue(int32(val)); }
  233. template<> inline uint8 FromSettingValue(const SettingValue &val) { return uint8(val.as<int32>()); }
  234. template<> inline SettingValue ToSettingValue(const LONG &val) { return SettingValue(int32(val)); }
  235. template<> inline LONG FromSettingValue(const SettingValue &val) { return LONG(val.as<int32>()); }
  236. // An instance of SetttingState represents the cached on-disk state of a certain SettingPath.
  237. // The mapping is stored externally in SettingsContainer::map.
  238. class SettingState
  239. {
  240. private:
  241. SettingValue value;
  242. const SettingValue defaultValue;
  243. bool dirty;
  244. public:
  245. SettingState()
  246. : dirty(false)
  247. {
  248. return;
  249. }
  250. SettingState(const SettingValue &def)
  251. : value(def)
  252. , defaultValue(def)
  253. , dirty(false)
  254. {
  255. return;
  256. }
  257. SettingState & assign(const SettingValue &other, bool setDirty = true)
  258. {
  259. MPT_ASSERT(defaultValue.GetType() == SettingTypeNone || (defaultValue.GetType() == other.GetType() && defaultValue.GetTypeTag() == other.GetTypeTag()));
  260. if(setDirty)
  261. {
  262. if(value != other)
  263. {
  264. value = other;
  265. dirty = true;
  266. }
  267. } else
  268. {
  269. value = other;
  270. }
  271. return *this;
  272. }
  273. SettingState & operator = (const SettingValue &val)
  274. {
  275. assign(val);
  276. return *this;
  277. }
  278. SettingValue GetDefault() const
  279. {
  280. return defaultValue;
  281. }
  282. const SettingValue &GetRefDefault() const
  283. {
  284. return defaultValue;
  285. }
  286. bool IsDefault() const
  287. {
  288. return value == defaultValue;
  289. }
  290. bool IsDirty() const
  291. {
  292. return dirty;
  293. }
  294. void Clean()
  295. {
  296. dirty = false;
  297. }
  298. SettingValue GetValue() const
  299. {
  300. return value;
  301. }
  302. const SettingValue &GetRefValue() const
  303. {
  304. return value;
  305. }
  306. operator SettingValue () const
  307. {
  308. return value;
  309. }
  310. };
  311. // SettingPath represents the path in a config backend to a certain setting.
  312. class SettingPath
  313. {
  314. private:
  315. mpt::ustring section;
  316. mpt::ustring key;
  317. public:
  318. SettingPath()
  319. {
  320. return;
  321. }
  322. SettingPath(mpt::ustring section_, mpt::ustring key_)
  323. : section(std::move(section_))
  324. , key(std::move(key_))
  325. {
  326. return;
  327. }
  328. mpt::ustring GetSection() const
  329. {
  330. return section;
  331. }
  332. mpt::ustring GetKey() const
  333. {
  334. return key;
  335. }
  336. const mpt::ustring &GetRefSection() const
  337. {
  338. return section;
  339. }
  340. const mpt::ustring &GetRefKey() const
  341. {
  342. return key;
  343. }
  344. int compare(const SettingPath &other) const
  345. {
  346. int cmp_section = section.compare(other.section);
  347. if(cmp_section)
  348. {
  349. return cmp_section;
  350. }
  351. int cmp_key = key.compare(other.key);
  352. return cmp_key;
  353. }
  354. mpt::ustring FormatAsString() const
  355. {
  356. return section + U_(".") + key;
  357. }
  358. };
  359. inline bool operator < (const SettingPath &left, const SettingPath &right) { return left.compare(right) < 0; }
  360. inline bool operator <= (const SettingPath &left, const SettingPath &right) { return left.compare(right) <= 0; }
  361. inline bool operator > (const SettingPath &left, const SettingPath &right) { return left.compare(right) > 0; }
  362. inline bool operator >= (const SettingPath &left, const SettingPath &right) { return left.compare(right) >= 0; }
  363. inline bool operator == (const SettingPath &left, const SettingPath &right) { return left.compare(right) == 0; }
  364. inline bool operator != (const SettingPath &left, const SettingPath &right) { return left.compare(right) != 0; }
  365. class ISettingsBackend
  366. {
  367. public:
  368. virtual SettingValue ReadSetting(const SettingPath &path, const SettingValue &def) const = 0;
  369. virtual void WriteSetting(const SettingPath &path, const SettingValue &val) = 0;
  370. virtual void RemoveSetting(const SettingPath &path) = 0;
  371. virtual void RemoveSection(const mpt::ustring &section) = 0;
  372. protected:
  373. virtual ~ISettingsBackend() = default;
  374. };
  375. class ISettingChanged
  376. {
  377. public:
  378. virtual void SettingChanged(const SettingPath &changedPath) = 0;
  379. protected:
  380. virtual ~ISettingChanged() = default;
  381. };
  382. enum SettingFlushMode
  383. {
  384. SettingWriteBack = 0,
  385. SettingWriteThrough = 1,
  386. };
  387. // SettingContainer basically represents a frontend to 1 or 2 backends (e.g. ini files or registry subtrees) for a collection of configuration settings.
  388. // SettingContainer provides basic read/write access to individual setting. The values are cached and only flushed on destruction or explicit flushs.
  389. class SettingsContainer
  390. {
  391. public:
  392. using SettingsMap = std::map<SettingPath,SettingState>;
  393. using SettingsListenerMap = std::map<SettingPath,std::set<ISettingChanged*>>;
  394. void WriteSettings();
  395. private:
  396. mutable SettingsMap map;
  397. mutable SettingsListenerMap mapListeners;
  398. private:
  399. ISettingsBackend *backend;
  400. private:
  401. bool immediateFlush = false;
  402. SettingValue BackendsReadSetting(const SettingPath &path, const SettingValue &def) const;
  403. void BackendsWriteSetting(const SettingPath &path, const SettingValue &val);
  404. void BackendsRemoveSetting(const SettingPath &path);
  405. void BackendsRemoveSection(const mpt::ustring &section);
  406. void NotifyListeners(const SettingPath &path);
  407. SettingValue ReadSetting(const SettingPath &path, const SettingValue &def) const;
  408. bool IsDefaultSetting(const SettingPath &path) const;
  409. void WriteSetting(const SettingPath &path, const SettingValue &val, SettingFlushMode flushMode);
  410. void ForgetSetting(const SettingPath &path);
  411. void RemoveSetting(const SettingPath &path);
  412. void RemoveSection(const mpt::ustring &section);
  413. private:
  414. SettingsContainer(const SettingsContainer &other); // disable
  415. SettingsContainer& operator = (const SettingsContainer &other); // disable
  416. public:
  417. SettingsContainer(ISettingsBackend *backend);
  418. void SetImmediateFlush(bool newImmediateFlush);
  419. template <typename T>
  420. T Read(const SettingPath &path, const T &def = T()) const
  421. {
  422. return FromSettingValue<T>(ReadSetting(path, ToSettingValue<T>(def)));
  423. }
  424. template <typename T>
  425. T Read(mpt::ustring section, mpt::ustring key, const T &def = T()) const
  426. {
  427. return FromSettingValue<T>(ReadSetting(SettingPath(std::move(section), std::move(key)), ToSettingValue<T>(def)));
  428. }
  429. bool IsDefault(const SettingPath &path) const
  430. {
  431. return IsDefaultSetting(path);
  432. }
  433. bool IsDefault(mpt::ustring section, mpt::ustring key) const
  434. {
  435. return IsDefaultSetting(SettingPath(std::move(section), std::move(key)));
  436. }
  437. template <typename T>
  438. void Write(const SettingPath &path, const T &val, SettingFlushMode flushMode = SettingWriteBack)
  439. {
  440. WriteSetting(path, ToSettingValue<T>(val), flushMode);
  441. }
  442. template <typename T>
  443. void Write(mpt::ustring section, mpt::ustring key, const T &val, SettingFlushMode flushMode = SettingWriteBack)
  444. {
  445. WriteSetting(SettingPath(std::move(section), std::move(key)), ToSettingValue<T>(val), flushMode);
  446. }
  447. void Forget(const SettingPath &path)
  448. {
  449. ForgetSetting(path);
  450. }
  451. void Forget(mpt::ustring section, mpt::ustring key)
  452. {
  453. ForgetSetting(SettingPath(std::move(section), std::move(key)));
  454. }
  455. void ForgetAll();
  456. void Remove(const SettingPath &path)
  457. {
  458. RemoveSetting(path);
  459. }
  460. void Remove(mpt::ustring section, mpt::ustring key)
  461. {
  462. RemoveSetting(SettingPath(std::move(section), std::move(key)));
  463. }
  464. void Remove(const mpt::ustring &section)
  465. {
  466. RemoveSection(section);
  467. }
  468. void Flush();
  469. ~SettingsContainer();
  470. public:
  471. void Register(ISettingChanged *listener, const SettingPath &path);
  472. void UnRegister(ISettingChanged *listener, const SettingPath &path);
  473. SettingsMap::const_iterator begin() const { return map.begin(); }
  474. SettingsMap::const_iterator end() const { return map.end(); }
  475. SettingsMap::size_type size() const { return map.size(); }
  476. bool empty() const { return map.empty(); }
  477. const SettingsMap &GetMap() const { return map; }
  478. };
  479. // Setting<T> and CachedSetting<T> are references to a SettingPath below a SettingConainer (both provided to the constructor).
  480. // They should mostly behave like normal non-reference variables of type T. I.e., they can be assigned to and read from.
  481. // As they have actual reference semantics, all Setting<T> or CachedSetting<T> that access the same path consistently have the same value.
  482. // The difference between the 2 lies in the way this consistency is achieved:
  483. // Setting<T>: The actual value is not stored in an instance of Setting<T>.
  484. // Instead, it is read/written and converted on every access from the SettingContainer.
  485. // In the SettingContainer, each SettingPath is mapped to a single instance of SettingValue, so there cannot be any incoherence.
  486. // CachedSetting<T>: The value, readily converted to T, is stored directly in each instance of CachedSetting<T>.
  487. // A callback for its SettingPath is registered with SettingContainer, and on every change to this SettingPath, the value gets re-read and updated.
  488. // Setting<T> implies some overhead on every access but is generally simpler to understand.
  489. // CachedSetting<T> implies overhead in stored (the copy of T and the callback pointers).
  490. // Except for the difference in runtime/space characteristics, Setting<T> and CachedSetting<T> behave exactly the same way.
  491. // It is recommended to only use CachedSetting<T> for settings that get read frequently, i.e. during GUI updates (like in the pattern view).
  492. template <typename T>
  493. class Setting
  494. {
  495. private:
  496. SettingsContainer &conf;
  497. const SettingPath path;
  498. public:
  499. Setting(const Setting &other) = delete;
  500. Setting & operator = (const Setting &other) = delete;
  501. public:
  502. Setting(SettingsContainer &conf_, mpt::ustring section, mpt::ustring key, const T&def)
  503. : conf(conf_)
  504. , path(std::move(section), std::move(key))
  505. {
  506. conf.Read(path, def); // set default value
  507. }
  508. Setting(SettingsContainer &conf_, const SettingPath &path_, const T&def)
  509. : conf(conf_)
  510. , path(path_)
  511. {
  512. conf.Read(path, def); // set default value
  513. }
  514. SettingPath GetPath() const
  515. {
  516. return path;
  517. }
  518. Setting & operator = (const T &val)
  519. {
  520. conf.Write(path, val);
  521. return *this;
  522. }
  523. operator T () const
  524. {
  525. return conf.Read<T>(path);
  526. }
  527. T Get() const
  528. {
  529. return conf.Read<T>(path);
  530. }
  531. bool IsDefault() const
  532. {
  533. return conf.IsDefault(path);
  534. }
  535. template<typename Trhs> Setting & operator += (const Trhs &rhs) { T tmp = *this; tmp += rhs; *this = tmp; return *this; }
  536. template<typename Trhs> Setting & operator -= (const Trhs &rhs) { T tmp = *this; tmp -= rhs; *this = tmp; return *this; }
  537. template<typename Trhs> Setting & operator *= (const Trhs &rhs) { T tmp = *this; tmp *= rhs; *this = tmp; return *this; }
  538. template<typename Trhs> Setting & operator /= (const Trhs &rhs) { T tmp = *this; tmp /= rhs; *this = tmp; return *this; }
  539. template<typename Trhs> Setting & operator %= (const Trhs &rhs) { T tmp = *this; tmp %= rhs; *this = tmp; return *this; }
  540. template<typename Trhs> Setting & operator |= (const Trhs &rhs) { T tmp = *this; tmp |= rhs; *this = tmp; return *this; }
  541. template<typename Trhs> Setting & operator &= (const Trhs &rhs) { T tmp = *this; tmp &= rhs; *this = tmp; return *this; }
  542. template<typename Trhs> Setting & operator ^= (const Trhs &rhs) { T tmp = *this; tmp ^= rhs; *this = tmp; return *this; }
  543. };
  544. template <typename T>
  545. class CachedSetting
  546. : public ISettingChanged
  547. {
  548. private:
  549. mutable mpt::mutex valueMutex;
  550. T value;
  551. SettingsContainer &conf;
  552. const SettingPath path;
  553. public:
  554. CachedSetting(const CachedSetting &other) = delete;
  555. CachedSetting & operator = (const CachedSetting &other) = delete;
  556. public:
  557. CachedSetting(SettingsContainer &conf_, mpt::ustring section, mpt::ustring key, const T&def)
  558. : value(def)
  559. , conf(conf_)
  560. , path(std::move(section), std::move(key))
  561. {
  562. {
  563. mpt::lock_guard<mpt::mutex> l(valueMutex);
  564. value = conf.Read(path, def);
  565. }
  566. conf.Register(this, path);
  567. }
  568. CachedSetting(SettingsContainer &conf_, const SettingPath &path_, const T&def)
  569. : value(def)
  570. , conf(conf_)
  571. , path(path_)
  572. {
  573. {
  574. mpt::lock_guard<mpt::mutex> l(valueMutex);
  575. value = conf.Read(path, def);
  576. }
  577. conf.Register(this, path);
  578. }
  579. ~CachedSetting()
  580. {
  581. conf.UnRegister(this, path);
  582. }
  583. SettingPath GetPath() const
  584. {
  585. return path;
  586. }
  587. CachedSetting & operator = (const T &val)
  588. {
  589. {
  590. mpt::lock_guard<mpt::mutex> l(valueMutex);
  591. value = val;
  592. }
  593. conf.Write(path, val);
  594. return *this;
  595. }
  596. operator T () const
  597. {
  598. mpt::lock_guard<mpt::mutex> l(valueMutex);
  599. return value;
  600. }
  601. T Get() const
  602. {
  603. mpt::lock_guard<mpt::mutex> l(valueMutex);
  604. return value;
  605. }
  606. bool IsDefault() const
  607. {
  608. return conf.IsDefault(path);
  609. }
  610. CachedSetting & Update()
  611. {
  612. {
  613. mpt::lock_guard<mpt::mutex> l(valueMutex);
  614. value = conf.Read<T>(path);
  615. }
  616. return *this;
  617. }
  618. void SettingChanged(const SettingPath &changedPath)
  619. {
  620. MPT_UNREFERENCED_PARAMETER(changedPath);
  621. Update();
  622. }
  623. template<typename Trhs> CachedSetting & operator += (const Trhs &rhs) { T tmp = *this; tmp += rhs; *this = tmp; return *this; }
  624. template<typename Trhs> CachedSetting & operator -= (const Trhs &rhs) { T tmp = *this; tmp -= rhs; *this = tmp; return *this; }
  625. template<typename Trhs> CachedSetting & operator *= (const Trhs &rhs) { T tmp = *this; tmp *= rhs; *this = tmp; return *this; }
  626. template<typename Trhs> CachedSetting & operator /= (const Trhs &rhs) { T tmp = *this; tmp /= rhs; *this = tmp; return *this; }
  627. template<typename Trhs> CachedSetting & operator %= (const Trhs &rhs) { T tmp = *this; tmp %= rhs; *this = tmp; return *this; }
  628. template<typename Trhs> CachedSetting & operator |= (const Trhs &rhs) { T tmp = *this; tmp |= rhs; *this = tmp; return *this; }
  629. template<typename Trhs> CachedSetting & operator &= (const Trhs &rhs) { T tmp = *this; tmp &= rhs; *this = tmp; return *this; }
  630. template<typename Trhs> CachedSetting & operator ^= (const Trhs &rhs) { T tmp = *this; tmp ^= rhs; *this = tmp; return *this; }
  631. };
  632. class IniFileSettingsBackend : public ISettingsBackend
  633. {
  634. private:
  635. const mpt::PathString filename;
  636. private:
  637. std::vector<std::byte> ReadSettingRaw(const SettingPath &path, const std::vector<std::byte> &def) const;
  638. mpt::ustring ReadSettingRaw(const SettingPath &path, const mpt::ustring &def) const;
  639. double ReadSettingRaw(const SettingPath &path, double def) const;
  640. int32 ReadSettingRaw(const SettingPath &path, int32 def) const;
  641. bool ReadSettingRaw(const SettingPath &path, bool def) const;
  642. void WriteSettingRaw(const SettingPath &path, const std::vector<std::byte> &val);
  643. void WriteSettingRaw(const SettingPath &path, const mpt::ustring &val);
  644. void WriteSettingRaw(const SettingPath &path, double val);
  645. void WriteSettingRaw(const SettingPath &path, int32 val);
  646. void WriteSettingRaw(const SettingPath &path, bool val);
  647. void RemoveSettingRaw(const SettingPath &path);
  648. void RemoveSectionRaw(const mpt::ustring &section);
  649. static mpt::winstring GetSection(const SettingPath &path);
  650. static mpt::winstring GetKey(const SettingPath &path);
  651. public:
  652. IniFileSettingsBackend(const mpt::PathString &filename);
  653. ~IniFileSettingsBackend() override;
  654. void ConvertToUnicode(const mpt::ustring &backupTag = mpt::ustring());
  655. virtual SettingValue ReadSetting(const SettingPath &path, const SettingValue &def) const override;
  656. virtual void WriteSetting(const SettingPath &path, const SettingValue &val) override;
  657. virtual void RemoveSetting(const SettingPath &path) override;
  658. virtual void RemoveSection(const mpt::ustring &section) override;
  659. const mpt::PathString& GetFilename() const { return filename; }
  660. };
  661. class IniFileSettingsContainer : private IniFileSettingsBackend, public SettingsContainer
  662. {
  663. public:
  664. IniFileSettingsContainer(const mpt::PathString &filename);
  665. ~IniFileSettingsContainer() override;
  666. };
  667. class DefaultSettingsContainer : public IniFileSettingsContainer
  668. {
  669. public:
  670. DefaultSettingsContainer();
  671. ~DefaultSettingsContainer() override;
  672. };
  673. class SettingChangedNotifyGuard
  674. {
  675. private:
  676. SettingsContainer &conf;
  677. SettingPath m_Path;
  678. bool m_Registered;
  679. ISettingChanged *m_Handler;
  680. public:
  681. SettingChangedNotifyGuard(SettingsContainer &conf, const SettingPath &path)
  682. : conf(conf)
  683. , m_Path(path)
  684. , m_Registered(false)
  685. , m_Handler(nullptr)
  686. {
  687. return;
  688. }
  689. void Register(ISettingChanged *handler)
  690. {
  691. if(m_Registered)
  692. {
  693. return;
  694. }
  695. m_Handler = handler;
  696. conf.Register(m_Handler, m_Path);
  697. m_Registered = true;
  698. }
  699. ~SettingChangedNotifyGuard()
  700. {
  701. if(m_Registered)
  702. {
  703. conf.UnRegister(m_Handler, m_Path);
  704. m_Registered = false;
  705. }
  706. }
  707. };
  708. OPENMPT_NAMESPACE_END