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