1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-02 08:59:28 +02:00

Don't hard-code the exhaustive list of sub-view types...

... in Wave track context menu and SetTrackVisualsCommand

Instead, discover them through a registry.

This eliminates some duplication of string constants and prepares for
non-intrusive generalization to more kinds of sub-views.

This makes the command agnostic about which subview types are known, but the
context menu still has special case treatment for Spectrogram Settings and
Wave Colors.
This commit is contained in:
Paul Licameli 2020-01-18 14:37:49 -05:00
parent fb8ba0ce43
commit 36aad4d1c6
7 changed files with 164 additions and 53 deletions

View File

@ -87,9 +87,10 @@ public:
const wxString &Internal() const { return mInternal; } const wxString &Internal() const { return mInternal; }
const TranslatableString &Msgid() const { return mMsgid; } const TranslatableString &Msgid() const { return mMsgid; }
const TranslatableString Stripped() const { return mMsgid.Stripped(); }
const wxString Translation() const { return mMsgid.Translation(); } const wxString Translation() const { return mMsgid.Translation(); }
const wxString StrippedTranslation() const const wxString StrippedTranslation() const
{ return mMsgid.Stripped().Translation(); } { return Stripped().Translation(); }
bool empty() const { return mInternal.empty(); } bool empty() const { return mInternal.empty(); }

View File

@ -248,20 +248,6 @@ static const EnumValueSymbol kColourStrings[nColours] =
}; };
enum kDisplayTypes
{
kWaveform,
kSpectrogram,
nDisplayTypes
};
static const EnumValueSymbol kDisplayTypeStrings[nDisplayTypes] =
{
// These are acceptable dual purpose internal/visible names
{ XO("Waveform") },
{ XO("Spectrogram") },
};
enum kScaleTypes enum kScaleTypes
{ {
kLinear, kLinear,
@ -292,10 +278,22 @@ static const EnumValueSymbol kZoomTypeStrings[nZoomTypes] =
{ XO("HalfWave") }, { XO("HalfWave") },
}; };
static EnumValueSymbols DiscoverSubViewTypes()
{
const auto &types = WaveTrackSubView::AllTypes();
return transform_container< EnumValueSymbols >(
types, std::mem_fn( &WaveTrackSubView::Type::name ) );
}
bool SetTrackVisualsCommand::DefineParams( ShuttleParams & S ){ bool SetTrackVisualsCommand::DefineParams( ShuttleParams & S ){
SetTrackBase::DefineParams( S ); SetTrackBase::DefineParams( S );
S.OptionalN( bHasHeight ).Define( mHeight, wxT("Height"), 120, 44, 2000 ); S.OptionalN( bHasHeight ).Define( mHeight, wxT("Height"), 120, 44, 2000 );
S.OptionalN( bHasDisplayType ).DefineEnum( mDisplayType, wxT("Display"), kWaveform, kDisplayTypeStrings, nDisplayTypes );
{
auto symbols = DiscoverSubViewTypes();
S.OptionalN( bHasDisplayType ).DefineEnum( mDisplayType, wxT("Display"), 0, symbols.data(), symbols.size() );
}
S.OptionalN( bHasScaleType ).DefineEnum( mScaleType, wxT("Scale"), kLinear, kScaleTypeStrings, nScaleTypes ); S.OptionalN( bHasScaleType ).DefineEnum( mScaleType, wxT("Scale"), kLinear, kScaleTypeStrings, nScaleTypes );
S.OptionalN( bHasColour ).DefineEnum( mColour, wxT("Color"), kColour0, kColourStrings, nColours ); S.OptionalN( bHasColour ).DefineEnum( mColour, wxT("Color"), kColour0, kColourStrings, nColours );
S.OptionalN( bHasVZoom ).DefineEnum( mVZoom, wxT("VZoom"), kReset, kZoomTypeStrings, nZoomTypes ); S.OptionalN( bHasVZoom ).DefineEnum( mVZoom, wxT("VZoom"), kReset, kZoomTypeStrings, nZoomTypes );
@ -318,8 +316,15 @@ void SetTrackVisualsCommand::PopulateOrExchange(ShuttleGui & S)
S.Optional( bHasHeight ).TieNumericTextBox( XO("Height:"), mHeight ); S.Optional( bHasHeight ).TieNumericTextBox( XO("Height:"), mHeight );
S.Optional( bHasColour ).TieChoice( XO("Color:"), mColour, S.Optional( bHasColour ).TieChoice( XO("Color:"), mColour,
Msgids( kColourStrings, nColours ) ); Msgids( kColourStrings, nColours ) );
{
auto symbols = DiscoverSubViewTypes();
auto typeNames = transform_container<TranslatableStrings>(
symbols, std::mem_fn( &EnumValueSymbol::Stripped ) );
S.Optional( bHasDisplayType ).TieChoice( XO("Display:"), mDisplayType, S.Optional( bHasDisplayType ).TieChoice( XO("Display:"), mDisplayType,
Msgids( kDisplayTypeStrings, nDisplayTypes ) ); typeNames );
}
S.Optional( bHasScaleType ).TieChoice( XO("Scale:"), mScaleType, S.Optional( bHasScaleType ).TieChoice( XO("Scale:"), mScaleType,
Msgids( kScaleTypeStrings, nScaleTypes ) ); Msgids( kScaleTypeStrings, nScaleTypes ) );
S.Optional( bHasVZoom ).TieChoice( XO("VZoom:"), mVZoom, S.Optional( bHasVZoom ).TieChoice( XO("VZoom:"), mVZoom,
@ -355,10 +360,7 @@ bool SetTrackVisualsCommand::ApplyInner(const CommandContext & context, Track *
if( wt && bHasDisplayType ) if( wt && bHasDisplayType )
WaveTrackView::Get( *wt ).SetDisplay( WaveTrackView::Get( *wt ).SetDisplay(
(mDisplayType == kWaveform) ? WaveTrackSubView::AllTypes()[ mDisplayType ].id );
WaveTrackViewConstants::Waveform
: WaveTrackViewConstants::Spectrum
);
if( wt && bHasScaleType ) if( wt && bHasScaleType )
wt->GetIndependentWaveformSettings().scaleType = wt->GetIndependentWaveformSettings().scaleType =
(mScaleType==kLinear) ? (mScaleType==kLinear) ?

View File

@ -30,6 +30,11 @@ Paul Licameli split from WaveTrackView.cpp
#include <wx/dcmemory.h> #include <wx/dcmemory.h>
#include <wx/graphics.h> #include <wx/graphics.h>
static WaveTrackSubView::RegisteredType reg{ {
WaveTrackViewConstants::Spectrum,
{ wxT("Spectrogram"), XO("&Spectrogram") }
} };
SpectrumView::~SpectrumView() = default; SpectrumView::~SpectrumView() = default;
bool SpectrumView::IsSpectral() const bool SpectrumView::IsSpectral() const

View File

@ -104,6 +104,8 @@ std::vector<UIHandlePtr> WaveTrackControls::HitTest
} }
enum { enum {
reserveDisplays = 100,
OnRate8ID = 30000, // <--- OnRate8ID = 30000, // <---
OnRate11ID, // | OnRate11ID, // |
OnRate16ID, // | OnRate16ID, // |
@ -123,8 +125,9 @@ enum {
OnFloatID, // <--- OnFloatID, // <---
OnMultiViewID, OnMultiViewID,
OnWaveformID,
OnSpectrumID, OnSetDisplayId, lastDisplayId = (OnSetDisplayId + reserveDisplays - 1),
OnSpectrogramSettingsID, OnSpectrogramSettingsID,
OnChannelLeftID, OnChannelLeftID,
@ -620,6 +623,16 @@ void WaveTrackMenuTable::InitUserData(void *pUserData)
mpData = static_cast<PlayableTrackControls::InitMenuData*>(pUserData); mpData = static_cast<PlayableTrackControls::InitMenuData*>(pUserData);
} }
static WaveTrackSubView::Types AllTypes()
{
auto result = WaveTrackSubView::AllTypes();
if ( result.size() > reserveDisplays ) {
wxASSERT( false );
result.resize( reserveDisplays );
}
return result;
}
void WaveTrackMenuTable::InitMenu(Menu *pMenu) void WaveTrackMenuTable::InitMenu(Menu *pMenu)
{ {
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack); WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
@ -631,16 +644,40 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu)
if (multiView) if (multiView)
checkedIds.push_back( OnMultiViewID ); checkedIds.push_back( OnMultiViewID );
bool hasSpectrum = false;
int uniqueDisplay = 0; int uniqueDisplay = 0;
{
// Find the set of type ids of the shown displays, disregarding their
// top-to-bottom arrangement
auto displays = view.GetDisplays();
const auto end = displays.end();
auto iter = displays.begin();
std::sort( iter, end );
const auto displays = view.GetDisplays(); // Check the corresponding menu items, and decide which if any has
for ( auto display : displays ) { // the unique check
auto id = (display == WaveTrackViewConstants::Waveform) int displayId = OnSetDisplayId;
? OnWaveformID int nDisplays = 0;
: OnSpectrumID; for ( const auto &type : AllTypes() ) {
checkedIds.push_back( id ); if ( iter != end && *iter == type.id ) {
if ( displays.size() == 1 ) checkedIds.push_back( displayId );
uniqueDisplay = id; uniqueDisplay = displayId;
++iter;
++nDisplays;
// Unfortunately this special knowledge of the Spectrum view type
// remains. In future, either let a registry system insert this
// menu item, or (better) move it to the context menu of the
// Spectrum vertical ruler.
// (But the latter won't be satisfactory without a means to
// open that other context menu with keystrokes only, and that
// would require some notion of a focused sub-view.)
hasSpectrum = ( type.id == WaveTrackViewConstants::Spectrum );
}
++displayId;
}
if ( nDisplays > 1 )
uniqueDisplay = 0;
} }
if ( multiView && uniqueDisplay ) if ( multiView && uniqueDisplay )
@ -652,9 +689,6 @@ void WaveTrackMenuTable::InitMenu(Menu *pMenu)
// We can't change them on the fly yet anyway. // We can't change them on the fly yet anyway.
auto gAudioIO = AudioIOBase::Get(); auto gAudioIO = AudioIOBase::Get();
const bool bAudioBusy = gAudioIO->IsBusy(); const bool bAudioBusy = gAudioIO->IsBusy();
bool hasSpectrum =
make_iterator_range( displays.begin(), displays.end() )
.contains( WaveTrackViewConstants::Spectrum );
pMenu->Enable(OnSpectrogramSettingsID, hasSpectrum && !bAudioBusy); pMenu->Enable(OnSpectrogramSettingsID, hasSpectrum && !bAudioBusy);
AudacityProject *const project = &mpData->project; AudacityProject *const project = &mpData->project;
@ -733,13 +767,14 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable)
if ( WaveTrackSubViews::slots() > 1 ) if ( WaveTrackSubViews::slots() > 1 )
POPUP_MENU_CHECK_ITEM(OnMultiViewID, XO("&Multi-view"), OnMultiView) POPUP_MENU_CHECK_ITEM(OnMultiViewID, XO("&Multi-view"), OnMultiView)
int id = OnSetDisplayId;
for ( const auto &type : AllTypes() ) {
if ( view.GetMultiView() ) { if ( view.GetMultiView() ) {
POPUP_MENU_CHECK_ITEM(OnWaveformID, XO("Wa&veform"), OnSetDisplay) POPUP_MENU_CHECK_ITEM(id++, type.name.Msgid(), OnSetDisplay)
POPUP_MENU_CHECK_ITEM(OnSpectrumID, XO("&Spectrogram"), OnSetDisplay)
} }
else { else {
POPUP_MENU_RADIO_ITEM(OnWaveformID, XO("Wa&veform"), OnSetDisplay) POPUP_MENU_RADIO_ITEM(id++, type.name.Msgid(), OnSetDisplay)
POPUP_MENU_RADIO_ITEM(OnSpectrumID, XO("&Spectrogram"), OnSetDisplay) }
} }
POPUP_MENU_ITEM(OnSpectrogramSettingsID, XO("S&pectrogram Settings..."), OnSpectrogramSettings) POPUP_MENU_ITEM(OnSpectrogramSettingsID, XO("S&pectrogram Settings..."), OnSpectrogramSettings)
@ -799,19 +834,12 @@ void WaveTrackMenuTable::OnMultiView(wxCommandEvent & event)
/// Set the Display mode based on the menu choice in the Track Menu. /// Set the Display mode based on the menu choice in the Track Menu.
void WaveTrackMenuTable::OnSetDisplay(wxCommandEvent & event) void WaveTrackMenuTable::OnSetDisplay(wxCommandEvent & event)
{ {
using namespace WaveTrackViewConstants;
int idInt = event.GetId(); int idInt = event.GetId();
wxASSERT(idInt >= OnWaveformID && idInt <= OnSpectrumID); wxASSERT(idInt >= OnSetDisplayId &&
idInt <= lastDisplayId);
const auto pTrack = static_cast<WaveTrack*>(mpData->pTrack); const auto pTrack = static_cast<WaveTrack*>(mpData->pTrack);
WaveTrackView::Display id; auto id = AllTypes()[ idInt - OnSetDisplayId ].id;
switch (idInt) {
default:
case OnWaveformID:
id = Waveform; break;
case OnSpectrumID:
id = Spectrum; break;
}
auto &view = WaveTrackView::Get( *pTrack ); auto &view = WaveTrackView::Get( *pTrack );
if ( view.GetMultiView() ) { if ( view.GetMultiView() ) {
@ -887,7 +915,7 @@ void WaveTrackMenuTable::OnSpectrogramSettings(wxCommandEvent &)
// factories.push_back(WaveformPrefsFactory( pTrack )); // factories.push_back(WaveformPrefsFactory( pTrack ));
factories.push_back(SpectrumPrefsFactory( pTrack )); factories.push_back(SpectrumPrefsFactory( pTrack ));
const int page = const int page =
// (pTrack->GetDisplay() == WaveTrack::Spectrum) ? 1 : // (pTrack->GetDisplay() == WaveTrackViewConstants::Spectrum) ? 1 :
0; 0;
auto title = XO("%s:").Format( pTrack->GetName() ); auto title = XO("%s:").Format( pTrack->GetName() );

View File

@ -40,6 +40,53 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../ui/ButtonHandle.h" #include "../../../ui/ButtonHandle.h"
#include "../../../../TrackInfo.h" #include "../../../../TrackInfo.h"
namespace {
class Registry {
public:
using Type = WaveTrackSubView::Type;
using Types = std::vector< Type >;
void Append( Type type )
{
types.emplace_back( std::move( type ) );
sorted = false;
}
Types &Get()
{
if ( !sorted ) {
auto begin = types.begin(), end = types.end();
std::sort( begin, end );
// We don't want duplicate ids!
wxASSERT( end == std::adjacent_find( begin, end ) );
sorted = true;
}
return types;
}
private:
Types types;
bool sorted = false;
};
Registry &GetRegistry()
{
static Registry result;
return result;
}
}
WaveTrackSubView::RegisteredType::RegisteredType( Type type )
{
GetRegistry().Append( std::move( type ) );
}
// static
auto WaveTrackSubView::AllTypes() -> const Types &
{
return GetRegistry().Get();
}
namespace { namespace {
using WaveTrackSubViewPtrs = std::vector< std::shared_ptr< WaveTrackSubView > >; using WaveTrackSubViewPtrs = std::vector< std::shared_ptr< WaveTrackSubView > >;

View File

@ -13,16 +13,39 @@ Paul Licameli split from class WaveTrack
#include "../../../ui/CommonTrackView.h" #include "../../../ui/CommonTrackView.h"
#include "../../../../ClientData.h" #include "../../../../ClientData.h"
#include "audacity/ComponentInterface.h"
namespace WaveTrackViewConstants{ enum Display : int; } namespace WaveTrackViewConstants{ enum Display : int; }
class CutlineHandle; class CutlineHandle;
class TranslatableString;
class WaveTrack; class WaveTrack;
class WaveTrackView; class WaveTrackView;
class WaveTrackSubView : public CommonTrackView class WaveTrackSubView : public CommonTrackView
{ {
public: public:
using Display = WaveTrackViewConstants::Display; using Display = WaveTrackViewConstants::Display;
struct Type {
// Identifies the type session-wide, and determines relative position in
// menus listing all types
Display id;
// The translation is suitable for the track control panel drop-down,
// and it may contain a menu accelerator
EnumValueSymbol name;
bool operator < ( const Type &other ) const { return id < other.id; }
bool operator == ( const Type &other ) const { return id == other.id; }
};
using Types = std::vector< Type >;
// Typically a file scope statically constructed object
struct RegisteredType {
RegisteredType( Type type );
};
// Discover all registered types
static const Types &AllTypes();
explicit explicit
WaveTrackSubView( WaveTrackView &waveTrackView ); WaveTrackSubView( WaveTrackView &waveTrackView );

View File

@ -36,6 +36,11 @@ Paul Licameli split from WaveTrackView.cpp
#include <wx/graphics.h> #include <wx/graphics.h>
#include <wx/dc.h> #include <wx/dc.h>
static WaveTrackSubView::RegisteredType reg{ {
WaveTrackViewConstants::Waveform,
{ wxT("Waveform"), XO("Wa&veform") }
} };
WaveformView::~WaveformView() = default; WaveformView::~WaveformView() = default;
std::vector<UIHandlePtr> WaveformView::DetailedHitTest( std::vector<UIHandlePtr> WaveformView::DetailedHitTest(