diff --git a/src/export/Export.cpp b/src/export/Export.cpp index f4917432d..6e7880491 100644 --- a/src/export/Export.cpp +++ b/src/export/Export.cpp @@ -282,6 +282,24 @@ BEGIN_EVENT_TABLE(Exporter, wxEvtHandler) END_EVENT_TABLE() namespace { +const auto PathStart = wxT("Exporters"); + +static Registry::GroupItem &sRegistry() +{ + static Registry::TransparentGroupItem<> registry{ PathStart }; + return registry; +} + +struct ExporterItem final : Registry::SingleItem { + ExporterItem( + const Identifier &id, const Exporter::ExportPluginFactory &factory ) + : SingleItem{ id } + , mFactory{ factory } + {} + + Exporter::ExportPluginFactory mFactory; +}; + using ExportPluginFactories = std::vector< Exporter::ExportPluginFactory >; ExportPluginFactories &sFactories() { @@ -291,15 +309,24 @@ namespace { } Exporter::RegisteredExportPlugin::RegisteredExportPlugin( - const ExportPluginFactory &factory ) + const Identifier &id, + const ExportPluginFactory &factory, + const Registry::Placement &placement ) { if ( factory ) - sFactories().emplace_back( factory ); + Registry::RegisterItem( sRegistry(), placement, + std::make_unique< ExporterItem >( id, factory ) ); } Exporter::Exporter( AudacityProject &project ) : mProject{ &project } { + using namespace Registry; + static OrderingPreferenceInitializer init{ + PathStart, + { {wxT(""), wxT("PCM,MP3,OGG,FLAC,MP2,CommandLine,FFmpeg") } }, + }; + mMixerSpec = NULL; mBook = NULL; @@ -307,15 +334,25 @@ Exporter::Exporter( AudacityProject &project ) for ( const auto &factory : sFactories() ) mPlugins.emplace_back( factory() ); - // The factories were pushed on the array at static initialization time in an - // unspecified sequence. Sort according to the sequence numbers the plugins - // report to make the order determinate. - std::sort( mPlugins.begin(), mPlugins.end(), - []( const ExportPluginArray::value_type &a, - const ExportPluginArray::value_type &b ){ - return a->SequenceNumber() < b->SequenceNumber(); + struct MyVisitor final : Visitor { + MyVisitor() + { + // visit the registry to collect the plug-ins properly + // sorted + TransparentGroupItem<> top{ PathStart }; + Registry::Visit( *this, &top, &sRegistry() ); } - ); + + void Visit( SingleItem &item, const Path &path ) override + { + mPlugins.emplace_back( + static_cast( item ).mFactory() ); + } + + ExportPluginArray mPlugins; + } visitor; + + mPlugins.swap( visitor.mPlugins ); SetFileDialogTitle( XO("Export Audio") ); } diff --git a/src/export/Export.h b/src/export/Export.h index d886e7d96..dc0ff8d17 100644 --- a/src/export/Export.h +++ b/src/export/Export.h @@ -19,6 +19,8 @@ #include "../widgets/wxPanelWrapper.h" // to inherit #include "../FileNames.h" // for FileTypes +#include "../commands/CommandManager.h" // for Registry::Placement + class wxArrayString; class FileDialogWrapper; class wxFileCtrlEvent; @@ -132,8 +134,6 @@ public: const Tags *metadata = NULL, int subformat = 0) = 0; - virtual unsigned SequenceNumber() const = 0; - protected: std::unique_ptr CreateMixer(const TrackList &tracks, bool selectionOnly, @@ -174,7 +174,10 @@ public: // to have some fresh state variables each time export begins again // and to compute translated strings for the current locale struct RegisteredExportPlugin{ - RegisteredExportPlugin( const ExportPluginFactory& ); + RegisteredExportPlugin( + const Identifier &id, // an internal string naming the plug-in + const ExportPluginFactory&, + const Registry::Placement &placement = { wxEmptyString, {} } ); }; static bool DoEditMetadata(AudacityProject &project, diff --git a/src/export/ExportCL.cpp b/src/export/ExportCL.cpp index 49a9d6a4f..d29c3ff87 100644 --- a/src/export/ExportCL.cpp +++ b/src/export/ExportCL.cpp @@ -310,8 +310,6 @@ public: MixerSpec *mixerSpec = NULL, const Tags *metadata = NULL, int subformat = 0) override; - - virtual unsigned SequenceNumber() const override { return 60; } }; ExportCL::ExportCL() @@ -567,5 +565,6 @@ void ExportCL::OptionsCreate(ShuttleGui &S, int format) S.AddWindow( safenew ExportCLOptions{ S.GetParent(), format } ); } -static Exporter::RegisteredExportPlugin -sRegisteredPlugin{ []{ return std::make_unique< ExportCL >(); } }; +static Exporter::RegisteredExportPlugin sRegisteredPlugin{ "CommandLine", + []{ return std::make_unique< ExportCL >(); } +}; diff --git a/src/export/ExportFFmpeg.cpp b/src/export/ExportFFmpeg.cpp index 4376be31f..9de4a4169 100644 --- a/src/export/ExportFFmpeg.cpp +++ b/src/export/ExportFFmpeg.cpp @@ -152,8 +152,6 @@ public: const Tags *metadata = NULL, int subformat = 0) override; - virtual unsigned SequenceNumber() const override { return 70; } - private: AVOutputFormat * mEncFormatDesc{}; // describes our output file to libavformat @@ -1141,8 +1139,9 @@ void ExportFFmpeg::OptionsCreate(ShuttleGui &S, int format) ExportPlugin::OptionsCreate(S, format); } -static Exporter::RegisteredExportPlugin -sRegisteredPlugin{ []{ return std::make_unique< ExportFFmpeg >(); } }; +static Exporter::RegisteredExportPlugin sRegisteredPlugin{ "FFmpeg", + []{ return std::make_unique< ExportFFmpeg >(); } +}; #endif diff --git a/src/export/ExportFLAC.cpp b/src/export/ExportFLAC.cpp index d78ffb481..3c4e4741e 100644 --- a/src/export/ExportFLAC.cpp +++ b/src/export/ExportFLAC.cpp @@ -224,8 +224,6 @@ public: const Tags *metadata = NULL, int subformat = 0) override; - virtual unsigned SequenceNumber() const override { return 40; } - private: bool GetMetadata(AudacityProject *project, const Tags *tags); @@ -474,8 +472,9 @@ bool ExportFLAC::GetMetadata(AudacityProject *project, const Tags *tags) return true; } -static Exporter::RegisteredExportPlugin -sRegisteredPlugin{ []{ return std::make_unique< ExportFLAC >(); } }; +static Exporter::RegisteredExportPlugin sRegisteredPlugin{ "FLAC", + []{ return std::make_unique< ExportFLAC >(); } +}; #endif // USE_LIBFLAC diff --git a/src/export/ExportMP2.cpp b/src/export/ExportMP2.cpp index af9476e86..f3472d020 100644 --- a/src/export/ExportMP2.cpp +++ b/src/export/ExportMP2.cpp @@ -217,8 +217,6 @@ public: const Tags *metadata = NULL, int subformat = 0) override; - virtual unsigned SequenceNumber() const override { return 50; } - private: int AddTags(AudacityProject *project, ArrayOf &buffer, bool *endOfFile, const Tags *tags); @@ -498,8 +496,9 @@ void ExportMP2::AddFrame(struct id3_tag *tp, const wxString & n, const wxString } #endif -static Exporter::RegisteredExportPlugin -sRegisteredPlugin{ []{ return std::make_unique< ExportMP2 >(); } }; +static Exporter::RegisteredExportPlugin sRegisteredPlugin{ "MP2", + []{ return std::make_unique< ExportMP2 >(); } +}; #endif // #ifdef USE_LIBTWOLAME diff --git a/src/export/ExportMP3.cpp b/src/export/ExportMP3.cpp index 5e081e5f0..83a98ab47 100644 --- a/src/export/ExportMP3.cpp +++ b/src/export/ExportMP3.cpp @@ -1687,8 +1687,6 @@ public: const Tags *metadata = NULL, int subformat = 0) override; - virtual unsigned SequenceNumber() const override { return 20; } - private: int AskResample(int bitrate, int rate, int lowrate, int highrate); @@ -2199,8 +2197,9 @@ void ExportMP3::AddFrame(struct id3_tag *tp, const wxString & n, const wxString } #endif -static Exporter::RegisteredExportPlugin -sRegisteredPlugin{ []{ return std::make_unique< ExportMP3 >(); } }; +static Exporter::RegisteredExportPlugin sRegisteredPlugin{ "MP3", + []{ return std::make_unique< ExportMP3 >(); } +}; //---------------------------------------------------------------------------- // Return library version diff --git a/src/export/ExportOGG.cpp b/src/export/ExportOGG.cpp index 9d2f2450b..41b4a5c58 100644 --- a/src/export/ExportOGG.cpp +++ b/src/export/ExportOGG.cpp @@ -143,8 +143,6 @@ public: const Tags *metadata = NULL, int subformat = 0) override; - virtual unsigned SequenceNumber() const override { return 30; } - private: bool FillComment(AudacityProject *project, vorbis_comment *comment, const Tags *metadata); @@ -398,8 +396,9 @@ bool ExportOGG::FillComment(AudacityProject *project, vorbis_comment *comment, c return true; } -static Exporter::RegisteredExportPlugin -sRegisteredPlugin{ []{ return std::make_unique< ExportOGG >(); } }; +static Exporter::RegisteredExportPlugin sRegisteredPlugin{ "OGG", + []{ return std::make_unique< ExportOGG >(); } +}; #endif // USE_LIBVORBIS diff --git a/src/export/ExportPCM.cpp b/src/export/ExportPCM.cpp index 585d65fd5..8e78af1c5 100644 --- a/src/export/ExportPCM.cpp +++ b/src/export/ExportPCM.cpp @@ -342,8 +342,6 @@ public: FileExtension GetExtension(int index) override; bool CheckFileName(wxFileName &filename, int format) override; - virtual unsigned SequenceNumber() const override { return 10; } - private: void ReportTooBigError(wxWindow * pParent); ArrayOf AdjustString(const wxString & wxStr, int sf_format); @@ -971,5 +969,6 @@ bool ExportPCM::CheckFileName(wxFileName &filename, int format) return ExportPlugin::CheckFileName(filename, format); } -static Exporter::RegisteredExportPlugin -sRegisteredPlugin{ []{ return std::make_unique< ExportPCM >(); } }; +static Exporter::RegisteredExportPlugin sRegisteredPlugin{ "PCM", + []{ return std::make_unique< ExportPCM >(); } +}; diff --git a/src/import/Import.cpp b/src/import/Import.cpp index a42683a7e..c72868bfc 100644 --- a/src/import/Import.cpp +++ b/src/import/Import.cpp @@ -84,11 +84,33 @@ ImportPluginList &Importer::sImportPluginList() return theList; } +namespace { +static const auto PathStart = wxT("Importers"); + +static Registry::GroupItem &sRegistry() +{ + static Registry::TransparentGroupItem<> registry{ PathStart }; + return registry; +} + +struct ImporterItem final : Registry::SingleItem { + ImporterItem( const Identifier &id, std::unique_ptr pPlugin ) + : SingleItem{ id } + , mpPlugin{ std::move( pPlugin ) } + {} + + std::unique_ptr mpPlugin; +}; +} + Importer::RegisteredImportPlugin::RegisteredImportPlugin( - std::unique_ptr pPlugin ) + const Identifier &id, + std::unique_ptr pPlugin, + const Registry::Placement &placement ) { if ( pPlugin ) - sImportPluginList().emplace_back( std::move( pPlugin ) ); + Registry::RegisterItem( sRegistry(), placement, + std::make_unique< ImporterItem >( id, std::move( pPlugin ) ) ); } UnusableImportPluginList &Importer::sUnusableImportPluginList() @@ -110,16 +132,29 @@ bool Importer::Initialize() // order is significant. If none match, they will all be tried // in the order defined here. - // They were pushed on the array at static initialization time in an - // unspecified sequence. Sort according to the sequence numbers they - // report to make the order determinate. - auto &list = sImportPluginList(); - std::sort( list.begin(), list.end(), - []( const ImportPluginList::value_type &a, - const ImportPluginList::value_type &b ){ - return a->SequenceNumber() < b->SequenceNumber(); + using namespace Registry; + static OrderingPreferenceInitializer init{ + PathStart, + { {wxT(""), wxT("PCM,OGG,FLAC,MP3,LOF,FFmpeg") } } + // QT and GStreamer are only conditionally compiled and would get + // placed at the end if present + }; + + static struct MyVisitor final : Visitor { + MyVisitor() + { + // Once only, visit the registry to collect the plug-ins properly + // sorted + TransparentGroupItem<> top{ PathStart }; + Registry::Visit( *this, &top, &sRegistry() ); } - ); + + void Visit( SingleItem &item, const Path &path ) override + { + sImportPluginList().push_back( + static_cast( item ).mpPlugin.get() ); + } + } visitor; // Ordering of the unusable plugin list is not important. @@ -292,7 +327,7 @@ void Importer::ReadImportItems() { if (importPlugin->GetPluginStringID() == new_item->filters[i]) { - new_item->filter_objects.push_back(importPlugin.get()); + new_item->filter_objects.push_back(importPlugin); found = true; break; } @@ -307,7 +342,7 @@ void Importer::ReadImportItems() bool found = false; for (size_t i = 0; i < new_item->filter_objects.size(); i++) { - if (importPlugin.get() == new_item->filter_objects[i]) + if (importPlugin == new_item->filter_objects[i]) { found = true; break; @@ -323,7 +358,7 @@ void Importer::ReadImportItems() new_item->filters.begin() + index, importPlugin->GetPluginStringID()); new_item->filter_objects.insert( - new_item->filter_objects.begin() + index, importPlugin.get()); + new_item->filter_objects.begin() + index, importPlugin); if (new_item->divider >= 0) new_item->divider++; } @@ -402,7 +437,7 @@ std::unique_ptr Importer::CreateDefaultImportItem() for (const auto &importPlugin : sImportPluginList()) { new_item->filters.push_back(importPlugin->GetPluginStringID()); - new_item->filter_objects.push_back(importPlugin.get()); + new_item->filter_objects.push_back(importPlugin); } new_item->divider = -1; return new_item; @@ -462,7 +497,7 @@ bool Importer::Import( AudacityProject &project, { // This plugin corresponds to user-selected filter, try it first. wxLogDebug(wxT("Inserting %s"),plugin->GetPluginStringID()); - importPlugins.insert(importPlugins.begin(), plugin.get()); + importPlugins.insert(importPlugins.begin(), plugin); } } } @@ -532,14 +567,14 @@ bool Importer::Import( AudacityProject &project, // is not changed by user selection overrides or any other mechanism, but we include an assert // in case subsequent code revisions to the constructor should break this assumption that // libsndfile is first. - ImportPlugin *libsndfilePlugin = sImportPluginList().begin()->get(); + ImportPlugin *libsndfilePlugin = *sImportPluginList().begin(); wxASSERT(libsndfilePlugin->GetPluginStringID() == wxT("libsndfile")); for (const auto &plugin : sImportPluginList()) { // Make sure its not already in the list if (importPlugins.end() == - std::find(importPlugins.begin(), importPlugins.end(), plugin.get())) + std::find(importPlugins.begin(), importPlugins.end(), plugin)) { if (plugin->SupportsExtension(extension)) { @@ -562,7 +597,7 @@ bool Importer::Import( AudacityProject &project, } } wxLogDebug(wxT("Appending %s"),plugin->GetPluginStringID()); - importPlugins.push_back(plugin.get()); + importPlugins.push_back(plugin); } } } @@ -577,10 +612,10 @@ bool Importer::Import( AudacityProject &project, { // Make sure its not already in the list if (importPlugins.end() == - std::find(importPlugins.begin(), importPlugins.end(), plugin.get())) + std::find(importPlugins.begin(), importPlugins.end(), plugin)) { wxLogDebug(wxT("Appending %s"),plugin->GetPluginStringID()); - importPlugins.push_back(plugin.get()); + importPlugins.push_back(plugin); } } } diff --git a/src/import/Import.h b/src/import/Import.h index e06b464ad..63ad89d5f 100644 --- a/src/import/Import.h +++ b/src/import/Import.h @@ -20,6 +20,8 @@ #include "../widgets/wxPanelWrapper.h" // to inherit #include "../FileNames.h" // for FileType +#include "../commands/CommandManager.h" // for Registry::Placement + class wxArrayString; class wxListBox; class AudacityProject; @@ -80,7 +82,10 @@ public: // Objects of this type are statically constructed in files implementing // subclasses of ImportPlugin struct RegisteredImportPlugin{ - RegisteredImportPlugin( std::unique_ptr ); + RegisteredImportPlugin( + const Identifier &id, // an internal string naming the plug-in + std::unique_ptr, + const Registry::Placement &placement = { wxEmptyString, {} } ); }; // Objects of this type are statically constructed in files, to identify diff --git a/src/import/ImportFFmpeg.cpp b/src/import/ImportFFmpeg.cpp index 14223eb13..11fa9f9eb 100644 --- a/src/import/ImportFFmpeg.cpp +++ b/src/import/ImportFFmpeg.cpp @@ -185,8 +185,6 @@ public: ///! Probes the file and opens it if appropriate std::unique_ptr Open( const FilePath &Filename, AudacityProject*) override; - - unsigned SequenceNumber() const override; }; ///! Does acual import, returned by FFmpegImportPlugin::Open @@ -335,12 +333,7 @@ std::unique_ptr FFmpegImportPlugin::Open( return std::move(handle); } -unsigned FFmpegImportPlugin::SequenceNumber() const -{ - return 60; -} - -static Importer::RegisteredImportPlugin registered{ +static Importer::RegisteredImportPlugin registered{ "FFmpeg", std::make_unique< FFmpegImportPlugin >() }; diff --git a/src/import/ImportFLAC.cpp b/src/import/ImportFLAC.cpp index 3ac4bd803..c942220a1 100644 --- a/src/import/ImportFLAC.cpp +++ b/src/import/ImportFLAC.cpp @@ -137,8 +137,6 @@ class FLACImportPlugin final : public ImportPlugin TranslatableString GetPluginFormatDescription() override; std::unique_ptr Open( const FilePath &Filename, AudacityProject*) override; - - unsigned SequenceNumber() const override; }; @@ -334,12 +332,7 @@ std::unique_ptr FLACImportPlugin::Open( return std::move(handle); } -unsigned FLACImportPlugin::SequenceNumber() const -{ - return 30; -} - -static Importer::RegisteredImportPlugin registered{ +static Importer::RegisteredImportPlugin registered{ "FLAC", std::make_unique< FLACImportPlugin >() }; diff --git a/src/import/ImportForwards.h b/src/import/ImportForwards.h index d41ccc0ec..ee957abc3 100644 --- a/src/import/ImportForwards.h +++ b/src/import/ImportForwards.h @@ -16,7 +16,7 @@ class ImportPlugin; class UnusableImportPlugin; using ImportPluginList = - std::vector< std::unique_ptr >; + std::vector< ImportPlugin * >; using UnusableImportPluginList = std::vector< std::unique_ptr >; diff --git a/src/import/ImportGStreamer.cpp b/src/import/ImportGStreamer.cpp index 8b75b853e..c7d36c132 100644 --- a/src/import/ImportGStreamer.cpp +++ b/src/import/ImportGStreamer.cpp @@ -249,8 +249,6 @@ public: ///! Probes the file and opens it if appropriate std::unique_ptr Open( const wxString &Filename, AudacityProject*) override; - - unsigned SequenceNumber() const override; }; // ============================================================================ @@ -261,7 +259,8 @@ public: // Instantiate GStreamerImportPlugin and add to the list of known importers static -Importer::RegisteredImportPlugin{ []() -> std::unique_ptr< ImportPlugin > { +Importer::RegisteredImportPlugin{ "GStreamer", + []() -> std::unique_ptr< ImportPlugin > { wxLogMessage(_TS("Audacity is built against GStreamer version %d.%d.%d-%d"), GST_VERSION_MAJOR, GST_VERSION_MINOR, @@ -1152,11 +1151,6 @@ GStreamerImportFileHandle::Import(TrackFactory *trackFactory, return updateResult; } -unsigned GStreamerImportPlugin::SequenceNumber() const -{ - return 80; -} - // ---------------------------------------------------------------------------- // Message handlers // ---------------------------------------------------------------------------- diff --git a/src/import/ImportLOF.cpp b/src/import/ImportLOF.cpp index 1274ae29d..af1a70664 100644 --- a/src/import/ImportLOF.cpp +++ b/src/import/ImportLOF.cpp @@ -116,8 +116,6 @@ public: TranslatableString GetPluginFormatDescription() override; std::unique_ptr Open( const FilePath &Filename, AudacityProject *pProject) override; - - unsigned SequenceNumber() const override; }; @@ -272,12 +270,7 @@ ProgressResult LOFImportFileHandle::Import( return ProgressResult::Success; } -unsigned LOFImportPlugin::SequenceNumber() const -{ - return 50; -} - -static Importer::RegisteredImportPlugin registered{ +static Importer::RegisteredImportPlugin registered{ "LOF", std::make_unique< LOFImportPlugin >() }; diff --git a/src/import/ImportMP3.cpp b/src/import/ImportMP3.cpp index 8b30a16a0..5d83630e3 100644 --- a/src/import/ImportMP3.cpp +++ b/src/import/ImportMP3.cpp @@ -117,8 +117,6 @@ public: TranslatableString GetPluginFormatDescription() override; std::unique_ptr Open( const FilePath &Filename, AudacityProject*) override; - - unsigned SequenceNumber() const override; }; class MP3ImportFileHandle final : public ImportFileHandle @@ -257,12 +255,7 @@ ProgressResult MP3ImportFileHandle::Import( return privateData.updateResult; } -unsigned MP3ImportPlugin::SequenceNumber() const -{ - return 40; -} - -static Importer::RegisteredImportPlugin registered{ +static Importer::RegisteredImportPlugin registered{ "MP3", std::make_unique< MP3ImportPlugin >() }; diff --git a/src/import/ImportOGG.cpp b/src/import/ImportOGG.cpp index a02ade9b5..04b599aa4 100644 --- a/src/import/ImportOGG.cpp +++ b/src/import/ImportOGG.cpp @@ -91,8 +91,6 @@ public: TranslatableString GetPluginFormatDescription() override; std::unique_ptr Open( const FilePath &Filename, AudacityProject*) override; - - unsigned SequenceNumber() const override; }; @@ -215,12 +213,7 @@ std::unique_ptr OggImportPlugin::Open( return std::make_unique(filename, std::move(file), std::move(vorbisFile)); } -unsigned OggImportPlugin::SequenceNumber() const -{ - return 20; -} - -static Importer::RegisteredImportPlugin registered{ +static Importer::RegisteredImportPlugin registered{ "OGG", std::make_unique< OggImportPlugin >() }; diff --git a/src/import/ImportPCM.cpp b/src/import/ImportPCM.cpp index 6df7c4033..666b8834a 100644 --- a/src/import/ImportPCM.cpp +++ b/src/import/ImportPCM.cpp @@ -89,8 +89,6 @@ public: TranslatableString GetPluginFormatDescription() override; std::unique_ptr Open( const FilePath &Filename, AudacityProject*) override; - - unsigned SequenceNumber() const override; }; @@ -192,12 +190,7 @@ std::unique_ptr PCMImportPlugin::Open( return std::make_unique(filename, std::move(file), info); } -unsigned PCMImportPlugin::SequenceNumber() const -{ - return 10; -} - -static Importer::RegisteredImportPlugin registered{ +static Importer::RegisteredImportPlugin registered{ "PCM", std::make_unique< PCMImportPlugin >() }; diff --git a/src/import/ImportPlugin.h b/src/import/ImportPlugin.h index faf660e34..482427f8f 100644 --- a/src/import/ImportPlugin.h +++ b/src/import/ImportPlugin.h @@ -96,8 +96,6 @@ public: virtual std::unique_ptr Open( const FilePath &Filename, AudacityProject*) = 0; - virtual unsigned SequenceNumber() const = 0; - virtual ~ImportPlugin() { } protected: diff --git a/src/import/ImportQT.cpp b/src/import/ImportQT.cpp index da75af54d..f931b5a63 100644 --- a/src/import/ImportQT.cpp +++ b/src/import/ImportQT.cpp @@ -125,8 +125,6 @@ class QTImportPlugin final : public ImportPlugin std::unique_ptr Open( const wxString & Filename, AudacityProject*) override; - unsigned SequenceNumber() const override; - private: bool mInitialized; }; @@ -222,12 +220,7 @@ std::unique_ptr QTImportPlugin::Open( return std::make_unique(Filename, theMovie); } -unsigned QTImportPlugin::SequenceNumber() const -{ - return 70; -} - -static Importer::RegisteredImportPlugin registered{ +static Importer::RegisteredImportPlugin registered{ "QT", std::make_unique< QTImportPlugin >() }; diff --git a/src/prefs/BatchPrefs.cpp b/src/prefs/BatchPrefs.cpp index 535f5c6a9..6a03afcfd 100644 --- a/src/prefs/BatchPrefs.cpp +++ b/src/prefs/BatchPrefs.cpp @@ -97,12 +97,16 @@ BatchPrefs::~BatchPrefs() #if 0 namespace{ -PrefsPanel::Registration sAttachment{ 190, +PrefsPanel::Registration sAttachment{ "Batch", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew return safenew BatchPrefs(parent, winid); - } + }, + false, + // Register with an explicit ordering hint because this one is + // only conditionally compiled + { "", { Registry::OrderingHint::Before, "KeyConfig" } } }; } #endif diff --git a/src/prefs/DevicePrefs.cpp b/src/prefs/DevicePrefs.cpp index 8ecc90c42..5a850bd98 100644 --- a/src/prefs/DevicePrefs.cpp +++ b/src/prefs/DevicePrefs.cpp @@ -423,7 +423,7 @@ bool DevicePrefs::Commit() } namespace{ -PrefsPanel::Registration sAttachment{ 10, +PrefsPanel::Registration sAttachment{ "Device", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew diff --git a/src/prefs/DirectoriesPrefs.cpp b/src/prefs/DirectoriesPrefs.cpp index 909058b62..af0be75a3 100644 --- a/src/prefs/DirectoriesPrefs.cpp +++ b/src/prefs/DirectoriesPrefs.cpp @@ -300,7 +300,7 @@ DirectoriesPrefsFactory() { } namespace{ -PrefsPanel::Registration sAttachment{ 150, +PrefsPanel::Registration sAttachment{ "Directories", DirectoriesPrefsFactory() }; }; diff --git a/src/prefs/EffectsPrefs.cpp b/src/prefs/EffectsPrefs.cpp index f632e64b7..088b4062e 100644 --- a/src/prefs/EffectsPrefs.cpp +++ b/src/prefs/EffectsPrefs.cpp @@ -251,7 +251,7 @@ bool EffectsPrefs::Commit() } namespace{ -PrefsPanel::Registration sAttachment{ 170, +PrefsPanel::Registration sAttachment{ "Effects", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew diff --git a/src/prefs/ExtImportPrefs.cpp b/src/prefs/ExtImportPrefs.cpp index 964e468c1..28a92f484 100644 --- a/src/prefs/ExtImportPrefs.cpp +++ b/src/prefs/ExtImportPrefs.cpp @@ -827,11 +827,14 @@ void ExtImportPrefsDropTarget::OnLeave() } namespace{ -PrefsPanel::Registration sAttachment{ 120, +PrefsPanel::Registration sAttachment{ "ExtImport", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew return safenew ExtImportPrefs(parent, winid); - } + }, + false, + // Place as a lower level of the tree of pages: + { "ImportExport" } }; } diff --git a/src/prefs/GUIPrefs.cpp b/src/prefs/GUIPrefs.cpp index 0f0fc684a..b30818abc 100644 --- a/src/prefs/GUIPrefs.cpp +++ b/src/prefs/GUIPrefs.cpp @@ -382,7 +382,7 @@ int ShowClippingPrefsID() } namespace{ -PrefsPanel::Registration sAttachment{ 60, +PrefsPanel::Registration sAttachment{ "GUI", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew diff --git a/src/prefs/ImportExportPrefs.cpp b/src/prefs/ImportExportPrefs.cpp index 0f16a5109..33980f1e3 100644 --- a/src/prefs/ImportExportPrefs.cpp +++ b/src/prefs/ImportExportPrefs.cpp @@ -150,12 +150,11 @@ bool ImportExportPrefs::Commit() } namespace{ -PrefsPanel::Registration sAttachment{ 110, +PrefsPanel::Registration sAttachment{ "ImportExport", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew return safenew ImportExportPrefs(parent, winid); - }, - 1 + } }; } diff --git a/src/prefs/KeyConfigPrefs.cpp b/src/prefs/KeyConfigPrefs.cpp index c2be8db85..b2a08b95a 100644 --- a/src/prefs/KeyConfigPrefs.cpp +++ b/src/prefs/KeyConfigPrefs.cpp @@ -890,7 +890,7 @@ KeyConfigPrefsFactory( const CommandID &name ) }; } namespace{ -PrefsPanel::Registration sAttachment{ 200, +PrefsPanel::Registration sAttachment{ "KeyConfig", KeyConfigPrefsFactory() }; } diff --git a/src/prefs/LibraryPrefs.cpp b/src/prefs/LibraryPrefs.cpp index 83cc8c063..fdb104169 100644 --- a/src/prefs/LibraryPrefs.cpp +++ b/src/prefs/LibraryPrefs.cpp @@ -263,12 +263,16 @@ bool LibraryPrefs::Commit() #if !defined(DISABLE_DYNAMIC_LOADING_FFMPEG) || !defined(DISABLE_DYNAMIC_LOADING_LAME) namespace{ -PrefsPanel::Registration sAttachment{ 140, +PrefsPanel::Registration sAttachment{ "Library", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew return safenew LibraryPrefs(parent, winid); - } + }, + false, + // Register with an explicit ordering hint because this one is + // only conditionally compiled + { "", { Registry::OrderingHint::Before, "Directories" } } }; } #endif diff --git a/src/prefs/MidiIOPrefs.cpp b/src/prefs/MidiIOPrefs.cpp index ddd854bbf..1a8ec49d4 100644 --- a/src/prefs/MidiIOPrefs.cpp +++ b/src/prefs/MidiIOPrefs.cpp @@ -303,12 +303,16 @@ bool MidiIOPrefs::Validate() #ifdef EXPERIMENTAL_MIDI_OUT namespace{ -PrefsPanel::Registration sAttachment{ 40, +PrefsPanel::Registration sAttachment{ "MidiIO", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew return safenew MidiIOPrefs(parent, winid); - } + }, + false, + // Register with an explicit ordering hint because this one is + // only conditionally compiled + { "", { Registry::OrderingHint::After, "Recording" } } }; } #endif diff --git a/src/prefs/ModulePrefs.cpp b/src/prefs/ModulePrefs.cpp index 604159b40..8f5dae341 100644 --- a/src/prefs/ModulePrefs.cpp +++ b/src/prefs/ModulePrefs.cpp @@ -191,12 +191,16 @@ void ModulePrefs::SetModuleStatus(const FilePath &fname, int iStatus){ #ifdef EXPERIMENTAL_MODULE_PREFS namespace{ -PrefsPanel::Registration sAttachment{ 220, +PrefsPanel::Registration sAttachment{ "Module", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew return safenew ModulePrefs(parent, winid); - } + }, + false, + // Register with an explicit ordering hint because this one is + // only conditionally compiled + { "", { Registry::OrderingHint::After, "Mouse" } } }; } #endif diff --git a/src/prefs/MousePrefs.cpp b/src/prefs/MousePrefs.cpp index 705c379c2..cd72eb7c0 100644 --- a/src/prefs/MousePrefs.cpp +++ b/src/prefs/MousePrefs.cpp @@ -223,7 +223,7 @@ bool MousePrefs::Commit() } namespace{ -PrefsPanel::Registration sAttachment{ 210, +PrefsPanel::Registration sAttachment{ "Mouse", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew diff --git a/src/prefs/PlaybackPrefs.cpp b/src/prefs/PlaybackPrefs.cpp index 1b4f57fb5..a27a6d69f 100644 --- a/src/prefs/PlaybackPrefs.cpp +++ b/src/prefs/PlaybackPrefs.cpp @@ -183,7 +183,7 @@ bool PlaybackPrefs::Commit() } namespace{ -PrefsPanel::Registration sAttachment{ 20, +PrefsPanel::Registration sAttachment{ "Playback", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew diff --git a/src/prefs/PrefsDialog.cpp b/src/prefs/PrefsDialog.cpp index 6ec6b657f..d489242a2 100644 --- a/src/prefs/PrefsDialog.cpp +++ b/src/prefs/PrefsDialog.cpp @@ -39,6 +39,7 @@ #include "../AudioIOBase.h" #include "../Prefs.h" #include "../ShuttleGui.h" +#include "../commands/CommandManager.h" #include "PrefsPanel.h" @@ -452,46 +453,101 @@ int wxTreebookExt::SetSelection(size_t n) } namespace { - struct Entry{ - unsigned sequenceNumber; - PrefsDialog::PrefsNode node; - bool operator < ( const Entry &other ) const - { return sequenceNumber < other.sequenceNumber; } - }; - using Entries = std::vector< Entry >; - Entries &Registry() +struct PrefsItem final : Registry::ConcreteGroupItem { + PrefsPanel::Factory factory; + bool expanded{ false }; + + PrefsItem( const wxString &name, + const PrefsPanel::Factory &factory_, bool expanded_ ) + : ConcreteGroupItem{ name } + , factory{ factory_ }, expanded{ expanded_ } + {} +}; + +// Collects registry tree nodes into a vector, in preorder. +struct PrefsItemVisitor final : Registry::Visitor { + PrefsItemVisitor( PrefsDialog::Factories &factories_ ) + : factories{ factories_ } { - static Entries result; - return result; + childCounts.push_back( 0 ); } + void BeginGroup( Registry::GroupItem &item, const Path & ) override + { + auto pItem = dynamic_cast( &item ); + if (!pItem) + return; + indices.push_back( factories.size() ); + factories.emplace_back( pItem->factory, 0, pItem->expanded ); + ++childCounts.back(); + childCounts.push_back( 0 ); + } + void EndGroup( Registry::GroupItem &item, const Path & ) override + { + auto pItem = dynamic_cast( &item ); + if (!pItem) + return; + auto &factory = factories[ indices.back() ]; + factory.nChildren = childCounts.back(); + childCounts.pop_back(); + indices.pop_back(); + } + + PrefsDialog::Factories &factories; + std::vector childCounts; + std::vector indices; +}; + + +const auto PathStart = wxT("Preferences"); + } -PrefsPanel::Registration::Registration( unsigned sequenceNumber, - const Factory &factory, - unsigned nChildren, - bool expanded ) + +namespace { +static Registry::GroupItem &sRegistry() { - auto ®istry = Registry(); - Entry entry{ sequenceNumber, { factory, nChildren, expanded } }; - const auto end = registry.end(); - // Find insertion point: - auto iter = std::lower_bound( registry.begin(), end, entry ); - // There should not be duplicate sequence numbers: - wxASSERT( iter == end || entry < *iter ); - registry.insert( iter, entry ); + static Registry::TransparentGroupItem<> registry{ PathStart }; + return registry; +} +} + +PrefsPanel::Registration::Registration( const wxString &name, + const Factory &factory, bool expanded, + const Registry::Placement &placement ) +{ + Registry::RegisterItem( sRegistry(), placement, + std::make_unique< PrefsItem >( name, factory, expanded ) ); } PrefsDialog::Factories &PrefsDialog::DefaultFactories() { + // Once only, cause initial population of preferences for the ordering + // of some preference pages that used to be given in a table but are now + // separately registered in several .cpp files; the sequence of registration + // depends on unspecified accidents of static initialization order across + // compilation units, so we need something specific here to preserve old + // default appearance of the preference dialog. + // But this needs only to mention some strings -- there is no compilation or + // link dependency of this source file on those other implementation files. + static Registry::OrderingPreferenceInitializer init{ + PathStart, + { + {wxT(""), + wxT("Device,Playback,Recording,Quality,GUI,Tracks,ImportExport,Directories,Warnings,Effects,KeyConfig,Mouse") + }, + {wxT("/Tracks"), wxT("TracksBehaviors,Spectrum")}, + } + }; + static Factories factories; static std::once_flag flag; std::call_once( flag, []{ - for ( const auto &entry : Registry() ) { - factories.push_back( entry.node ); - } + PrefsItemVisitor visitor{ factories }; + Registry::TransparentGroupItem<> top{ PathStart }; + Registry::Visit( visitor, &top, &sRegistry() ); } ); return factories; } diff --git a/src/prefs/PrefsDialog.h b/src/prefs/PrefsDialog.h index 10def863f..198464fd6 100644 --- a/src/prefs/PrefsDialog.h +++ b/src/prefs/PrefsDialog.h @@ -39,8 +39,8 @@ class PrefsDialog /* not final */ : public wxDialogWrapper std::function< PrefsPanel * ( wxWindow *parent, wxWindowID winid, AudacityProject *) >; Factory factory; - unsigned nChildren; - bool expanded; + size_t nChildren{ 0 }; + bool expanded{ false }; PrefsNode(const Factory &factory_, unsigned nChildren_ = 0, diff --git a/src/prefs/PrefsPanel.h b/src/prefs/PrefsPanel.h index c8508ac8f..bcd8063b5 100644 --- a/src/prefs/PrefsPanel.h +++ b/src/prefs/PrefsPanel.h @@ -30,6 +30,7 @@ MousePrefs, QualityPrefs, SpectrumPrefs and ThemePrefs. #include #include "../widgets/wxPanelWrapper.h" // to inherit #include "../include/audacity/ComponentInterface.h" +#include "../commands/CommandManager.h" /* A few constants for an attempt at semi-uniformity */ #define PREFS_FONT_SIZE 8 @@ -61,10 +62,9 @@ class PrefsPanel /* not final */ : public wxPanelWrapper, ComponentInterface // also implements the PrefsPanel subclass. struct Registration final { - Registration( unsigned sequenceNumber, - const Factory &factory, - unsigned nChildren = 0, - bool expanded = true ); + Registration( const wxString &name, const Factory &factory, + bool expanded = true, + const Registry::Placement &placement = { wxEmptyString, {} }); }; PrefsPanel(wxWindow * parent, diff --git a/src/prefs/ProjectsPrefs.cpp b/src/prefs/ProjectsPrefs.cpp index 092ab3e03..3c4e0effe 100644 --- a/src/prefs/ProjectsPrefs.cpp +++ b/src/prefs/ProjectsPrefs.cpp @@ -99,12 +99,16 @@ bool ProjectsPrefs::Commit() #ifdef EXPERIMENTAL_OD_DATA namespace{ -PrefsPanel::Registration sAttachment{ 130, +PrefsPanel::Registration sAttachment{ "Projects", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew return safenew ProjectsPrefs(parent, winid); - } + }, + false, + // Register with an explicit ordering hint because this one is + // only conditionally compiled + { "", { Registry::OrderingHint::After, "ImportExport" } } }; } #endif diff --git a/src/prefs/QualityPrefs.cpp b/src/prefs/QualityPrefs.cpp index 88a424bc0..ef4504a8e 100644 --- a/src/prefs/QualityPrefs.cpp +++ b/src/prefs/QualityPrefs.cpp @@ -234,7 +234,7 @@ bool QualityPrefs::Commit() } namespace{ -PrefsPanel::Registration sAttachment{ 50, +PrefsPanel::Registration sAttachment{ "Quality", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew diff --git a/src/prefs/RecordingPrefs.cpp b/src/prefs/RecordingPrefs.cpp index bf04ed74e..92ab9c56e 100644 --- a/src/prefs/RecordingPrefs.cpp +++ b/src/prefs/RecordingPrefs.cpp @@ -305,7 +305,7 @@ void RecordingPrefs::OnToggleCustomName(wxCommandEvent & /* Evt */) } namespace{ -PrefsPanel::Registration sAttachment{ 30, +PrefsPanel::Registration sAttachment{ "Recording", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew diff --git a/src/prefs/SpectrumPrefs.cpp b/src/prefs/SpectrumPrefs.cpp index 09dcb0ec7..156b56604 100644 --- a/src/prefs/SpectrumPrefs.cpp +++ b/src/prefs/SpectrumPrefs.cpp @@ -602,8 +602,10 @@ SpectrumPrefsFactory( WaveTrack *wt ) } namespace{ -PrefsPanel::Registration sAttachment{ 100, +PrefsPanel::Registration sAttachment{ "Spectrum", SpectrumPrefsFactory( nullptr ), - false + false, + // Place it at a lower tree level + { "Tracks" } }; } diff --git a/src/prefs/ThemePrefs.cpp b/src/prefs/ThemePrefs.cpp index 2d1d0ddfd..3c6de95e9 100644 --- a/src/prefs/ThemePrefs.cpp +++ b/src/prefs/ThemePrefs.cpp @@ -233,12 +233,16 @@ bool ThemePrefs::Commit() #ifdef EXPERIMENTAL_THEME_PREFS namespace{ -PrefsPanel::Registration sAttachment{ 180, +PrefsPanel::Registration sAttachment{ "Theme", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew return safenew ThemePrefs(parent, winid); - } + }, + false, + // Register with an explicit ordering hint because this one is + // only conditionally compiled + { "", { Registry::OrderingHint::After, "Effects" } } }; } #endif diff --git a/src/prefs/TracksBehaviorsPrefs.cpp b/src/prefs/TracksBehaviorsPrefs.cpp index 2cd89ff79..5da8861b4 100644 --- a/src/prefs/TracksBehaviorsPrefs.cpp +++ b/src/prefs/TracksBehaviorsPrefs.cpp @@ -132,11 +132,14 @@ bool TracksBehaviorsPrefs::Commit() } namespace{ -PrefsPanel::Registration sAttachment{ 90, +PrefsPanel::Registration sAttachment{ "TracksBehaviors", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew return safenew TracksBehaviorsPrefs(parent, winid); - } + }, + false, + // Place it at a lower tree level + { "Tracks" } }; } diff --git a/src/prefs/TracksPrefs.cpp b/src/prefs/TracksPrefs.cpp index 2916cc2ab..a8cfde7e1 100644 --- a/src/prefs/TracksPrefs.cpp +++ b/src/prefs/TracksPrefs.cpp @@ -448,12 +448,11 @@ bool TracksPrefs::Commit() } namespace{ -PrefsPanel::Registration sAttachment{ 70, +PrefsPanel::Registration sAttachment{ "Tracks", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew return safenew TracksPrefs(parent, winid); - }, - 2 + } }; } diff --git a/src/prefs/WarningsPrefs.cpp b/src/prefs/WarningsPrefs.cpp index 2ad2a306f..2a09b9bd7 100644 --- a/src/prefs/WarningsPrefs.cpp +++ b/src/prefs/WarningsPrefs.cpp @@ -107,7 +107,7 @@ bool WarningsPrefs::Commit() } namespace{ -PrefsPanel::Registration sAttachment{ 160, +PrefsPanel::Registration sAttachment{ "Warnings", [](wxWindow *parent, wxWindowID winid, AudacityProject *) { wxASSERT(parent); // to justify safenew diff --git a/src/prefs/WaveformPrefs.cpp b/src/prefs/WaveformPrefs.cpp index c9159f6a1..6ff108b7c 100644 --- a/src/prefs/WaveformPrefs.cpp +++ b/src/prefs/WaveformPrefs.cpp @@ -265,9 +265,12 @@ WaveformPrefsFactory(WaveTrack *wt) } #if 0 namespace{ -PrefsPanel::Registration sAttachment{ 80, +PrefsPanel::Registration sAttachment{ "Waveform", WaveformPrefsFactory( nullptr ), - false + false, + // Register with an explicit ordering hint because this one is + // only conditionally compiled; and place it at a lower tree level + { "Tracks", { Registry::OrderingHint::Before, "Spectrum" } } }; } #endif