1 /********************************************************************** 2 3 Audacity: A Digital Audio Editor 4 5 Prefs.h 6 7 Dominic Mazzoni 8 Markus Meyer 9 10 Audacity uses wxWidgets' wxFileConfig class to handle preferences. 11 In Audacity versions prior to 1.3.1, it used wxConfig, which would 12 store the prefs in a platform-dependent way (e.g. in the registry 13 on Windows). Now it always stores the settings in a configuration file 14 in the Audacity Data Directory. 15 16 Every time we read a preference, we need to specify the default 17 value for that preference, to be used if the preference hasn't 18 been set before. 19 20 So, to avoid code duplication, we provide functions in this file 21 to read and write preferences which have a nonobvious default 22 value, so that if we later want to change this value, we only 23 have to change it in one place. 24 25 See Prefs.cpp for a (complete?) list of preferences we keep 26 track of... 27 28 **********************************************************************/ 29 #ifndef __AUDACITY_PREFS__ 30 #define __AUDACITY_PREFS__ 31 32 // Increment this every time the prefs need to be reset 33 // the first part (before the r) indicates the version the reset took place 34 // the second part (after the r) indicates the number of times the prefs have been reset within the same version 35 #define AUDACITY_PREFS_VERSION_STRING "1.1.1r1" 36 37 #include <functional> 38 39 #include "ComponentInterfaceSymbol.h" 40 #include "wxArrayStringEx.h" 41 #include "FileConfig.h" 42 43 #include <memory> 44 #include <wx/event.h> // to declare custom event types 45 46 class wxFileName; 47 48 PREFERENCES_API void InitPreferences( std::unique_ptr<FileConfig> uPrefs ); 49 //! Call this to reset preferences to an (almost)-"new" default state 50 /*! 51 There is at least one exception to that: user preferences we want to make 52 more "sticky." Notably, whether automatic update checking is preferred. 53 */ 54 PREFERENCES_API void ResetPreferences(); 55 PREFERENCES_API void FinishPreferences(); 56 57 extern PREFERENCES_API FileConfig *gPrefs; 58 extern int gMenusDirty; 59 60 61 struct ByColumns_t{}; 62 extern PREFERENCES_API ByColumns_t ByColumns; 63 64 //! Base class for settings objects. It holds a configuration key path. 65 /* The constructors are non-explicit for convenience */ 66 class PREFERENCES_API SettingBase 67 { 68 public: SettingBase(const char * path)69 SettingBase( const char *path ) : mPath{ path } {} SettingBase(const wxChar * path)70 SettingBase( const wxChar *path ) : mPath{ path } {} SettingBase(const wxString & path)71 SettingBase( const wxString &path ) : mPath{ path } {} 72 73 wxConfigBase *GetConfig() const; 74 GetPath()75 const wxString &GetPath() const { return mPath; } 76 77 //! Delete the key if present, and return true iff it was. 78 bool Delete(); 79 80 protected: 81 SettingBase( const SettingBase& ) = default; 82 const RegistryPath mPath; 83 }; 84 85 //! Class template adds an in-memory cache of a value to SettingBase. 86 template< typename T > 87 class CachingSettingBase : public SettingBase 88 { 89 public: CachingSettingBase(const SettingBase & path)90 explicit CachingSettingBase( const SettingBase &path ) 91 : SettingBase{ path } {} 92 protected: 93 CachingSettingBase( const CachingSettingBase & ) = default; 94 mutable T mCurrentValue{}; 95 mutable bool mValid{false}; 96 }; 97 98 //! Class template adds default value, read, and write methods to CachingSetingBase 99 template< typename T > 100 class Setting : public CachingSettingBase< T > 101 { 102 public: 103 using CachingSettingBase< T >::CachingSettingBase; 104 105 using DefaultValueFunction = std::function< T() >; 106 107 //! Usual overload supplies a default value Setting(const SettingBase & path,const T & defaultValue)108 Setting( const SettingBase &path, const T &defaultValue ) 109 : CachingSettingBase< T >{ path } 110 , mDefaultValue{ defaultValue } 111 {} 112 113 //! This overload causes recomputation of the default each time it is needed Setting(const SettingBase & path,DefaultValueFunction function)114 Setting( const SettingBase &path, DefaultValueFunction function ) 115 : CachingSettingBase< T >{ path } 116 , mFunction{ function } 117 {} 118 119 GetDefault()120 const T& GetDefault() const 121 { 122 if ( mFunction ) 123 mDefaultValue = mFunction(); 124 return mDefaultValue; 125 } 126 127 //! overload of Read returning a boolean that is true if the value was previously defined */ Read(T * pVar)128 bool Read( T *pVar ) const 129 { 130 return ReadWithDefault( pVar, GetDefault() ); 131 } 132 133 //! overload of ReadWithDefault returning a boolean that is true if the value was previously defined */ ReadWithDefault(T * pVar,const T & defaultValue)134 bool ReadWithDefault( T *pVar, const T& defaultValue ) const 135 { 136 if ( pVar ) 137 *pVar = defaultValue; 138 if ( pVar && this->mValid ) { 139 *pVar = this->mCurrentValue; 140 return true; 141 } 142 const auto config = this->GetConfig(); 143 if ( pVar && config ) { 144 if ((this->mValid = config->Read( this->mPath, &this->mCurrentValue ))) 145 *pVar = this->mCurrentValue; 146 return this->mValid; 147 } 148 return (this->mValid = false); 149 } 150 151 //! overload of Read, always returning a value 152 /*! The value is the default stored in this in case the key is known to be absent from the config; 153 but it returns type T's default value if there was failure to read the config */ Read()154 T Read() const 155 { 156 return ReadWithDefault( GetDefault() ); 157 } 158 159 //! new direct use is discouraged but it may be needed in legacy code 160 /*! Use the given default in case the preference is not defined, which may not be the 161 default-default stored in this object. */ ReadWithDefault(const T & defaultValue)162 T ReadWithDefault( const T &defaultValue ) const 163 { 164 const auto config = this->GetConfig(); 165 return config 166 ? ( this->mValid = true, this->mCurrentValue = 167 config->ReadObject( this->mPath, defaultValue ) ) 168 : T{}; 169 } 170 171 //! Write value to config and return true if successful Write(const T & value)172 bool Write( const T &value ) 173 { 174 const auto config = this->GetConfig(); 175 if ( config ) { 176 this->mCurrentValue = value; 177 return DoWrite(); 178 } 179 return false; 180 } 181 182 //! Reset to the default value Reset()183 bool Reset() 184 { 185 return Write( GetDefault() ); 186 } 187 188 protected: 189 //! Write cached value to config and return true if successful 190 /*! (But the config object is not flushed) */ DoWrite()191 bool DoWrite( ) 192 { 193 const auto config = this->GetConfig(); 194 return this->mValid = 195 config ? config->Write( this->mPath, this->mCurrentValue ) : false; 196 } 197 198 mutable T mDefaultValue{}; 199 const DefaultValueFunction mFunction; 200 }; 201 202 //! This specialization of Setting for bool adds a Toggle method to negate the saved value 203 class PREFERENCES_API BoolSetting final : public Setting< bool > 204 { 205 public: 206 using Setting::Setting; 207 208 //! Write the negation of the previous value, and then return the current value. 209 bool Toggle(); 210 }; 211 212 //! Specialization of Setting for int 213 class IntSetting final : public Setting< int > 214 { 215 public: 216 using Setting::Setting; 217 }; 218 219 //! Specialization of Setting for double 220 class DoubleSetting final : public Setting< double > 221 { 222 public: 223 using Setting::Setting; 224 }; 225 226 //! Specialization of Setting for strings 227 class StringSetting final : public Setting< wxString > 228 { 229 public: 230 using Setting::Setting; 231 }; 232 233 using EnumValueSymbol = ComponentInterfaceSymbol; 234 235 /// A table of EnumValueSymbol that you can access by "row" with 236 /// operator [] but also allowing access to the "columns" of internal or 237 /// translated strings, and also allowing convenient column-wise construction 238 class PREFERENCES_API EnumValueSymbols : public std::vector< EnumValueSymbol > 239 { 240 public: 241 EnumValueSymbols() = default; EnumValueSymbols(std::initializer_list<EnumValueSymbol> symbols)242 EnumValueSymbols( std::initializer_list<EnumValueSymbol> symbols ) 243 : vector( symbols ) 244 {} EnumValueSymbols(std::vector<EnumValueSymbol> symbols)245 EnumValueSymbols( std::vector< EnumValueSymbol > symbols ) 246 : vector( symbols ) 247 {} 248 249 // columnwise constructor; arguments must have same size 250 // (Implicit constructor takes initial tag argument to avoid unintended 251 // overload resolution to the inherited constructor taking 252 // initializer_list, in the case that each column has exactly two strings) 253 EnumValueSymbols( 254 ByColumns_t, 255 const TranslatableStrings &msgids, 256 wxArrayStringEx internals 257 ); 258 259 const TranslatableStrings &GetMsgids() const; 260 const wxArrayStringEx &GetInternals() const; 261 262 private: 263 mutable TranslatableStrings mMsgids; 264 mutable wxArrayStringEx mInternals; 265 }; 266 267 /// Packages a table of user-visible choices each with an internal code string, 268 /// a preference key path, and a default choice 269 class PREFERENCES_API ChoiceSetting 270 { 271 public: 272 ChoiceSetting( 273 const SettingBase &key, 274 EnumValueSymbols symbols, 275 long defaultSymbol = -1 276 ) 277 : mKey{ key.GetPath() } 278 279 , mSymbols{ std::move( symbols ) } 280 281 , mDefaultSymbol{ defaultSymbol } 282 { 283 wxASSERT( defaultSymbol < (long)mSymbols.size() ); 284 } 285 Key()286 const wxString &Key() const { return mKey; } 287 const EnumValueSymbol &Default() const; GetSymbols()288 const EnumValueSymbols &GetSymbols() const { return mSymbols; } 289 290 wxString Read() const; 291 292 // new direct use is discouraged but it may be needed in legacy code: 293 // use a default in case the preference is not defined, which may not be 294 // the default-default stored in this object. 295 wxString ReadWithDefault( const wxString & ) const; 296 297 bool Write( const wxString &value ); // you flush gPrefs afterward 298 299 void SetDefault( long value ); 300 301 protected: 302 size_t Find( const wxString &value ) const; 303 virtual void Migrate( wxString& ); 304 305 const wxString mKey; 306 307 const EnumValueSymbols mSymbols; 308 309 // stores an internal value 310 mutable bool mMigrated { false }; 311 312 long mDefaultSymbol; 313 }; 314 315 /// Extends ChoiceSetting with a corresponding table of integer codes 316 /// (generally not equal to their table positions), 317 /// and optionally an old preference key path that stored integer codes, to be 318 /// migrated into one that stores internal string values instead 319 class PREFERENCES_API EnumSettingBase : public ChoiceSetting 320 { 321 public: 322 EnumSettingBase( 323 const SettingBase &key, 324 EnumValueSymbols symbols, 325 long defaultSymbol, 326 327 std::vector<int> intValues, // must have same size as symbols 328 const wxString &oldKey = {} 329 ); 330 331 protected: 332 333 // Read and write the encoded values 334 int ReadInt() const; 335 336 // new direct use is discouraged but it may be needed in legacy code: 337 // use a default in case the preference is not defined, which may not be 338 // the default-default stored in this object. 339 int ReadIntWithDefault( int defaultValue ) const; 340 341 bool WriteInt( int code ); // you flush gPrefs afterward 342 343 size_t FindInt( int code ) const; 344 void Migrate( wxString& ) override; 345 346 private: 347 std::vector<int> mIntValues; 348 const wxString mOldKey; 349 }; 350 351 /// Adapts EnumSettingBase to a particular enumeration type 352 template< typename Enum > 353 class EnumSetting : public EnumSettingBase 354 { 355 public: 356 357 EnumSetting( 358 const SettingBase &key, 359 EnumValueSymbols symbols, 360 long defaultSymbol, 361 362 std::vector< Enum > values, // must have same size as symbols 363 const wxString &oldKey = {} 364 ) 365 : EnumSettingBase{ 366 key, symbols, defaultSymbol, 367 { values.begin(), values.end() }, 368 oldKey 369 } 370 {} 371 372 // Wrap ReadInt() and ReadIntWithDefault() and WriteInt() ReadEnum()373 Enum ReadEnum() const 374 { return static_cast<Enum>( ReadInt() ); } 375 376 // new direct use is discouraged but it may be needed in legacy code: 377 // use a default in case the preference is not defined, which may not be 378 // the default-default stored in this object. ReadEnumWithDefault(Enum defaultValue)379 Enum ReadEnumWithDefault( Enum defaultValue ) const 380 { 381 auto integer = static_cast<int>(defaultValue); 382 return static_cast<Enum>( ReadIntWithDefault( integer ) ); 383 } 384 WriteEnum(Enum value)385 bool WriteEnum( Enum value ) 386 { return WriteInt( static_cast<int>( value ) ); } 387 388 }; 389 390 //! A listener notified of changes in preferences 391 class PREFERENCES_API PrefsListener 392 { 393 public: 394 //! Call this static function to notify all PrefsListener objects 395 /*! 396 @param id when positive, passed to UpdateSelectedPrefs() of all listeners, 397 meant to indicate that only a certain subset of preferences have changed; 398 else their UpdatePrefs() methods are called. (That is supposed to happen 399 when the user OK's changes in the Preferences dialog.) 400 Callbacks are delayed, in the main thread, using BasicUI::CallAfter 401 */ 402 static void Broadcast(int id = 0); 403 404 PrefsListener(); 405 virtual ~PrefsListener(); 406 407 // Called when all preferences should be updated. 408 virtual void UpdatePrefs() = 0; 409 410 protected: 411 // Called when only selected preferences are to be updated. 412 // id is some value generated by wxNewId() that identifies the portion 413 // of preferences. 414 // Default function does nothing. 415 virtual void UpdateSelectedPrefs( int id ); 416 417 private: 418 struct Impl; 419 std::unique_ptr<Impl> mpImpl; 420 }; 421 422 /// Return the config file key associated with a warning dialog identified 423 /// by internalDialogName. When the box is checked, the value at the key 424 /// becomes false. 425 PREFERENCES_API 426 wxString WarningDialogKey(const wxString &internalDialogName); 427 428 /*! 429 Meant to be statically constructed. A callback to repopulate configuration 430 files after a reset. 431 */ 432 struct PREFERENCES_API PreferenceInitializer { 433 PreferenceInitializer(); 434 virtual ~PreferenceInitializer(); 435 virtual void operator () () = 0; 436 437 static void ReinitializeAll(); 438 }; 439 440 // Special extra-sticky settings 441 extern PREFERENCES_API BoolSetting DefaultUpdatesCheckingFlag; 442 443 #endif 444