/********************************************************************** Audacity: A Digital Audio Editor Prefs.cpp Dominic Mazzoni *******************************************************************//*! \file Prefs.cpp \brief Utility functions for working with our wxConf (gPrefs) Audacity uses wxWidgets' wxConfig class to handle preferences. See Prefs.h for more information on how it works... \verbatim Note: The info below is very outdated and incomplete Preference field specification: / Version - Audacity Version that created these prefs DefaultOpenPath - Default directory for NEW file selector /FileFormats CopyOrEditUncompressedData - Copy data from uncompressed files or [ "copy", "edit"] - edit in place? ExportFormat_SF1 - Format to export PCM data in (this number is a libsndfile1.0 format) /SamplingRate DefaultProjectSampleRate- New projects will have this rate [ 8000, 11025, 16000, 22050, 44100, 48000 ] /AudioIO PlaybackDevice - device to use for playback RecordingDevice - device to use for recording (these are device names understood by PortAudio) /Display WaveformColor - 0xRRGGBB --since it will be stored in ShadowColor - decimal, it will be somewhat SpectrumLowColor - non-intuitive to edit, but SpectrumHighColor - much easier to parse. /Locale Language - two-letter language code for translations (*): wxGTK (+): wxWin ($): wxMac \endverbatim *//*******************************************************************/ #include "Audacity.h" #include "Prefs.h" #include #include #include #include #include #include #include "Internat.h" #include "MemoryX.h" std::unique_ptr ugPrefs {}; AudacityPrefs *gPrefs = NULL; int gMenusDirty = 0; wxDEFINE_EVENT(EVT_PREFS_UPDATE, wxCommandEvent); struct PrefsListener::Impl : wxEvtHandler { Impl( PrefsListener &owner ); ~Impl(); void OnEvent(wxCommandEvent&); PrefsListener &mOwner; }; PrefsListener::Impl::Impl( PrefsListener &owner ) : mOwner{ owner } { wxTheApp->Bind(EVT_PREFS_UPDATE, &PrefsListener::Impl::OnEvent, this); } PrefsListener::Impl::~Impl() { } PrefsListener::PrefsListener() : mpImpl{ std::make_unique( *this ) } { } PrefsListener::~PrefsListener() { } void PrefsListener::UpdateSelectedPrefs( int ) { } void PrefsListener::Impl::OnEvent( wxCommandEvent &evt ) { evt.Skip(); auto id = evt.GetId(); if (id <= 0) mOwner.UpdatePrefs(); else mOwner.UpdateSelectedPrefs( id ); } #if 0 // Copy one entry from one wxConfig object to another static void CopyEntry(wxString path, wxConfigBase *src, wxConfigBase *dst, wxString entry) { switch(src->GetEntryType(entry)) { case wxConfigBase::Type_Unknown: case wxConfigBase::Type_String: { wxString value = src->Read(entry, wxT("")); dst->Write(path + entry, value); break; } case wxConfigBase::Type_Boolean: { bool value = false; src->Read(entry, &value, value); dst->Write(path + entry, value); break; } case wxConfigBase::Type_Integer: { long value = false; src->Read(entry, &value, value); dst->Write(path + entry, value); break; } case wxConfigBase::Type_Float: { double value = false; src->Read(entry, &value, value); dst->Write(path + entry, value); break; } } } // Recursive routine to copy all groups and entries from one wxConfig object to another static void CopyEntriesRecursive(wxString path, wxConfigBase *src, wxConfigBase *dst) { wxString entryName; long entryIndex; bool entryKeepGoing; entryKeepGoing = src->GetFirstEntry(entryName, entryIndex); while (entryKeepGoing) { CopyEntry(path, src, dst, entryName); entryKeepGoing = src->GetNextEntry(entryName, entryIndex); } wxString groupName; long groupIndex; bool groupKeepGoing; groupKeepGoing = src->GetFirstGroup(groupName, groupIndex); while (groupKeepGoing) { wxString subPath = path+groupName+wxT("/"); src->SetPath(subPath); CopyEntriesRecursive(subPath, src, dst); src->SetPath(path); groupKeepGoing = src->GetNextGroup(groupName, groupIndex); } } #endif AudacityPrefs::AudacityPrefs(const wxString& appName, const wxString& vendorName, const wxString& localFilename, const wxString& globalFilename, long style, const wxMBConv& conv) : wxFileConfig(appName, vendorName, localFilename, globalFilename, style, conv) { } void InitPreferences( const wxFileName &configFileName ) { wxString appName = wxTheApp->GetAppName(); ugPrefs = std::make_unique (appName, wxEmptyString, configFileName.GetFullPath(), wxEmptyString, wxCONFIG_USE_LOCAL_FILE); gPrefs = ugPrefs.get(); wxConfigBase::Set(gPrefs); } bool CheckWritablePreferences() { return gPrefs->Write("/TEST", true) && gPrefs->Flush(); } TranslatableString UnwritablePreferencesErrorMessage( const wxFileName &configFileName ) { return XO("Audacity cannot start because the settings file at %s is not writable.") .Format(configFileName.GetFullPath()); } void FinishPreferences() { if (gPrefs) { wxConfigBase::Set(NULL); ugPrefs.reset(); gPrefs = NULL; } } ////////// EnumValueSymbols::EnumValueSymbols( ByColumns_t, const TranslatableStrings &msgids, wxArrayStringEx internals ) : mInternals( std::move( internals ) ) { auto size = mInternals.size(), size2 = msgids.size(); if ( size != size2 ) { wxASSERT( false ); size = std::min( size, size2 ); } reserve( size ); auto iter1 = mInternals.begin(); auto iter2 = msgids.begin(); while( size-- ) emplace_back( *iter1++, *iter2++ ); } const TranslatableStrings &EnumValueSymbols::GetMsgids() const { if ( mMsgids.empty() ) mMsgids = transform_container( *this, std::mem_fn( &EnumValueSymbol::Msgid ) ); return mMsgids; } const wxArrayStringEx &EnumValueSymbols::GetInternals() const { if ( mInternals.empty() ) mInternals = transform_container( *this, std::mem_fn( &EnumValueSymbol::Internal ) ); return mInternals; } ////////// const EnumValueSymbol &ChoiceSetting::Default() const { if ( mDefaultSymbol >= 0 && mDefaultSymbol < (long)mSymbols.size() ) return mSymbols[ mDefaultSymbol ]; static EnumValueSymbol empty; return empty; } wxString ChoiceSetting::Read() const { const auto &defaultValue = Default().Internal(); return ReadWithDefault( defaultValue ); } wxString ChoiceSetting::ReadWithDefault( const wxString &defaultValue ) const { wxString value; if ( !gPrefs->Read(mKey, &value, defaultValue) ) if (!mMigrated) { const_cast(this)->Migrate( value ); mMigrated = true; } // Remap to default if the string is not known -- this avoids surprises // in case we try to interpret config files from future versions auto index = Find( value ); if ( index >= mSymbols.size() ) value = defaultValue; return value; } size_t ChoiceSetting::Find( const wxString &value ) const { auto start = GetSymbols().begin(); return size_t( std::find( start, GetSymbols().end(), EnumValueSymbol{ value, {} } ) - start ); } void ChoiceSetting::Migrate( wxString &value ) { (void)value;// Compiler food } bool ChoiceSetting::Write( const wxString &value ) { auto index = Find( value ); if (index >= mSymbols.size()) return false; auto result = gPrefs->Write( mKey, value ); mMigrated = true; return result; } EnumSettingBase::EnumSettingBase( const wxString &key, EnumValueSymbols symbols, long defaultSymbol, std::vector intValues, // must have same size as symbols const wxString &oldKey ) : ChoiceSetting{ key, std::move( symbols ), defaultSymbol } , mIntValues{ std::move( intValues ) } , mOldKey{ oldKey } { auto size = mSymbols.size(); if( mIntValues.size() != size ) { wxASSERT( false ); mIntValues.resize( size ); } } void ChoiceSetting::SetDefault( long value ) { if ( value < (long)mSymbols.size() ) mDefaultSymbol = value; else wxASSERT( false ); } int EnumSettingBase::ReadInt() const { auto index = Find( Read() ); wxASSERT( index < mIntValues.size() ); return mIntValues[ index ]; } int EnumSettingBase::ReadIntWithDefault( int defaultValue ) const { wxString defaultString; auto index0 = FindInt( defaultValue ); if ( index0 < mSymbols.size() ) defaultString = mSymbols[ index0 ].Internal(); else wxASSERT( false ); auto index = Find( ReadWithDefault( defaultString ) ); wxASSERT( index < mSymbols.size() ); return mIntValues[ index ]; } size_t EnumSettingBase::FindInt( int code ) const { const auto start = mIntValues.begin(); return size_t( std::find( start, mIntValues.end(), code ) - start ); } void EnumSettingBase::Migrate( wxString &value ) { int intValue = 0; if ( !mOldKey.empty() && gPrefs->Read(mOldKey, &intValue, 0) ) { // Make the migration, only once and persistently. // Do not DELETE the old key -- let that be read if user downgrades // Audacity. But further changes will be stored only to the NEW key // and won't be seen then. auto index = (long) FindInt( intValue ); if ( index >= (long)mSymbols.size() ) index = mDefaultSymbol; if ( index >= 0 && index < (long)mSymbols.size() ) { value = mSymbols[index].Internal(); Write(value); gPrefs->Flush(); } } } bool EnumSettingBase::WriteInt( int code ) // you flush gPrefs afterward { auto index = FindInt( code ); if ( index >= mSymbols.size() ) return false; return Write( mSymbols[index].Internal() ); } wxString WarningDialogKey(const wxString &internalDialogName) { return wxT("/Warnings/") + internalDialogName; } ByColumns_t ByColumns{}; #include namespace { using PreferenceInitializers = std::set< PreferenceInitializer* >; PreferenceInitializers &allInitializers() { static PreferenceInitializers theSet; return theSet; } } PreferenceInitializer::PreferenceInitializer() { allInitializers().insert( this ); } PreferenceInitializer::~PreferenceInitializer() { allInitializers().erase( this ); } void PreferenceInitializer::ReinitializeAll() { for ( auto pInitializer : allInitializers() ) (*pInitializer)(); }