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