1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-07-25 08:58:06 +02:00

Break dependency cycle introduced at 21296b0

This commit is contained in:
Paul Licameli 2021-01-31 11:31:43 -05:00
commit bde537351c
9 changed files with 266 additions and 186 deletions

View File

@ -71,6 +71,7 @@ It handles initialization and termination by subclassing wxApp.
#include "AudacityLogger.h"
#include "AboutDialog.h"
#include "AColor.h"
#include "AudacityFileConfig.h"
#include "AudioIO.h"
#include "Benchmark.h"
#include "Clipboard.h"
@ -188,11 +189,10 @@ void PopulatePreferences()
{
const wxString fullPath{fn.GetFullPath()};
FileConfig ini(wxEmptyString,
wxEmptyString,
fullPath,
wxEmptyString,
wxCONFIG_USE_LOCAL_FILE);
auto pIni =
AudacityFileConfig::Create({}, {}, fullPath, {},
wxCONFIG_USE_LOCAL_FILE);
auto &ini = *pIni;
wxString lang;
if (ini.Read(wxT("/FromInno/Language"), &lang))
@ -1245,9 +1245,15 @@ bool AudacityApp::OnInit()
#endif
// Initialize preferences and language
wxFileName configFileName(FileNames::DataDir(), wxT("audacity.cfg"));
InitPreferences( configFileName );
PopulatePreferences();
{
wxFileName configFileName(FileNames::DataDir(), wxT("audacity.cfg"));
auto appName = wxTheApp->GetAppName();
InitPreferences( AudacityFileConfig::Create(
appName, wxEmptyString,
configFileName.GetFullPath(),
wxEmptyString, wxCONFIG_USE_LOCAL_FILE) );
PopulatePreferences();
}
#if defined(__WXMSW__) && !defined(__WXUNIVERSAL__) && !defined(__CYGWIN__)
this->AssociateFileTypes();

150
src/AudacityFileConfig.cpp Normal file
View File

@ -0,0 +1,150 @@
/**********************************************************************
Audacity: A Digital Audio Editor
AudacityFileConfig.cpp
Paul Licameli split from Prefs.cpp
**********************************************************************/
#include "Audacity.h"
#include "AudacityFileConfig.h"
#include "widgets/HelpSystem.h"
#include "widgets/wxPanelWrapper.h"
#include "ShuttleGui.h"
#include "../images/Help.xpm"
#include <wx/app.h>
#include <wx/bmpbuttn.h>
#include <wx/sizer.h>
AudacityFileConfig::AudacityFileConfig(
const wxString& appName,
const wxString& vendorName,
const wxString& localFilename,
const wxString& globalFilename,
long style,
const wxMBConv& conv
)
: FileConfig{ appName, vendorName, localFilename, globalFilename, style, conv }
{}
AudacityFileConfig::~AudacityFileConfig() = default;
std::unique_ptr<AudacityFileConfig> AudacityFileConfig::Create(
const wxString& appName,
const wxString& vendorName,
const wxString& localFilename,
const wxString& globalFilename,
long style,
const wxMBConv& conv
)
{
// Private ctor means make_unique can't compile, so this verbosity:
auto result = std::unique_ptr<AudacityFileConfig>{
safenew AudacityFileConfig{
appName, vendorName, localFilename, globalFilename, style, conv } };
result->Init();
return result;
}
void AudacityFileConfig::Warn(bool canRetry)
{
wxDialogWrapper dlg(nullptr, wxID_ANY, XO("Audacity Configuration Error"));
ShuttleGui S(&dlg, eIsCreating);
wxButton *retryButton;
wxButton *quitButton;
S.SetBorder(5);
S.StartVerticalLay(wxEXPAND, 1);
{
S.SetBorder(15);
S.StartHorizontalLay(wxALIGN_RIGHT, 0);
{
auto cause = canRetry
? XO("The following configuration file could not be written:")
: XO("The following configuration file could not be read:");
auto retryMsg = canRetry
? XO("You can attempt to correct the issue and then click \"Retry\" to coninue.\n\n")
: XO("");
S.AddFixedText(
XO("%s:\n\n"
"\t%s\n\n"
"This could be caused by many reasons, but the most likely are that "
"the disk is full or you do not have write permissions to the file. "
"More information can be obtained by clicking the help button below.\n\n"
"%s"
"If you choose to \"Quit Audacity\", your project may be left in an unsaved "
"state which will be recovered the next time you open it.")
.Format(cause, GetFilePath(), retryMsg),
false,
500);
}
S.EndHorizontalLay();
S.SetBorder(5);
S.StartHorizontalLay(wxALIGN_RIGHT, 0);
{
// Can't use themed bitmap since the theme manager might not be
// initialized yet and it requires a configuration file.
wxButton *b = S.Id(wxID_HELP).AddBitmapButton(wxBitmap(Help_xpm));
b->SetToolTip( XO("Help").Translation() );
b->SetLabel(XO("Help").Translation()); // for screen readers
b = S.Id(wxID_CANCEL).AddButton(XXO("&Quit Audacity"));
if (canRetry)
{
b = S.Id(wxID_OK).AddButton(XXO("&Retry"));
dlg.SetAffirmativeId(wxID_OK);
}
b->SetDefault();
b->SetFocus();
}
S.EndHorizontalLay();
}
S.EndVerticalLay();
dlg.Layout();
dlg.GetSizer()->Fit(&dlg);
dlg.SetMinSize(dlg.GetSize());
dlg.Center();
auto onButton = [&](wxCommandEvent &e)
{
dlg.EndModal(e.GetId());
};
dlg.Bind(wxEVT_BUTTON, onButton);
switch (dlg.ShowModal())
{
case wxID_HELP:
// Can't use the HelpSystem since the theme manager may not
// yet be initialized and it requires a configuration file.
OpenInDefaultBrowser("https://" +
HelpSystem::HelpHostname +
HelpSystem::HelpServerHomeDir +
"Error:_Audacity_settings_file_unwritable");
break;
case wxID_CANCEL:
// This REALLY needs to use an exception with decent cleanup and program exit
wxExit();
#if defined(__WXGTK__)
wxAbort();
#else
exit(-1);
#endif
break;
}
dlg.Unbind(wxEVT_BUTTON, onButton);
}

48
src/AudacityFileConfig.h Normal file
View File

@ -0,0 +1,48 @@
/**********************************************************************
Audacity: A Digital Audio Editor
@file AudacityFileConfig.h
@brief Extend FileConfig with application-specific behavior
Paul Licameli split from Prefs.h
**********************************************************************/
#ifndef __AUDACITY_FILE_CONFIG__
#define __AUDACITY_FILE_CONFIG__
#include <memory>
#include "widgets/FileConfig.h" // to inherit
/// \brief Our own specialisation of FileConfig.
class AUDACITY_DLL_API AudacityFileConfig final : public FileConfig
{
public:
//! Require a call to this factory, to guarantee proper two-phase initialization
static std::unique_ptr<AudacityFileConfig> Create(
const wxString& appName = {},
const wxString& vendorName = {},
const wxString& localFilename = {},
const wxString& globalFilename = {},
long style = wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_GLOBAL_FILE,
const wxMBConv& conv = wxConvAuto()
);
~AudacityFileConfig() override;
protected:
void Warn(bool canRetry) override;
private:
//! Disallow direct constructor call, because a two-phase initialization is required
AudacityFileConfig(
const wxString& appName,
const wxString& vendorName,
const wxString& localFilename,
const wxString& globalFilename,
long style,
const wxMBConv& conv
);
};
#endif

View File

@ -78,6 +78,8 @@ list( APPEND SOURCES
$<$<BOOL:${wxIS_MAC}>:AudacityApp.mm>
AudacityException.cpp
AudacityException.h
AudacityFileConfig.cpp
AudacityFileConfig.h
AudacityHeaders.cpp
AudacityHeaders.h
AudacityLogger.cpp

View File

@ -42,6 +42,7 @@ for shared and private configs - which need to move out.
#include "audacity/EffectInterface.h"
#include "audacity/ModuleInterface.h"
#include "AudacityFileConfig.h"
#include "FileNames.h"
#include "ModuleManager.h"
#include "PlatformCompatibility.h"
@ -1929,7 +1930,9 @@ bool PluginManager::DropFile(const wxString &fileName)
void PluginManager::Load()
{
// Create/Open the registry
FileConfig registry(wxEmptyString, wxEmptyString, FileNames::PluginRegistry());
auto pRegistry = AudacityFileConfig::Create(
{}, {}, FileNames::PluginRegistry());
auto &registry = *pRegistry;
// If this group doesn't exist then we have something that's not a registry.
// We should probably warn the user, but it's pretty unlikely that this will happen.
@ -2270,7 +2273,9 @@ void PluginManager::LoadGroup(FileConfig *pRegistry, PluginType type)
void PluginManager::Save()
{
// Create/Open the registry
FileConfig registry(wxEmptyString, wxEmptyString, FileNames::PluginRegistry());
auto pRegistry = AudacityFileConfig::Create(
{}, {}, FileNames::PluginRegistry());
auto &registry = *pRegistry;
// Clear it out
registry.DeleteAll();
@ -2777,7 +2782,8 @@ FileConfig *PluginManager::GetSettings()
{
if (!mSettings)
{
mSettings = std::make_unique<FileConfig>(wxEmptyString, wxEmptyString, FileNames::PluginSettings());
mSettings =
AudacityFileConfig::Create({}, {}, FileNames::PluginSettings());
// Check for a settings version that we can understand
if (mSettings->HasEntry(SETVERKEY))
@ -2805,7 +2811,7 @@ FileConfig *PluginManager::GetSettings()
bool PluginManager::HasGroup(const RegistryPath & group)
{
FileConfig *settings = GetSettings();
auto settings = GetSettings();
bool res = settings->HasGroup(group);
if (res)

View File

@ -62,9 +62,9 @@
#include "Internat.h"
#include "MemoryX.h"
std::unique_ptr<AudacityPrefs> ugPrefs {};
std::unique_ptr<FileConfig> ugPrefs {};
AudacityPrefs *gPrefs = NULL;
FileConfig *gPrefs = nullptr;
int gMenusDirty = 0;
wxDEFINE_EVENT(EVT_PREFS_UPDATE, wxCommandEvent);
@ -171,33 +171,10 @@ static void CopyEntriesRecursive(wxString path, wxConfigBase *src, wxConfigBase
}
#endif
AudacityPrefs::AudacityPrefs(const wxString& appName,
const wxString& vendorName,
const wxString& localFilename,
const wxString& globalFilename,
long style,
const wxMBConv& conv) :
FileConfig(appName,
vendorName,
localFilename,
globalFilename,
style,
conv)
void InitPreferences( std::unique_ptr<FileConfig> uPrefs )
{
}
void InitPreferences( const wxFileName &configFileName )
{
wxString appName = wxTheApp->GetAppName();
ugPrefs = std::make_unique<AudacityPrefs>
(appName, wxEmptyString,
configFileName.GetFullPath(),
wxEmptyString, wxCONFIG_USE_LOCAL_FILE);
gPrefs = ugPrefs.get();
gPrefs = uPrefs.get();
ugPrefs = std::move(uPrefs);
wxConfigBase::Set(gPrefs);
}

View File

@ -40,50 +40,13 @@
class wxFileName;
void InitPreferences( const wxFileName &configFileName );
void InitPreferences( std::unique_ptr<FileConfig> uPrefs );
void FinishPreferences();
class AudacityPrefs;
extern AUDACITY_DLL_API AudacityPrefs *gPrefs;
extern AUDACITY_DLL_API FileConfig *gPrefs;
extern int gMenusDirty;
/// \brief Our own specialisation of wxFileConfig. It is essentially a renaming,
/// though it does provide one new access function. Most of the prefs work
/// is actually done by the InitPreferences() function.
class AUDACITY_DLL_API AudacityPrefs : public FileConfig
{
public:
AudacityPrefs(const wxString& appName = {},
const wxString& vendorName = {},
const wxString& localFilename = {},
const wxString& globalFilename = {},
long style = wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_GLOBAL_FILE,
const wxMBConv& conv = wxConvAuto());
// Set and Get values of the version major/minor/micro keys in audacity.cfg when Audacity first opens
void SetVersionKeysInit( int major, int minor, int micro)
{
mVersionMajorKeyInit = major;
mVersionMinorKeyInit = minor;
mVersionMicroKeyInit = micro;
}
void GetVersionKeysInit( int& major, int& minor, int& micro) const
{
major = mVersionMajorKeyInit;
minor = mVersionMinorKeyInit;
micro = mVersionMicroKeyInit;
}
// values of the version major/minor/micro keys in audacity.cfg
// when Audacity first opens
int mVersionMajorKeyInit{};
int mVersionMinorKeyInit{};
int mVersionMicroKeyInit{};
};
struct ByColumns_t{};
extern ByColumns_t ByColumns;

View File

@ -20,11 +20,6 @@
#include <wx/wfstream.h>
#include "FileConfig.h"
#include "HelpSystem.h"
#include "wxPanelWrapper.h"
#include "../ShuttleGui.h"
#include "../../images/Help.xpm"
#if !defined(F_OK)
#define F_OK 0x00
@ -45,6 +40,10 @@ FileConfig::FileConfig(const wxString& appName,
: wxFileConfig(appName, vendorName, localFilename, globalFilename, style, conv),
mConfigPath(localFilename),
mDirty(false)
{
}
void FileConfig::Init()
{
// Prevent wxFileConfig from attempting a Flush() during object deletion. This happens
// because we don't use the wxFileConfig::Flush() method and so the wxFileConfig dirty
@ -87,6 +86,8 @@ FileConfig::FileConfig(const wxString& appName,
//
// If the wxFileConfig class allowed us to call wxFileConfig::Init(), we wouldn't
// have to do all this mess.
// (Note that invocation of virtual Warn() can't be done in the ctor,
// which is why this is two-phase construction.)
Warn(canRead == true);
}
}
@ -173,102 +174,3 @@ bool FileConfig::DoWriteBinary(const wxString& key, const wxMemoryBuffer& buf)
return res;
}
#endif // wxUSE_BASE64
void FileConfig::Warn(bool canRetry)
{
wxDialogWrapper dlg(nullptr, wxID_ANY, XO("Audacity Configuration Error"));
ShuttleGui S(&dlg, eIsCreating);
wxButton *retryButton;
wxButton *quitButton;
S.SetBorder(5);
S.StartVerticalLay(wxEXPAND, 1);
{
S.SetBorder(15);
S.StartHorizontalLay(wxALIGN_RIGHT, 0);
{
auto cause = canRetry
? XO("The following configuration file could not be written:")
: XO("The following configuration file could not be read:");
auto retryMsg = canRetry
? XO("You can attempt to correct the issue and then click \"Retry\" to coninue.\n\n")
: XO("");
S.AddFixedText(
XO("%s:\n\n"
"\t%s\n\n"
"This could be caused by many reasons, but the most likely are that "
"the disk is full or you do not have write permissions to the file. "
"More information can be obtained by clicking the help button below.\n\n"
"%s"
"If you choose to \"Quit Audacity\", your project may be left in an unsaved "
"state which will be recovered the next time you open it.")
.Format(cause, mConfigPath, retryMsg),
false,
500);
}
S.EndHorizontalLay();
S.SetBorder(5);
S.StartHorizontalLay(wxALIGN_RIGHT, 0);
{
// Can't use themed bitmap since the theme manager might not be
// initialized yet and it requires a configuration file.
wxButton *b = S.Id(wxID_HELP).AddBitmapButton(wxBitmap(Help_xpm));
b->SetToolTip( XO("Help").Translation() );
b->SetLabel(XO("Help").Translation()); // for screen readers
b = S.Id(wxID_CANCEL).AddButton(XXO("&Quit Audacity"));
if (canRetry)
{
b = S.Id(wxID_OK).AddButton(XXO("&Retry"));
dlg.SetAffirmativeId(wxID_OK);
}
b->SetDefault();
b->SetFocus();
}
S.EndHorizontalLay();
}
S.EndVerticalLay();
dlg.Layout();
dlg.GetSizer()->Fit(&dlg);
dlg.SetMinSize(dlg.GetSize());
dlg.Center();
auto onButton = [&](wxCommandEvent &e)
{
dlg.EndModal(e.GetId());
};
dlg.Bind(wxEVT_BUTTON, onButton);
switch (dlg.ShowModal())
{
case wxID_HELP:
// Can't use the HelpSystem since the theme manager may not
// yet be initialized and it requires a configuration file.
OpenInDefaultBrowser("https://" +
HelpSystem::HelpHostname +
HelpSystem::HelpServerHomeDir +
"Error:_Audacity_settings_file_unwritable");
break;
case wxID_CANCEL:
// This REALLY needs to use an exception with decent cleanup and program exit
wxExit();
#if defined(__WXGTK__)
wxAbort();
#else
exit(-1);
#endif
break;
}
dlg.Unbind(wxEVT_BUTTON, onButton);
}

View File

@ -25,10 +25,25 @@ public:
const wxString& globalFilename = wxEmptyString,
long style = wxCONFIG_USE_LOCAL_FILE | wxCONFIG_USE_GLOBAL_FILE,
const wxMBConv& conv = wxConvAuto());
void Init();
virtual ~FileConfig();
virtual bool Flush(bool bCurrentOnly = false) wxOVERRIDE;
// Set and Get values of the version major/minor/micro keys in audacity.cfg when Audacity first opens
void SetVersionKeysInit( int major, int minor, int micro)
{
mVersionMajorKeyInit = major;
mVersionMinorKeyInit = minor;
mVersionMicroKeyInit = micro;
}
void GetVersionKeysInit( int& major, int& minor, int& micro) const
{
major = mVersionMajorKeyInit;
minor = mVersionMinorKeyInit;
micro = mVersionMicroKeyInit;
}
protected:
virtual bool DoWriteString(const wxString& key, const wxString& szValue) wxOVERRIDE;
virtual bool DoWriteLong(const wxString& key, long lValue) wxOVERRIDE;
@ -36,10 +51,21 @@ protected:
virtual bool DoWriteBinary(const wxString& key, const wxMemoryBuffer& buf) wxOVERRIDE;
#endif // wxUSE_BASE64
private:
void Warn(bool canRetry = true);
protected:
//! Override to notify the user of error conditions involving writability of config files
virtual void Warn(bool canRetry = true) = 0;
const FilePath &GetFilePath() const { return mConfigPath; }
private:
const FilePath mConfigPath;
// values of the version major/minor/micro keys in audacity.cfg
// when Audacity first opens
int mVersionMajorKeyInit{};
int mVersionMinorKeyInit{};
int mVersionMicroKeyInit{};
FilePath mConfigPath;
bool mDirty;
};