838 lines
25 KiB
C++
838 lines
25 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
Menus.cpp
|
|
|
|
Dominic Mazzoni
|
|
Brian Gunlogson
|
|
et al.
|
|
|
|
*******************************************************************//**
|
|
|
|
\file Menus.cpp
|
|
\brief Functions for building toobar menus and enabling and disabling items
|
|
|
|
*//****************************************************************//**
|
|
|
|
\class MenuCreator
|
|
\brief MenuCreator is responsible for creating the main menu bar.
|
|
|
|
*//****************************************************************//**
|
|
|
|
\class MenuManager
|
|
\brief MenuManager handles updates to menu state.
|
|
|
|
*//*******************************************************************/
|
|
|
|
#include "Audacity.h" // for USE_* macros
|
|
#include "Menus.h"
|
|
|
|
#include "Experimental.h"
|
|
|
|
#include "AdornedRulerPanel.h"
|
|
#include "AudioIO.h"
|
|
#include "Clipboard.h"
|
|
#include "LabelTrack.h"
|
|
#include "ModuleManager.h"
|
|
#ifdef USE_MIDI
|
|
#include "NoteTrack.h"
|
|
#endif // USE_MIDI
|
|
#include "Prefs.h"
|
|
#include "Project.h"
|
|
#include "ProjectSettings.h"
|
|
#include "TrackPanel.h"
|
|
#include "UndoManager.h"
|
|
#include "ViewInfo.h"
|
|
#include "commands/CommandManager.h"
|
|
#include "effects/EffectManager.h"
|
|
#include "prefs/TracksPrefs.h"
|
|
#include "toolbars/ControlToolBar.h"
|
|
#include "toolbars/ToolManager.h"
|
|
#include "widgets/FileHistory.h"
|
|
|
|
#include <wx/menu.h>
|
|
#include <wx/windowptr.h>
|
|
|
|
MenuCreator::MenuCreator()
|
|
{
|
|
}
|
|
|
|
MenuCreator::~MenuCreator()
|
|
{
|
|
}
|
|
|
|
static const AudacityProject::AttachedObjects::RegisteredFactory key{
|
|
[]( AudacityProject&){
|
|
return std::make_shared< MenuManager >(); }
|
|
};
|
|
|
|
MenuManager &MenuManager::Get( AudacityProject &project )
|
|
{
|
|
return project.AttachedObjects::Get< MenuManager >( key );
|
|
}
|
|
|
|
const MenuManager &MenuManager::Get( const AudacityProject &project )
|
|
{
|
|
return Get( const_cast< AudacityProject & >( project ) );
|
|
}
|
|
|
|
MenuManager::MenuManager()
|
|
{
|
|
UpdatePrefs();
|
|
}
|
|
|
|
void MenuManager::UpdatePrefs()
|
|
{
|
|
bool bSelectAllIfNone;
|
|
gPrefs->Read(wxT("/GUI/SelectAllOnNone"), &bSelectAllIfNone, false);
|
|
// 0 is grey out, 1 is Autoselect, 2 is Give warnings.
|
|
#ifdef EXPERIMENTAL_DA
|
|
// DA warns or greys out.
|
|
mWhatIfNoSelection = bSelectAllIfNone ? 2 : 0;
|
|
#else
|
|
// Audacity autoselects or warns.
|
|
mWhatIfNoSelection = bSelectAllIfNone ? 1 : 2;
|
|
#endif
|
|
mStopIfWasPaused = true; // not configurable for now, but could be later.
|
|
}
|
|
|
|
/// Namespace for structures that go into building a menu
|
|
namespace MenuTable {
|
|
|
|
BaseItem::~BaseItem() {}
|
|
|
|
ComputedItem::~ComputedItem() {}
|
|
|
|
GroupItem::GroupItem( BaseItemPtrs &&items_ )
|
|
: items{ std::move( items_ ) }
|
|
{
|
|
}
|
|
void GroupItem::AppendOne( BaseItemPtr&& ptr )
|
|
{
|
|
items.push_back( std::move( ptr ) );
|
|
}
|
|
GroupItem::~GroupItem() {}
|
|
|
|
MenuItem::MenuItem( const wxString &title_, BaseItemPtrs &&items_ )
|
|
: GroupItem{ std::move( items_ ) }, title{ title_ }
|
|
{
|
|
wxASSERT( !title.empty() );
|
|
}
|
|
MenuItem::~MenuItem() {}
|
|
|
|
ConditionalGroupItem::ConditionalGroupItem(
|
|
Condition condition_, BaseItemPtrs &&items_ )
|
|
: GroupItem{ std::move( items_ ) }, condition{ condition_ }
|
|
{
|
|
}
|
|
ConditionalGroupItem::~ConditionalGroupItem() {}
|
|
|
|
SeparatorItem::~SeparatorItem() {}
|
|
|
|
CommandItem::CommandItem(const CommandID &name_,
|
|
const wxString &label_in_,
|
|
bool hasDialog_,
|
|
CommandHandlerFinder finder_,
|
|
CommandFunctorPointer callback_,
|
|
CommandFlag flags_,
|
|
const CommandManager::Options &options_)
|
|
: name{ name_ }, label_in{ label_in_ }, hasDialog{ hasDialog_ }
|
|
, finder{ finder_ }, callback{ callback_ }
|
|
, flags{ flags_ }, options{ options_ }
|
|
{}
|
|
CommandItem::~CommandItem() {}
|
|
|
|
CommandGroupItem::CommandGroupItem(const wxString &name_,
|
|
std::initializer_list< ComponentInterfaceSymbol > items_,
|
|
CommandHandlerFinder finder_,
|
|
CommandFunctorPointer callback_,
|
|
CommandFlag flags_,
|
|
bool isEffect_)
|
|
: name{ name_ }, items{ items_ }
|
|
, finder{ finder_ }, callback{ callback_ }
|
|
, flags{ flags_ }, isEffect{ isEffect_ }
|
|
{}
|
|
CommandGroupItem::~CommandGroupItem() {}
|
|
|
|
SpecialItem::~SpecialItem() {}
|
|
|
|
}
|
|
|
|
namespace {
|
|
|
|
void VisitItem( AudacityProject &project, MenuTable::BaseItem *pItem );
|
|
|
|
void VisitItems(
|
|
AudacityProject &project, const MenuTable::BaseItemPtrs &items )
|
|
{
|
|
for ( auto &pSubItem : items )
|
|
VisitItem( project, pSubItem.get() );
|
|
}
|
|
|
|
void VisitItem( AudacityProject &project, MenuTable::BaseItem *pItem )
|
|
{
|
|
if (!pItem)
|
|
return;
|
|
|
|
auto &manager = CommandManager::Get( project );
|
|
|
|
using namespace MenuTable;
|
|
if (const auto pComputed =
|
|
dynamic_cast<ComputedItem*>( pItem )) {
|
|
// TODO maybe? memo-ize the results of the function, but that requires
|
|
// invalidating the memo at the right times
|
|
auto result = pComputed->factory( project );
|
|
if (result)
|
|
// recursion
|
|
VisitItem( project, result.get() );
|
|
}
|
|
else
|
|
if (const auto pCommand =
|
|
dynamic_cast<CommandItem*>( pItem )) {
|
|
manager.AddItem(
|
|
pCommand->name, pCommand->label_in, pCommand->hasDialog,
|
|
pCommand->finder, pCommand->callback,
|
|
pCommand->flags, pCommand->options
|
|
);
|
|
}
|
|
else
|
|
if (const auto pCommandList =
|
|
dynamic_cast<CommandGroupItem*>( pItem ) ) {
|
|
manager.AddItemList(pCommandList->name,
|
|
pCommandList->items.data(), pCommandList->items.size(),
|
|
pCommandList->finder, pCommandList->callback,
|
|
pCommandList->flags, pCommandList->isEffect);
|
|
}
|
|
else
|
|
if (const auto pMenu =
|
|
dynamic_cast<MenuItem*>( pItem )) {
|
|
manager.BeginMenu( pMenu->title );
|
|
// recursion
|
|
VisitItems( project, pMenu->items );
|
|
manager.EndMenu();
|
|
}
|
|
else
|
|
if (const auto pConditionalGroup =
|
|
dynamic_cast<ConditionalGroupItem*>( pItem )) {
|
|
const auto flag = pConditionalGroup->condition();
|
|
if (!flag)
|
|
manager.BeginOccultCommands();
|
|
// recursion
|
|
VisitItems( project, pConditionalGroup->items );
|
|
if (!flag)
|
|
manager.EndOccultCommands();
|
|
}
|
|
else
|
|
if (const auto pGroup =
|
|
dynamic_cast<GroupItem*>( pItem )) {
|
|
// recursion
|
|
VisitItems( project, pGroup->items );
|
|
}
|
|
else
|
|
if (dynamic_cast<SeparatorItem*>( pItem )) {
|
|
manager.AddSeparator();
|
|
}
|
|
else
|
|
if (const auto pSpecial =
|
|
dynamic_cast<SpecialItem*>( pItem )) {
|
|
const auto pCurrentMenu = manager.CurrentMenu();
|
|
wxASSERT( pCurrentMenu );
|
|
pSpecial->fn( project, *pCurrentMenu );
|
|
}
|
|
else
|
|
wxASSERT( false );
|
|
}
|
|
|
|
}
|
|
|
|
/// CreateMenusAndCommands builds the menus, and also rebuilds them after
|
|
/// changes in configured preferences - for example changes in key-bindings
|
|
/// affect the short-cut key legend that appears beside each command,
|
|
|
|
MenuTable::BaseItemPtr FileMenu( AudacityProject& );
|
|
|
|
MenuTable::BaseItemPtr EditMenu( AudacityProject& );
|
|
|
|
MenuTable::BaseItemPtr SelectMenu( AudacityProject& );
|
|
|
|
MenuTable::BaseItemPtr ViewMenu( AudacityProject& );
|
|
|
|
MenuTable::BaseItemPtr TransportMenu( AudacityProject& );
|
|
|
|
MenuTable::BaseItemPtr TracksMenu( AudacityProject& );
|
|
|
|
MenuTable::BaseItemPtr GenerateMenu( AudacityProject& );
|
|
MenuTable::BaseItemPtr EffectMenu( AudacityProject& );
|
|
MenuTable::BaseItemPtr AnalyzeMenu( AudacityProject& );
|
|
MenuTable::BaseItemPtr ToolsMenu( AudacityProject& );
|
|
|
|
MenuTable::BaseItemPtr WindowMenu( AudacityProject& );
|
|
|
|
MenuTable::BaseItemPtr ExtraMenu( AudacityProject& );
|
|
|
|
MenuTable::BaseItemPtr HelpMenu( AudacityProject& );
|
|
|
|
// Table of menu factories.
|
|
// TODO: devise a registration system instead.
|
|
static const auto menuTree = MenuTable::Items(
|
|
FileMenu
|
|
, EditMenu
|
|
, SelectMenu
|
|
, ViewMenu
|
|
, TransportMenu
|
|
, TracksMenu
|
|
, GenerateMenu
|
|
, EffectMenu
|
|
, AnalyzeMenu
|
|
, ToolsMenu
|
|
, WindowMenu
|
|
, ExtraMenu
|
|
, HelpMenu
|
|
);
|
|
|
|
void MenuCreator::CreateMenusAndCommands(AudacityProject &project)
|
|
{
|
|
auto &commandManager = CommandManager::Get( project );
|
|
|
|
// The list of defaults to exclude depends on
|
|
// preference wxT("/GUI/Shortcuts/FullDefaults"), which may have changed.
|
|
commandManager.SetMaxList();
|
|
|
|
auto menubar = commandManager.AddMenuBar(wxT("appmenu"));
|
|
wxASSERT(menubar);
|
|
|
|
VisitItem( project, menuTree.get() );
|
|
|
|
GetProjectFrame( project ).SetMenuBar(menubar.release());
|
|
|
|
mLastFlags = AlwaysEnabledFlag;
|
|
|
|
#if defined(__WXDEBUG__)
|
|
// c->CheckDups();
|
|
#endif
|
|
}
|
|
|
|
// TODO: This surely belongs in CommandManager?
|
|
void MenuManager::ModifyUndoMenuItems(AudacityProject &project)
|
|
{
|
|
wxString desc;
|
|
auto &undoManager = UndoManager::Get( project );
|
|
auto &commandManager = CommandManager::Get( project );
|
|
int cur = undoManager.GetCurrentState();
|
|
|
|
if (undoManager.UndoAvailable()) {
|
|
undoManager.GetShortDescription(cur, &desc);
|
|
commandManager.Modify(wxT("Undo"),
|
|
wxString::Format(_("&Undo %s"),
|
|
desc));
|
|
commandManager.Enable(wxT("Undo"), project.UndoAvailable());
|
|
}
|
|
else {
|
|
commandManager.Modify(wxT("Undo"),
|
|
_("&Undo"));
|
|
}
|
|
|
|
if (undoManager.RedoAvailable()) {
|
|
undoManager.GetShortDescription(cur+1, &desc);
|
|
commandManager.Modify(wxT("Redo"),
|
|
wxString::Format(_("&Redo %s"),
|
|
desc));
|
|
commandManager.Enable(wxT("Redo"), project.RedoAvailable());
|
|
}
|
|
else {
|
|
commandManager.Modify(wxT("Redo"),
|
|
_("&Redo"));
|
|
commandManager.Enable(wxT("Redo"), false);
|
|
}
|
|
}
|
|
|
|
void MenuCreator::RebuildMenuBar(AudacityProject &project)
|
|
{
|
|
// On OSX, we can't rebuild the menus while a modal dialog is being shown
|
|
// since the enabled state for menus like Quit and Preference gets out of
|
|
// sync with wxWidgets idea of what it should be.
|
|
#if defined(__WXMAC__) && defined(__WXDEBUG__)
|
|
{
|
|
wxDialog *dlg =
|
|
wxDynamicCast(wxGetTopLevelParent(wxWindow::FindFocus()), wxDialog);
|
|
wxASSERT((!dlg || !dlg->IsModal()));
|
|
}
|
|
#endif
|
|
|
|
// Delete the menus, since we will soon recreate them.
|
|
// Rather oddly, the menus don't vanish as a result of doing this.
|
|
{
|
|
auto &window = ProjectWindow::Get( project );
|
|
wxWindowPtr<wxMenuBar> menuBar{ window.GetMenuBar() };
|
|
window.DetachMenuBar();
|
|
// menuBar gets deleted here
|
|
}
|
|
|
|
CommandManager::Get( project ).PurgeData();
|
|
|
|
CreateMenusAndCommands(project);
|
|
|
|
ModuleManager::Get().Dispatch(MenusRebuilt);
|
|
}
|
|
|
|
CommandFlag MenuManager::GetFocusedFrame(AudacityProject &project)
|
|
{
|
|
wxWindow *w = wxWindow::FindFocus();
|
|
|
|
while (w) {
|
|
if (w == ToolManager::Get( project ).GetTopDock()) {
|
|
return TopDockHasFocus;
|
|
}
|
|
|
|
if (w == &AdornedRulerPanel::Get( project ))
|
|
return RulerHasFocus;
|
|
|
|
if (dynamic_cast<NonKeystrokeInterceptingWindow*>(w)) {
|
|
return TrackPanelHasFocus;
|
|
}
|
|
if (w == ToolManager::Get( project ).GetBotDock()) {
|
|
return BotDockHasFocus;
|
|
}
|
|
|
|
w = w->GetParent();
|
|
}
|
|
|
|
return AlwaysEnabledFlag;
|
|
}
|
|
|
|
|
|
CommandFlag MenuManager::GetUpdateFlags
|
|
(AudacityProject &project, bool checkActive)
|
|
{
|
|
// This method determines all of the flags that determine whether
|
|
// certain menu items and commands should be enabled or disabled,
|
|
// and returns them in a bitfield. Note that if none of the flags
|
|
// have changed, it's not necessary to even check for updates.
|
|
auto flags = AlwaysEnabledFlag;
|
|
// static variable, used to remember flags for next time.
|
|
static auto lastFlags = flags;
|
|
|
|
// if (auto focus = wxWindow::FindFocus()) {
|
|
auto &window = GetProjectFrame( project );
|
|
if (wxWindow * focus = &window) {
|
|
while (focus && focus->GetParent())
|
|
focus = focus->GetParent();
|
|
if (focus && !static_cast<wxTopLevelWindow*>(focus)->IsIconized())
|
|
flags |= NotMinimizedFlag;
|
|
}
|
|
|
|
// These flags are cheap to calculate.
|
|
if (!gAudioIO->IsAudioTokenActive(project.GetAudioIOToken()))
|
|
flags |= AudioIONotBusyFlag;
|
|
else
|
|
flags |= AudioIOBusyFlag;
|
|
|
|
if( gAudioIO->IsPaused() )
|
|
flags |= PausedFlag;
|
|
else
|
|
flags |= NotPausedFlag;
|
|
|
|
// quick 'short-circuit' return.
|
|
if ( checkActive && !window.IsActive() ){
|
|
const auto checkedFlags =
|
|
NotMinimizedFlag | AudioIONotBusyFlag | AudioIOBusyFlag |
|
|
PausedFlag | NotPausedFlag;
|
|
// short cirucit return should preserve flags that have not been calculated.
|
|
flags = (lastFlags & ~checkedFlags) | flags;
|
|
lastFlags = flags;
|
|
return flags;
|
|
}
|
|
|
|
auto &viewInfo = ViewInfo::Get( project );
|
|
const auto &selectedRegion = viewInfo.selectedRegion;
|
|
|
|
if (!selectedRegion.isPoint())
|
|
flags |= TimeSelectedFlag;
|
|
|
|
auto &tracks = TrackList::Get( project );
|
|
auto trackRange = tracks.Any();
|
|
if ( trackRange )
|
|
flags |= TracksExistFlag;
|
|
trackRange.Visit(
|
|
[&](LabelTrack *lt) {
|
|
flags |= LabelTracksExistFlag;
|
|
|
|
if (lt->GetSelected()) {
|
|
flags |= TracksSelectedFlag;
|
|
for (int i = 0; i < lt->GetNumLabels(); i++) {
|
|
const LabelStruct *ls = lt->GetLabel(i);
|
|
if (ls->getT0() >= selectedRegion.t0() &&
|
|
ls->getT1() <= selectedRegion.t1()) {
|
|
flags |= LabelsSelectedFlag;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lt->IsTextSelected()) {
|
|
flags |= CutCopyAvailableFlag;
|
|
}
|
|
},
|
|
[&](WaveTrack *t) {
|
|
flags |= WaveTracksExistFlag;
|
|
flags |= PlayableTracksExistFlag;
|
|
if (t->GetSelected()) {
|
|
flags |= TracksSelectedFlag;
|
|
// TODO: more-than-two-channels
|
|
if (TrackList::Channels(t).size() > 1) {
|
|
flags |= StereoRequiredFlag;
|
|
}
|
|
flags |= WaveTracksSelectedFlag;
|
|
flags |= AudioTracksSelectedFlag;
|
|
}
|
|
if( t->GetEndTime() > t->GetStartTime() )
|
|
flags |= HasWaveDataFlag;
|
|
}
|
|
#if defined(USE_MIDI)
|
|
,
|
|
[&](NoteTrack *nt) {
|
|
flags |= NoteTracksExistFlag;
|
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
|
flags |= PlayableTracksExistFlag;
|
|
#endif
|
|
|
|
if (nt->GetSelected()) {
|
|
flags |= TracksSelectedFlag;
|
|
flags |= NoteTracksSelectedFlag;
|
|
flags |= AudioTracksSelectedFlag; // even if not EXPERIMENTAL_MIDI_OUT
|
|
}
|
|
}
|
|
#endif
|
|
);
|
|
|
|
if( Clipboard::Get().Duration() > 0 )
|
|
flags |= ClipboardFlag;
|
|
|
|
auto &undoManager = UndoManager::Get( project );
|
|
|
|
if (undoManager.UnsavedChanges() || !project.IsProjectSaved())
|
|
flags |= UnsavedChangesFlag;
|
|
|
|
if (!mLastEffect.empty())
|
|
flags |= HasLastEffectFlag;
|
|
|
|
if (project.UndoAvailable())
|
|
flags |= UndoAvailableFlag;
|
|
|
|
if (project.RedoAvailable())
|
|
flags |= RedoAvailableFlag;
|
|
|
|
if (ViewInfo::Get( project ).ZoomInAvailable() && (flags & TracksExistFlag))
|
|
flags |= ZoomInAvailableFlag;
|
|
|
|
if (ViewInfo::Get( project ).ZoomOutAvailable() && (flags & TracksExistFlag))
|
|
flags |= ZoomOutAvailableFlag;
|
|
|
|
// TextClipFlag is currently unused (Jan 2017, 2.1.3 alpha)
|
|
// and LabelTrack::IsTextClipSupported() is quite slow on Linux,
|
|
// so disable for now (See bug 1575).
|
|
// if ((flags & LabelTracksExistFlag) && LabelTrack::IsTextClipSupported())
|
|
// flags |= TextClipFlag;
|
|
|
|
flags |= GetFocusedFrame(project);
|
|
|
|
double start, end;
|
|
project.GetPlayRegion(&start, &end);
|
|
if (project.IsPlayRegionLocked())
|
|
flags |= PlayRegionLockedFlag;
|
|
else if (start != end)
|
|
flags |= PlayRegionNotLockedFlag;
|
|
|
|
if (flags & AudioIONotBusyFlag) {
|
|
if (flags & TimeSelectedFlag) {
|
|
if (flags & TracksSelectedFlag) {
|
|
flags |= CutCopyAvailableFlag;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FileHistory::Global().GetCount() > 0)
|
|
flags |= HaveRecentFiles;
|
|
|
|
const auto &settings = ProjectSettings::Get( project );
|
|
if (settings.IsSyncLocked())
|
|
flags |= IsSyncLockedFlag;
|
|
else
|
|
flags |= IsNotSyncLockedFlag;
|
|
|
|
if (!EffectManager::Get().RealtimeIsActive())
|
|
flags |= IsRealtimeNotActiveFlag;
|
|
|
|
if ( !( gAudioIO->IsBusy() && gAudioIO->GetNumCaptureChannels() > 0 ) )
|
|
flags |= CaptureNotBusyFlag;
|
|
|
|
auto &bar = ControlToolBar::Get( project );
|
|
if (bar.ControlToolBar::CanStopAudioStream())
|
|
flags |= CanStopAudioStreamFlag;
|
|
|
|
lastFlags = flags;
|
|
return flags;
|
|
}
|
|
|
|
void MenuManager::ModifyAllProjectToolbarMenus()
|
|
{
|
|
for (auto pProject : AllProjects{}) {
|
|
auto &project = *pProject;
|
|
MenuManager::Get(project).ModifyToolbarMenus(project);
|
|
}
|
|
}
|
|
|
|
void MenuManager::ModifyToolbarMenus(AudacityProject &project)
|
|
{
|
|
// Refreshes can occur during shutdown and the toolmanager may already
|
|
// be deleted, so protect against it.
|
|
auto &toolManager = ToolManager::Get( project );
|
|
|
|
auto &commandManager = CommandManager::Get( project );
|
|
|
|
auto &settings = ProjectSettings::Get( project );
|
|
|
|
commandManager.Check(wxT("ShowScrubbingTB"),
|
|
toolManager.IsVisible(ScrubbingBarID));
|
|
commandManager.Check(wxT("ShowDeviceTB"),
|
|
toolManager.IsVisible(DeviceBarID));
|
|
commandManager.Check(wxT("ShowEditTB"),
|
|
toolManager.IsVisible(EditBarID));
|
|
commandManager.Check(wxT("ShowMeterTB"),
|
|
toolManager.IsVisible(MeterBarID));
|
|
commandManager.Check(wxT("ShowRecordMeterTB"),
|
|
toolManager.IsVisible(RecordMeterBarID));
|
|
commandManager.Check(wxT("ShowPlayMeterTB"),
|
|
toolManager.IsVisible(PlayMeterBarID));
|
|
commandManager.Check(wxT("ShowMixerTB"),
|
|
toolManager.IsVisible(MixerBarID));
|
|
commandManager.Check(wxT("ShowSelectionTB"),
|
|
toolManager.IsVisible(SelectionBarID));
|
|
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
|
commandManager.Check(wxT("ShowSpectralSelectionTB"),
|
|
toolManager.IsVisible(SpectralSelectionBarID));
|
|
#endif
|
|
commandManager.Check(wxT("ShowToolsTB"),
|
|
toolManager.IsVisible(ToolsBarID));
|
|
commandManager.Check(wxT("ShowTranscriptionTB"),
|
|
toolManager.IsVisible(TranscriptionBarID));
|
|
commandManager.Check(wxT("ShowTransportTB"),
|
|
toolManager.IsVisible(TransportBarID));
|
|
|
|
// Now, go through each toolbar, and call EnableDisableButtons()
|
|
for (int i = 0; i < ToolBarCount; i++) {
|
|
auto bar = toolManager.GetToolBar(i);
|
|
if (bar)
|
|
bar->EnableDisableButtons();
|
|
}
|
|
|
|
// These don't really belong here, but it's easier and especially so for
|
|
// the Edit toolbar and the sync-lock menu item.
|
|
bool active;
|
|
gPrefs->Read(wxT("/AudioIO/SoundActivatedRecord"),&active, false);
|
|
commandManager.Check(wxT("SoundActivation"), active);
|
|
#ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
|
|
gPrefs->Read(wxT("/AudioIO/AutomatedInputLevelAdjustment"),&active, false);
|
|
commandManager.Check(wxT("AutomatedInputLevelAdjustmentOnOff"), active);
|
|
#endif
|
|
|
|
active = TracksPrefs::GetPinnedHeadPreference();
|
|
commandManager.Check(wxT("PinnedHead"), active);
|
|
|
|
#ifdef EXPERIMENTAL_DA
|
|
gPrefs->Read(wxT("/AudioIO/Duplex"),&active, false);
|
|
#else
|
|
gPrefs->Read(wxT("/AudioIO/Duplex"),&active, true);
|
|
#endif
|
|
commandManager.Check(wxT("Overdub"), active);
|
|
gPrefs->Read(wxT("/AudioIO/SWPlaythrough"),&active, false);
|
|
commandManager.Check(wxT("SWPlaythrough"), active);
|
|
|
|
gPrefs->Read(wxT("/GUI/SyncLockTracks"), &active, false);
|
|
settings.SetSyncLock(active);
|
|
|
|
commandManager.Check(wxT("SyncLock"), active);
|
|
gPrefs->Read(wxT("/GUI/TypeToCreateLabel"),&active, false);
|
|
commandManager.Check(wxT("TypeToCreateLabel"), active);
|
|
}
|
|
|
|
// checkActive is a temporary hack that should be removed as soon as we
|
|
// get multiple effect preview working
|
|
void MenuManager::UpdateMenus(AudacityProject &project, bool checkActive)
|
|
{
|
|
//ANSWER-ME: Why UpdateMenus only does active project?
|
|
//JKC: Is this test fixing a bug when multiple projects are open?
|
|
//so that menu states work even when different in different projects?
|
|
if (&project != GetActiveProject())
|
|
return;
|
|
|
|
auto flags = MenuManager::Get(project).GetUpdateFlags(project, checkActive);
|
|
auto flags2 = flags;
|
|
|
|
// We can enable some extra items if we have select-all-on-none.
|
|
//EXPLAIN-ME: Why is this here rather than in GetUpdateFlags()?
|
|
//ANSWER: Because flags2 is used in the menu enable/disable.
|
|
//The effect still needs flags to determine whether it will need
|
|
//to actually do the 'select all' to make the command valid.
|
|
if (mWhatIfNoSelection != 0)
|
|
{
|
|
if ((flags & TracksExistFlag))
|
|
{
|
|
flags2 |= TracksSelectedFlag;
|
|
if ((flags & WaveTracksExistFlag))
|
|
{
|
|
flags2 |= TimeSelectedFlag
|
|
| WaveTracksSelectedFlag
|
|
| CutCopyAvailableFlag;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( mStopIfWasPaused )
|
|
{
|
|
if( flags & PausedFlag ){
|
|
flags2 |= AudioIONotBusyFlag;
|
|
}
|
|
}
|
|
|
|
// Return from this function if nothing's changed since
|
|
// the last time we were here.
|
|
if (flags == mLastFlags)
|
|
return;
|
|
mLastFlags = flags;
|
|
|
|
auto &commandManager = CommandManager::Get( project );
|
|
|
|
commandManager.EnableUsingFlags(flags2 , NoFlagsSpecified);
|
|
|
|
// With select-all-on-none, some items that we don't want enabled may have
|
|
// been enabled, since we changed the flags. Here we manually disable them.
|
|
// 0 is grey out, 1 is Autoselect, 2 is Give warnings.
|
|
if (mWhatIfNoSelection != 0)
|
|
{
|
|
if (!(flags & TimeSelectedFlag) | !(flags & TracksSelectedFlag))
|
|
{
|
|
commandManager.Enable(wxT("SplitCut"), false);
|
|
commandManager.Enable(wxT("SplitDelete"), false);
|
|
}
|
|
if (!(flags & WaveTracksSelectedFlag))
|
|
{
|
|
commandManager.Enable(wxT("Split"), false);
|
|
}
|
|
if (!(flags & TimeSelectedFlag) | !(flags & WaveTracksSelectedFlag))
|
|
{
|
|
commandManager.Enable(wxT("ExportSel"), false);
|
|
commandManager.Enable(wxT("SplitNew"), false);
|
|
}
|
|
if (!(flags & TimeSelectedFlag) | !(flags & AudioTracksSelectedFlag))
|
|
{
|
|
commandManager.Enable(wxT("Trim"), false);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
if (flags & CutCopyAvailableFlag) {
|
|
GetCommandManager()->Enable(wxT("Copy"), true);
|
|
GetCommandManager()->Enable(wxT("Cut"), true);
|
|
}
|
|
#endif
|
|
|
|
MenuManager::ModifyToolbarMenus(project);
|
|
}
|
|
|
|
/// The following method moves to the previous track
|
|
/// selecting and unselecting depending if you are on the start of a
|
|
/// block or not.
|
|
|
|
void MenuCreator::RebuildAllMenuBars()
|
|
{
|
|
for( auto p : AllProjects{} ) {
|
|
MenuManager::Get(*p).RebuildMenuBar(*p);
|
|
#if defined(__WXGTK__)
|
|
// Workaround for:
|
|
//
|
|
// http://bugzilla.audacityteam.org/show_bug.cgi?id=458
|
|
//
|
|
// This workaround should be removed when Audacity updates to wxWidgets 3.x which has a fix.
|
|
auto &window = GetProjectFrame( *p );
|
|
wxRect r = window.GetRect();
|
|
window.SetSize(wxSize(1,1));
|
|
window.SetSize(r.GetSize());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
bool MenuManager::ReportIfActionNotAllowed
|
|
( AudacityProject &project,
|
|
const wxString & Name, CommandFlag & flags, CommandFlag flagsRqd, CommandFlag mask )
|
|
{
|
|
bool bAllowed = TryToMakeActionAllowed( project, flags, flagsRqd, mask );
|
|
if( bAllowed )
|
|
return true;
|
|
auto &cm = CommandManager::Get( project );
|
|
cm.TellUserWhyDisallowed( Name, flags & mask, flagsRqd & mask);
|
|
return false;
|
|
}
|
|
|
|
|
|
/// Determines if flags for command are compatible with current state.
|
|
/// If not, then try some recovery action to make it so.
|
|
/// @return whether compatible or not after any actions taken.
|
|
bool MenuManager::TryToMakeActionAllowed
|
|
( AudacityProject &project,
|
|
CommandFlag & flags, CommandFlag flagsRqd, CommandFlag mask )
|
|
{
|
|
bool bAllowed;
|
|
|
|
if( !flags )
|
|
flags = MenuManager::Get(project).GetUpdateFlags(project);
|
|
|
|
bAllowed = ((flags & mask) == (flagsRqd & mask));
|
|
if( bAllowed )
|
|
return true;
|
|
|
|
// Why is action not allowed?
|
|
// 1's wherever a required flag is missing.
|
|
auto MissingFlags = (~flags & flagsRqd) & mask;
|
|
|
|
if( mStopIfWasPaused && (MissingFlags & AudioIONotBusyFlag ) ){
|
|
TransportActions::StopIfPaused( project );
|
|
// Hope this will now reflect stopped audio.
|
|
flags = MenuManager::Get(project).GetUpdateFlags(project);
|
|
bAllowed = ((flags & mask) == (flagsRqd & mask));
|
|
if( bAllowed )
|
|
return true;
|
|
}
|
|
|
|
//We can only make the action allowed if we select audio when no selection.
|
|
// IF not set up to select all audio when none, THEN return with failure.
|
|
if( mWhatIfNoSelection != 1 )
|
|
return false;
|
|
|
|
// Some effects disallow autoselection.
|
|
if( flagsRqd & NoAutoSelect )
|
|
return false;
|
|
|
|
// Why is action still not allowed?
|
|
// 0's wherever a required flag is missing (or is don't care)
|
|
MissingFlags = (flags & ~flagsRqd) & mask;
|
|
|
|
// IF selecting all audio won't do any good, THEN return with failure.
|
|
if( !(flags & WaveTracksExistFlag) )
|
|
return false;
|
|
// returns if mask wants a zero in some flag and that's not present.
|
|
// logic seems a bit peculiar and worth revisiting.
|
|
if( (MissingFlags & ~( TimeSelectedFlag | WaveTracksSelectedFlag)) )
|
|
return false;
|
|
|
|
// This was 'DoSelectSomething()'.
|
|
// This made autoselect more confusing.
|
|
// When autoselect triggers, it might not select all audio in all tracks.
|
|
// So changed to DoSelectAllAudio.
|
|
SelectActions::DoSelectAllAudio(project);
|
|
flags = MenuManager::Get(project).GetUpdateFlags(project);
|
|
bAllowed = ((flags & mask) == (flagsRqd & mask));
|
|
return bAllowed;
|
|
}
|