1 /*
2  Copyright (C) 2010-2014 Kristian Duske
3 
4  This file is part of TrenchBroom.
5 
6  TrenchBroom is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  TrenchBroom is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef TrenchBroom_Preference
21 #define TrenchBroom_Preference
22 
23 #include "Color.h"
24 #include "ConfigTypes.h"
25 #include "Exceptions.h"
26 #include "Macros.h"
27 #include "StringUtils.h"
28 #include "IO/ConfigParser.h"
29 #include "IO/Path.h"
30 #include "View/KeyboardShortcut.h"
31 
32 #include <wx/config.h>
33 #include <wx/confbase.h>
34 #include <wx/tokenzr.h>
35 
36 namespace TrenchBroom {
37     template <typename T>
38     class PreferenceSerializer {
39     public:
read(wxConfigBase * config,const IO::Path & path,T & result)40         bool read(wxConfigBase* config, const IO::Path& path, T& result) const { return false; }
write(wxConfigBase * config,const IO::Path & path,const T & value)41         bool write(wxConfigBase* config, const IO::Path& path, const T& value) const { return false; }
42     };
43 
44     template <>
45     class PreferenceSerializer<bool> {
46     public:
read(wxConfigBase * config,const IO::Path & path,bool & result)47         bool read(wxConfigBase* config, const IO::Path& path, bool& result) const {
48             wxString string;
49             if (config->Read(path.asString('/'), &string)) {
50                 long longValue = 0;
51                 if (string.ToLong(&longValue)) {
52                     result = longValue != 0L;
53                     return true;
54                 }
55             }
56             return false;
57         }
58 
write(wxConfigBase * config,const IO::Path & path,const bool & value)59         bool write(wxConfigBase* config, const IO::Path& path, const bool& value) const {
60             wxString str;
61             str << (value ? 1 : 0);
62             return config->Write(path.asString('/'), str);
63         }
64     };
65 
66     template <>
67     class PreferenceSerializer<int> {
68     public:
read(wxConfigBase * config,const IO::Path & path,int & result)69         bool read(wxConfigBase* config, const IO::Path& path, int& result) const {
70             wxString string;
71             if (config->Read(path.asString('/'), &string)) {
72                 long longValue = 0;
73                 if (string.ToLong(&longValue) && longValue >= std::numeric_limits<int>::min() && longValue <= std::numeric_limits<int>::max()) {
74                     result = static_cast<int>(longValue);
75                     return true;
76                 }
77             }
78             return false;
79         }
80 
write(wxConfigBase * config,const IO::Path & path,const int & value)81         bool write(wxConfigBase* config, const IO::Path& path, const int& value) const {
82             wxString str;
83             str << value;
84             return config->Write(path.asString('/'), str);
85         }
86     };
87 
88     template <>
89     class PreferenceSerializer<float> {
90     public:
read(wxConfigBase * config,const IO::Path & path,float & result)91         bool read(wxConfigBase* config, const IO::Path& path, float& result) const {
92             wxString string;
93             if (config->Read(path.asString('/'), &string)) {
94                 double doubleValue = 0.0;
95                 if (string.ToDouble(&doubleValue) && doubleValue >= std::numeric_limits<float>::min() && doubleValue <= std::numeric_limits<float>::max()) {
96                     result = static_cast<float>(doubleValue);
97                     return true;
98                 }
99             }
100             return false;
101         }
102 
write(wxConfigBase * config,const IO::Path & path,const float & value)103         bool write(wxConfigBase* config, const IO::Path& path, const float& value) const {
104             wxString str;
105             str << value;
106             return config->Write(path.asString('/'), str);
107         }
108     };
109 
110     template <>
111     class PreferenceSerializer<double> {
112     public:
read(wxConfigBase * config,const IO::Path & path,double & result)113         bool read(wxConfigBase* config, const IO::Path& path, double& result) const {
114             wxString string;
115             if (config->Read(path.asString('/'), &string)) {
116                 double doubleValue = 0.0;
117                 if (string.ToDouble(&doubleValue)) {
118                     result = doubleValue;
119                     return true;
120                 }
121             }
122             return false;
123         }
124 
write(wxConfigBase * config,const IO::Path & path,const double & value)125         bool write(wxConfigBase* config, const IO::Path& path, const double& value) const {
126             wxString str;
127             str << value;
128             return config->Write(path.asString('/'), str);
129         }
130     };
131 
132     template <>
133     class PreferenceSerializer<String> {
134     public:
read(wxConfigBase * config,const IO::Path & path,String & result)135         bool read(wxConfigBase* config, const IO::Path& path, String& result) const {
136             wxString string;
137             if (config->Read(path.asString('/'), &string)) {
138                 result = string.ToStdString();
139                 return true;
140             }
141             return false;
142         }
143 
write(wxConfigBase * config,const IO::Path & path,const String & value)144         bool write(wxConfigBase* config, const IO::Path& path, const String& value) const {
145             return config->Write(path.asString('/'), wxString(value));
146         }
147     };
148 
149     template <>
150     class PreferenceSerializer<Color> {
151     public:
read(wxConfigBase * config,const IO::Path & path,Color & result)152         bool read(wxConfigBase* config, const IO::Path& path, Color& result) const {
153             wxString string;
154             if (config->Read(path.asString('/'), &string)) {
155                 result = Color::parse(string.ToStdString());
156                 return true;
157             }
158             return false;
159         }
160 
write(wxConfigBase * config,const IO::Path & path,const Color & value)161         bool write(wxConfigBase* config, const IO::Path& path, const Color& value) const {
162             return config->Write(path.asString('/'), wxString(value.asString()));
163         }
164     };
165 
166     template<>
167     class PreferenceSerializer<View::KeyboardShortcut> {
168     public:
read(wxConfigBase * config,const IO::Path & path,View::KeyboardShortcut & result)169         bool read(wxConfigBase* config, const IO::Path& path, View::KeyboardShortcut& result) const {
170             wxString string;
171             if (config->Read(path.asString('/'), &string)) {
172                 result = View::KeyboardShortcut(string);
173                 return true;
174             }
175             return false;
176         }
177 
write(wxConfigBase * config,const IO::Path & path,const View::KeyboardShortcut & value)178         bool write(wxConfigBase* config, const IO::Path& path, const View::KeyboardShortcut& value) const {
179             return config->Write(path.asString('/'), wxString(value.asString()));
180         }
181     };
182 
183     template<>
184     class PreferenceSerializer<IO::Path> {
185     public:
read(wxConfigBase * config,const IO::Path & path,IO::Path & result)186         bool read(wxConfigBase* config, const IO::Path& path, IO::Path& result) const {
187             wxString string;
188             if (config->Read(path.asString('/'), &string)) {
189                 result = IO::Path(string.ToStdString());
190                 return true;
191             }
192             return false;
193         }
194 
write(wxConfigBase * config,const IO::Path & path,const IO::Path & value)195         bool write(wxConfigBase* config, const IO::Path& path, const IO::Path& value) const {
196             return config->Write(path.asString('/'), wxString(value.asString()));
197         }
198     };
199 
200     template <>
201     class PreferenceSerializer<ConfigEntry::Ptr> {
202     public:
read(wxConfigBase * config,const IO::Path & path,ConfigEntry::Ptr & result)203         bool read(wxConfigBase* config, const IO::Path& path, ConfigEntry::Ptr& result) const {
204             wxString string;
205             if (config->Read(path.asString('/'), &string)) {
206                 result = IO::ConfigParser(string.ToStdString()).parse();
207                 return true;
208             }
209             return false;
210         }
211 
write(wxConfigBase * config,const IO::Path & path,const ConfigEntry::Ptr & value)212         bool write(wxConfigBase* config, const IO::Path& path, const ConfigEntry::Ptr& value) const {
213             StringStream stream;
214             stream << value;
215             return config->Write(path.asString('/'), wxString(stream.str()));
216         }
217     };
218 
219     template<typename S>
220     class PreferenceSerializer<std::vector<S> > {
221     private:
222         PreferenceSerializer<S> m_serializer;
223     public:
read(wxConfigBase * config,const IO::Path & path,std::vector<S> & result)224         bool read(wxConfigBase* config, const IO::Path& path, std::vector<S>& result) const {
225             const wxString wxPath(path.asString('/'));
226             if (!config->Exists(wxPath))
227                 return false;
228 
229             const wxString oldPath = config->GetPath();
230             config->SetPath(wxPath);
231 
232             bool success = true;
233             std::vector<S> temp;
234 
235             wxString name;
236             long index;
237             if (config->GetFirstEntry(name, index)) {
238                 do {
239                     S value;
240                     success = m_serializer.read(config, IO::Path(name.ToStdString()), value);
241                     if (success)
242                         temp.push_back(value);
243                 } while (success && config->GetNextEntry(name, index));
244             }
245 
246             config->SetPath(oldPath);
247 
248             using std::swap;
249             if (success)
250                 swap(result, temp);
251             return success;
252         }
253 
write(wxConfigBase * config,const IO::Path & path,const std::vector<S> & values)254         bool write(wxConfigBase* config, const IO::Path& path, const std::vector<S>& values) const {
255             const wxString oldPath = config->GetPath();
256             config->DeleteGroup(path.asString('/'));
257             config->SetPath(path.asString('/'));
258 
259             for (size_t i = 0; i < values.size(); ++i) {
260                 wxString name;
261                 name << i;
262                 m_serializer.write(config, IO::Path(name.ToStdString()), values[i]);
263             }
264 
265             config->SetPath(oldPath);
266             return true;
267         }
268     };
269 
270     template<typename S>
271     class PreferenceSerializer<std::map<String, S> > {
272     private:
273         PreferenceSerializer<S> m_serializer;
274     public:
read(wxConfigBase * config,const IO::Path & path,std::map<String,S> & result)275         bool read(wxConfigBase* config, const IO::Path& path, std::map<String, S>& result) const {
276             const wxString oldPath = config->GetPath();
277             config->SetPath(path.asString('/'));
278 
279             bool success = true;
280             std::map<String, S> temp;
281 
282             wxString name;
283             long index;
284             if (config->GetFirstEntry(name, index)) {
285                 do {
286                     const String nameStr = name.ToStdString();
287                     S value;
288                     success = m_serializer.read(config, IO::Path(nameStr), value);
289                     if (success)
290                         temp[nameStr] = value;
291                 } while (success && config->GetNextEntry(name, index));
292             }
293 
294             config->SetPath(oldPath);
295 
296             using std::swap;
297             if (success)
298                 swap(result, temp);
299             return success;
300         }
301 
write(wxConfigBase * config,const IO::Path & path,const std::map<String,S> & values)302         bool write(wxConfigBase* config, const IO::Path& path, const std::map<String, S>& values) const {
303             const wxString oldPath = config->GetPath();
304             config->DeleteGroup(path.asString('/'));
305             config->SetPath(path.asString('/'));
306 
307             typename std::map<String, S>::const_iterator it, end;
308             for (it = values.begin(), end = values.end(); it != end; ++it) {
309                 const String& name = it->first;
310                 const S& value = it->second;
311                 m_serializer.write(config, IO::Path(name), value);
312             }
313 
314             config->SetPath(oldPath);
315             return true;
316         }
317     };
318 
319     class ValueHolderBase {};
320 
321     template <typename T>
322     class ValueHolder : public ValueHolderBase {
323     private:
324         T m_value;
325     public:
ValueHolder(T value)326         ValueHolder(T value) :
327         m_value(value) {}
328 
value()329         const T& value() const {
330             return m_value;
331         }
332     };
333 
334     class PreferenceBase {
335     public:
336         typedef std::set<const PreferenceBase*> Set;
337 
~PreferenceBase()338         virtual ~PreferenceBase() {}
339 
340         virtual void load(wxConfigBase* config) const = 0;
341         virtual void save(wxConfigBase* config) = 0;
342         virtual void setValue(const ValueHolderBase* valueHolder) = 0;
343 
344         bool operator==(const PreferenceBase& other) const {
345             return this == &other;
346         }
347 
348         virtual const IO::Path& path() const = 0;
349     };
350 
351 
352     template <typename T>
353     class Preference : public PreferenceBase {
354     protected:
355         friend class PreferenceManager;
356         template<typename> friend class SetTemporaryPreference;
357 
358         PreferenceSerializer<T> m_serializer;
359         IO::Path m_path;
360         T m_defaultValue;
361         mutable T m_value;
362         mutable bool m_initialized;
363         bool m_modified;
364 
setValue(const T & value)365         void setValue(const T& value) {
366             m_modified = true;
367             m_value = value;
368         }
369 
setValue(const ValueHolderBase * valueHolder)370         void setValue(const ValueHolderBase* valueHolder) {
371             const ValueHolder<T>* actualValueHolder = static_cast<const ValueHolder<T>*>(valueHolder);
372             setValue(actualValueHolder->value());
373         }
374 
initialized()375         bool initialized() const {
376             return m_initialized;
377         }
378 
load(wxConfigBase * config)379         void load(wxConfigBase* config) const {
380             using std::swap;
381             T temp;
382             if (m_serializer.read(config, m_path, temp))
383                 std::swap(m_value, temp);
384             m_initialized = true;
385         }
386 
save(wxConfigBase * config)387         void save(wxConfigBase* config) {
388             if (m_modified) {
389                 assertResult(m_serializer.write(config, m_path, m_value));
390                 m_modified = false;
391             }
392         }
393     public:
Preference(const IO::Path & path,const T & defaultValue)394         Preference(const IO::Path& path, const T& defaultValue) :
395         m_path(path),
396         m_defaultValue(defaultValue),
397         m_value(m_defaultValue),
398         m_initialized(false),
399         m_modified(false) {
400             m_modified = m_initialized;
401         }
402 
path()403         const IO::Path& path() const {
404             return m_path;
405         }
406 
defaultValue()407         const T& defaultValue() const {
408             return m_defaultValue;
409         }
410 
value()411         const T& value() const {
412             return m_value;
413         }
414     };
415 }
416 
417 #endif /* defined(TrenchBroom_Preference) */
418