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

Register Wave Color, Spectrogram Settings popup menu items

This commit is contained in:
Paul Licameli 2020-02-14 22:04:08 -05:00
parent 87382d51d8
commit 3b9b0eb5f2
4 changed files with 311 additions and 208 deletions

View File

@ -665,3 +665,144 @@ static const WaveTrackSubViews::RegisteredFactory key{
return std::make_shared< SpectrumView >( view );
}
};
// The following attaches the spectrogram settings item to the wave track popup
// menu. It is appropriate only to spectrum view and so is kept in this
// source file with the rest of the spectrum view implementation.
#include "WaveTrackControls.h"
#include "../../../../AudioIOBase.h"
#include "../../../../Menus.h"
#include "../../../../ProjectHistory.h"
#include "../../../../RefreshCode.h"
#include "../../../../prefs/PrefsDialog.h"
#include "../../../../prefs/SpectrumPrefs.h"
#include "../../../../widgets/AudacityMessageBox.h"
#include "../../../../widgets/PopupMenuTable.h"
namespace {
struct SpectrogramSettingsHandler : PopupMenuHandler {
PlayableTrackControls::InitMenuData *mpData{};
static SpectrogramSettingsHandler &Instance()
{
static SpectrogramSettingsHandler instance;
return instance;
}
void OnSpectrogramSettings(wxCommandEvent &);
void InitUserData(void *pUserData) override
{
mpData = static_cast< PlayableTrackControls::InitMenuData* >(pUserData);
}
void DestroyMenu() override
{
mpData = nullptr;
}
};
void SpectrogramSettingsHandler::OnSpectrogramSettings(wxCommandEvent &)
{
class ViewSettingsDialog final : public PrefsDialog
{
public:
ViewSettingsDialog(wxWindow *parent, AudacityProject &project,
const TranslatableString &title, PrefsDialog::Factories &factories,
int page)
: PrefsDialog(parent, &project, title, factories)
, mPage(page)
{
}
long GetPreferredPage() override
{
return mPage;
}
void SavePreferredPage() override
{
}
private:
const int mPage;
};
auto gAudioIO = AudioIOBase::Get();
if (gAudioIO->IsBusy()){
AudacityMessageBox(
XO(
"To change Spectrogram Settings, stop any\n playing or recording first."),
XO("Stop the Audio First"),
wxOK | wxICON_EXCLAMATION | wxCENTRE);
return;
}
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
PrefsDialog::Factories factories;
// factories.push_back(WaveformPrefsFactory( pTrack ));
factories.push_back(SpectrumPrefsFactory( pTrack ));
const int page =
// (pTrack->GetDisplay() == WaveTrackViewConstants::Spectrum) ? 1 :
0;
auto title = XO("%s:").Format( pTrack->GetName() );
ViewSettingsDialog dialog(
mpData->pParent, mpData->project, title, factories, page);
if (0 != dialog.ShowModal()) {
// Redraw
AudacityProject *const project = &mpData->project;
ProjectHistory::Get( *project ).ModifyState(true);
//Bug 1725 Toolbar was left greyed out.
//This solution is overkill, but does fix the problem and is what the
//prefs dialog normally does.
MenuCreator::RebuildAllMenuBars();
mpData->result = RefreshCode::RefreshAll;
}
}
PopupMenuTable::AttachedItem sAttachment{
GetWaveTrackMenuTable(),
{ "SubViews/Extra" },
std::make_unique<PopupMenuSection>( "SpectrogramSettings",
// Conditionally add menu item for settings, if showing spectrum
PopupMenuTable::Computed< WaveTrackPopupMenuTable >(
[]( WaveTrackPopupMenuTable &table ) -> Registry::BaseItemPtr {
using Entry = PopupMenuTable::Entry;
static const int OnSpectrogramSettingsID =
GetWaveTrackMenuTable().ReserveId();
const auto pTrack = &table.FindWaveTrack();
const auto &view = WaveTrackView::Get( *pTrack );
const auto displays = view.GetDisplays();
bool hasSpectrum = (displays.end() != std::find(
displays.begin(), displays.end(),
WaveTrackSubView::Type{ WaveTrackViewConstants::Spectrum, {} }
) );
if( hasSpectrum )
// In future, we might move this 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.)
return std::make_unique<Entry>( "SpectrogramSettings",
Entry::Item,
OnSpectrogramSettingsID,
XO("S&pectrogram Settings..."),
(wxCommandEventFunction)
(&SpectrogramSettingsHandler::OnSpectrogramSettings),
SpectrogramSettingsHandler::Instance(),
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
// Bug 1253. Shouldn't open preferences if audio is busy.
// We can't change them on the fly yet anyway.
auto gAudioIO = AudioIOBase::Get();
menu.Enable(id, !gAudioIO->IsBusy());
} );
else
return nullptr;
} ) )
};
}

View File

@ -17,6 +17,7 @@ Paul Licameli split from TrackPanel.cpp
#include "WaveTrackSliderHandles.h"
#include "WaveTrackView.h"
#include "WaveTrackViewConstants.h"
#include "../../../../AudioIOBase.h"
#include "../../../../CellularPanel.h"
#include "../../../../Menus.h"
@ -33,9 +34,7 @@ Paul Licameli split from TrackPanel.cpp
#include "../../../../effects/RealtimeEffectManager.h"
#include "../../../../ondemand/ODManager.h"
#include "../../../../prefs/PrefsDialog.h"
#include "../../../../prefs/SpectrumPrefs.h"
#include "../../../../prefs/ThemePrefs.h"
#include "../../../../prefs/WaveformPrefs.h"
#include "../../../../widgets/AudacityMessageBox.h"
#include <wx/combobox.h>
@ -116,23 +115,20 @@ enum {
OnSetDisplayId, lastDisplayId = (OnSetDisplayId + reserveDisplays - 1),
OnSpectrogramSettingsID,
OnChannelLeftID,
OnChannelRightID,
OnChannelMonoID,
OnMergeStereoID,
OnInstrument1ID,
OnInstrument2ID,
OnInstrument3ID,
OnInstrument4ID,
OnSwapChannelsID,
OnSplitStereoID,
OnSplitStereoMonoID,
ChannelMenuID,
// Range of ids for registered items -- keep this last!
FirstAttachedItemId,
};
@ -156,94 +152,6 @@ PopupMenuTableEntry::InitFunction initFn( const ValueFinder &findValue )
};
}
//=============================================================================
// Table class for a sub-menu
struct WaveColorMenuTable : PopupMenuTable
{
WaveColorMenuTable()
: PopupMenuTable( "WaveColor", XO("&Wave Color") )
{}
DECLARE_POPUP_MENU(WaveColorMenuTable);
static WaveColorMenuTable &Instance();
void InitUserData(void *pUserData) override;
void DestroyMenu() override
{
mpData = NULL;
}
PlayableTrackControls::InitMenuData *mpData{};
static int IdOfWaveColor(int WaveColor);
void OnWaveColorChange(wxCommandEvent & event);
};
WaveColorMenuTable &WaveColorMenuTable::Instance()
{
static WaveColorMenuTable instance;
return instance;
}
void WaveColorMenuTable::InitUserData(void *pUserData)
{
mpData = static_cast<PlayableTrackControls::InitMenuData*>(pUserData);
}
const TranslatableString GetWaveColorStr(int colorIndex)
{
return XO("Instrument %i").Format( colorIndex+1 );
}
BEGIN_POPUP_MENU(WaveColorMenuTable)
static const auto fn = initFn< WaveColorMenuTable >(
[]( WaveTrack &track ){
return IdOfWaveColor( track.GetWaveColorIndex() );
}
);
AppendRadioItem( "Instrument1", OnInstrument1ID,
GetWaveColorStr(0), POPUP_MENU_FN( OnWaveColorChange ), fn );
AppendRadioItem( "Instrument2", OnInstrument2ID,
GetWaveColorStr(1), POPUP_MENU_FN( OnWaveColorChange ), fn );
AppendRadioItem( "Instrument3", OnInstrument3ID,
GetWaveColorStr(2), POPUP_MENU_FN( OnWaveColorChange ), fn );
AppendRadioItem( "Instrument4", OnInstrument4ID,
GetWaveColorStr(3), POPUP_MENU_FN( OnWaveColorChange ), fn );
END_POPUP_MENU()
/// Converts a WaveColor enumeration to a wxWidgets menu item Id.
int WaveColorMenuTable::IdOfWaveColor(int WaveColor)
{ return OnInstrument1ID + WaveColor;}
/// Handles the selection from the WaveColor submenu of the
/// track menu.
void WaveColorMenuTable::OnWaveColorChange(wxCommandEvent & event)
{
int id = event.GetId();
wxASSERT(id >= OnInstrument1ID && id <= OnInstrument4ID);
const auto pTrack = static_cast<WaveTrack*>(mpData->pTrack);
int newWaveColor = id - OnInstrument1ID;
AudacityProject *const project = &mpData->project;
for (auto channel : TrackList::Channels(pTrack))
channel->SetWaveColorIndex(newWaveColor);
ProjectHistory::Get( *project )
.PushState(XO("Changed '%s' to %s")
.Format( pTrack->GetName(), GetWaveColorStr(newWaveColor) ),
XO("WaveColor Change"));
using namespace RefreshCode;
mpData->result = RefreshAll | FixScrollbars;
}
//=============================================================================
// Table class for a sub-menu
@ -552,6 +460,8 @@ void RateMenuTable::OnRateOther(wxCommandEvent &)
mpData->result = RefreshAll | FixScrollbars;
}
static const auto MenuPathStart = wxT("WaveTrackMenu");
//=============================================================================
// Class defining common command handlers for mono and stereo tracks
struct WaveTrackMenuTable
@ -561,21 +471,22 @@ struct WaveTrackMenuTable
WaveTrackMenuTable()
: ComputedPopupMenuTable< WaveTrackMenuTable, WaveTrackPopupMenuTable >{
"WaveTrack" }
{}
MenuPathStart }
{
mNextId = FirstAttachedItemId;
}
void InitUserData(void *pUserData) override;
void DestroyMenu() override
{
mpData = nullptr;
//mpData = nullptr;
}
DECLARE_POPUP_MENU(WaveTrackMenuTable);
void OnMultiView(wxCommandEvent & event);
void OnSetDisplay(wxCommandEvent & event);
void OnSpectrogramSettings(wxCommandEvent & event);
void OnChannelChange(wxCommandEvent & event);
void OnMergeStereo(wxCommandEvent & event);
@ -694,54 +605,9 @@ BEGIN_POPUP_MENU(WaveTrackMenuTable)
} );
++id;
}
BeginSection( "Extra" );
EndSection();
EndSection();
// Conditionally add sub-menu for wave color, if showing waveform
Append( []( My &table ) -> Registry::BaseItemPtr {
const auto pTrack = &table.FindWaveTrack();
const auto &view = WaveTrackView::Get( *pTrack );
const auto displays = view.GetDisplays();
bool hasWaveform = (displays.end() != std::find(
displays.begin(), displays.end(),
WaveTrackSubView::Type{ WaveTrackViewConstants::Waveform, {} }
) );
if( hasWaveform )
return std::make_unique<PopupMenuSection>( "WaveColor",
Registry::Shared( WaveColorMenuTable::Instance()
.Get( table.mpData ) ) );
else
return nullptr;
} );
// Conditionally add sub-menu for spectrogram settings, if showing spectrum
Append( []( My &table ) -> Registry::BaseItemPtr {
const auto pTrack = &table.FindWaveTrack();
const auto &view = WaveTrackView::Get( *pTrack );
const auto displays = view.GetDisplays();
bool hasSpectrum = (displays.end() != std::find(
displays.begin(), displays.end(),
WaveTrackSubView::Type{ WaveTrackViewConstants::Spectrum, {} }
) );
if( hasSpectrum )
// In future, we might move this 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.)
return std::make_unique<PopupMenuSection>( "SpectrogramSettings",
std::make_unique<Entry>( "SpectrogramSettings", Entry::Item,
OnSpectrogramSettingsID,
XO("S&pectrogram Settings..."),
POPUP_MENU_FN( OnSpectrogramSettings ), table,
[]( PopupMenuHandler &handler, wxMenu &menu, int id ){
// Bug 1253. Shouldn't open preferences if audio is busy.
// We can't change them on the fly yet anyway.
auto gAudioIO = AudioIOBase::Get();
menu.Enable(id, !gAudioIO->IsBusy());
} ) );
else
return nullptr;
} );
BeginSection( "Channels" );
// If these are enabled again, choose a hot key for Mono that does not conflict
@ -885,67 +751,6 @@ void WaveTrackMenuTable::OnSetDisplay(wxCommandEvent & event)
}
}
void WaveTrackMenuTable::OnSpectrogramSettings(wxCommandEvent &)
{
class ViewSettingsDialog final : public PrefsDialog
{
public:
ViewSettingsDialog(wxWindow *parent, AudacityProject &project,
const TranslatableString &title, PrefsDialog::Factories &factories,
int page)
: PrefsDialog(parent, &project, title, factories)
, mPage(page)
{
}
long GetPreferredPage() override
{
return mPage;
}
void SavePreferredPage() override
{
}
private:
const int mPage;
};
auto gAudioIO = AudioIOBase::Get();
if (gAudioIO->IsBusy()){
AudacityMessageBox(
XO(
"To change Spectrogram Settings, stop any\n playing or recording first."),
XO("Stop the Audio First"),
wxOK | wxICON_EXCLAMATION | wxCENTRE);
return;
}
WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
PrefsDialog::Factories factories;
// factories.push_back(WaveformPrefsFactory( pTrack ));
factories.push_back(SpectrumPrefsFactory( pTrack ));
const int page =
// (pTrack->GetDisplay() == WaveTrackViewConstants::Spectrum) ? 1 :
0;
auto title = XO("%s:").Format( pTrack->GetName() );
ViewSettingsDialog dialog(
mpData->pParent, mpData->project, title, factories, page);
if (0 != dialog.ShowModal()) {
// Redraw
AudacityProject *const project = &mpData->project;
ProjectHistory::Get( *project ).ModifyState(true);
//Bug 1725 Toolbar was left greyed out.
//This solution is overkill, but does fix the problem and is what the
//prefs dialog normally does.
MenuCreator::RebuildAllMenuBars();
mpData->result = RefreshCode::RefreshAll;
}
}
#if 0
void WaveTrackMenuTable::OnChannelChange(wxCommandEvent & event)
{
@ -1143,11 +948,22 @@ void WaveTrackMenuTable::OnSplitStereoMono(wxCommandEvent &)
//=============================================================================
PopupMenuTable *WaveTrackControls::GetMenuExtension(Track * pTrack)
{
static Registry::OrderingPreferenceInitializer init{
MenuPathStart,
{
{wxT("/SubViews/Extra"), wxT("WaveColor,SpectrogramSettings")},
}
};
WaveTrackMenuTable & result = WaveTrackMenuTable::Instance();
return &result;
}
WaveTrackPopupMenuTable &GetWaveTrackMenuTable()
{
return WaveTrackMenuTable::Instance();
}
// drawing related
#include "../../../../widgets/ASlider.h"
#include "../../../../TrackInfo.h"

View File

@ -75,6 +75,12 @@ struct WaveTrackPopupMenuTable : public PopupMenuTable
using PopupMenuTable::PopupMenuTable;
PlayableTrackControls::InitMenuData *mpData{};
WaveTrack &FindWaveTrack () const;
int ReserveId() { return mNextId++; }
protected:
int mNextId = 0;
};
// Expose the wave track menu table to registration of menu items
WaveTrackPopupMenuTable &GetWaveTrackMenuTable();
#endif

View File

@ -1039,3 +1039,143 @@ static const WaveTrackSubViews::RegisteredFactory key{
return std::make_shared< WaveformView >( view );
}
};
// The following attaches the wave color sub-menu to the wave track popup
// menu. It is appropriate only to waveform view and so is kept in this
// source file with the rest of the waveform view implementation.
#include <mutex> // for std::call_once
#include "WaveTrackControls.h"
#include "../../../../widgets/PopupMenuTable.h"
#include "../../../../ProjectAudioIO.h"
#include "../../../../ProjectHistory.h"
#include "../../../../RefreshCode.h"
//=============================================================================
// Table class for a sub-menu
struct WaveColorMenuTable : PopupMenuTable
{
WaveColorMenuTable() : PopupMenuTable{ "WaveColor", XO("&Wave Color") } {}
DECLARE_POPUP_MENU(WaveColorMenuTable);
static WaveColorMenuTable &Instance();
void InitUserData(void *pUserData) override;
void DestroyMenu() override
{
mpData = NULL;
}
PlayableTrackControls::InitMenuData *mpData{};
int IdOfWaveColor(int WaveColor);
void OnWaveColorChange(wxCommandEvent & event);
int OnInstrument1ID, OnInstrument2ID, OnInstrument3ID, OnInstrument4ID;
};
WaveColorMenuTable &WaveColorMenuTable::Instance()
{
static WaveColorMenuTable instance;
return instance;
}
void WaveColorMenuTable::InitUserData(void *pUserData)
{
mpData = static_cast<PlayableTrackControls::InitMenuData*>(pUserData);
}
namespace {
using ValueFinder = std::function< int( WaveTrack& ) >;
const TranslatableString GetWaveColorStr(int colorIndex)
{
return XO("Instrument %i").Format( colorIndex+1 );
}
}
BEGIN_POPUP_MENU(WaveColorMenuTable)
static const auto fn = []( PopupMenuHandler &handler, wxMenu &menu, int id ){
auto &me = static_cast<WaveColorMenuTable&>( handler );
auto pData = me.mpData;
const auto &track = *static_cast<WaveTrack*>(pData->pTrack);
auto &project = pData->project;
bool unsafe = ProjectAudioIO::Get( project ).IsAudioActive();
menu.Check( id, id == me.IdOfWaveColor( track.GetWaveColorIndex() ) );
menu.Enable( id, !unsafe );
};
static std::once_flag flag;
std::call_once( flag, [this]{
auto &hostTable = GetWaveTrackMenuTable();
OnInstrument1ID = hostTable.ReserveId();
OnInstrument2ID = hostTable.ReserveId();
OnInstrument3ID = hostTable.ReserveId();
OnInstrument4ID = hostTable.ReserveId();
} );
AppendRadioItem( "Instrument1", OnInstrument1ID,
GetWaveColorStr(0), POPUP_MENU_FN( OnWaveColorChange ), fn );
AppendRadioItem( "Instrument2", OnInstrument2ID,
GetWaveColorStr(1), POPUP_MENU_FN( OnWaveColorChange ), fn );
AppendRadioItem( "Instrument3", OnInstrument3ID,
GetWaveColorStr(2), POPUP_MENU_FN( OnWaveColorChange ), fn );
AppendRadioItem( "Instrument4", OnInstrument4ID,
GetWaveColorStr(3), POPUP_MENU_FN( OnWaveColorChange ), fn );
END_POPUP_MENU()
/// Converts a WaveColor enumeration to a wxWidgets menu item Id.
int WaveColorMenuTable::IdOfWaveColor(int WaveColor)
{ return OnInstrument1ID + WaveColor;}
/// Handles the selection from the WaveColor submenu of the
/// track menu.
void WaveColorMenuTable::OnWaveColorChange(wxCommandEvent & event)
{
int id = event.GetId();
wxASSERT(id >= OnInstrument1ID && id <= OnInstrument4ID);
const auto pTrack = static_cast<WaveTrack*>(mpData->pTrack);
int newWaveColor = id - OnInstrument1ID;
AudacityProject *const project = &mpData->project;
for (auto channel : TrackList::Channels(pTrack))
channel->SetWaveColorIndex(newWaveColor);
ProjectHistory::Get( *project )
.PushState(XO("Changed '%s' to %s")
.Format( pTrack->GetName(), GetWaveColorStr(newWaveColor) ),
XO("WaveColor Change"));
using namespace RefreshCode;
mpData->result = RefreshAll | FixScrollbars;
}
namespace {
PopupMenuTable::AttachedItem sAttachment{
GetWaveTrackMenuTable(),
{ "SubViews/Extra" },
std::make_unique<PopupMenuSection>( "WaveColor",
// Conditionally add sub-menu for wave color, if showing waveform
PopupMenuTable::Computed< WaveTrackPopupMenuTable >(
[]( WaveTrackPopupMenuTable &table ) -> Registry::BaseItemPtr {
const auto pTrack = &table.FindWaveTrack();
const auto &view = WaveTrackView::Get( *pTrack );
const auto displays = view.GetDisplays();
bool hasWaveform = (displays.end() != std::find(
displays.begin(), displays.end(),
WaveTrackSubView::Type{ WaveTrackViewConstants::Waveform, {} }
) );
if( hasWaveform )
return Registry::Shared( WaveColorMenuTable::Instance()
.Get( table.mpData ) );
else
return nullptr;
} ) )
};
}