From 1f763984c946ca82474b4aea6bb179f655fd8d1f Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 13 Jun 2019 12:15:44 -0400 Subject: [PATCH 01/23] Remove unused command flags --- src/Menus.cpp | 28 +--------------------------- src/commands/CommandFlag.h | 13 ++++++------- src/menus/TransportMenus.cpp | 3 +-- 3 files changed, 8 insertions(+), 36 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index eae25b1eb..68b019d89 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -32,7 +32,6 @@ #include "AdornedRulerPanel.h" #include "AudioIO.h" -#include "Clipboard.h" #include "LabelTrack.h" #include "ModuleManager.h" #ifdef USE_MIDI @@ -53,7 +52,6 @@ #include "prefs/TracksPrefs.h" #include "toolbars/ControlToolBar.h" #include "toolbars/ToolManager.h" -#include "widgets/FileHistory.h" #include #include @@ -405,19 +403,9 @@ 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(w)) { return TrackPanelHasFocus; } - if (w == ToolManager::Get( project ).GetBotDock()) { - return BotDockHasFocus; - } w = w->GetParent(); } @@ -457,14 +445,12 @@ CommandFlag MenuManager::GetUpdateFlags( bool checkActive ) if( gAudioIO->IsPaused() ) flags |= PausedFlag; - else - flags |= NotPausedFlag; // quick 'short-circuit' return. if ( checkActive && !window.IsActive() ){ const auto checkedFlags = NotMinimizedFlag | AudioIONotBusyFlag | AudioIOBusyFlag | - PausedFlag | NotPausedFlag; + PausedFlag; // short cirucit return should preserve flags that have not been calculated. flags = (lastFlags & ~checkedFlags) | flags; lastFlags = flags; @@ -533,9 +519,6 @@ CommandFlag MenuManager::GetUpdateFlags( bool checkActive ) #endif ); - if( Clipboard::Get().Duration() > 0 ) - flags |= ClipboardFlag; - auto &undoManager = UndoManager::Get( project ); if (undoManager.UnsavedChanges() || @@ -558,12 +541,6 @@ CommandFlag MenuManager::GetUpdateFlags( bool checkActive ) 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); const auto &playRegion = viewInfo.playRegion; @@ -580,9 +557,6 @@ CommandFlag MenuManager::GetUpdateFlags( bool checkActive ) } } - if (FileHistory::Global().GetCount() > 0) - flags |= HaveRecentFiles; - const auto &settings = ProjectSettings::Get( project ); if (settings.IsSyncLocked()) flags |= IsSyncLockedFlag; diff --git a/src/commands/CommandFlag.h b/src/commands/CommandFlag.h index 8e922aff4..204e44f95 100644 --- a/src/commands/CommandFlag.h +++ b/src/commands/CommandFlag.h @@ -23,8 +23,7 @@ enum CommandFlag : unsigned long long TracksExistFlag = 0x00000008, LabelTracksExistFlag = 0x00000010, WaveTracksSelectedFlag = 0x00000020, - ClipboardFlag = 0x00000040, - TextClipFlag = 0x00000040, // Same as Clipboard flag for now. + UnsavedChangesFlag = 0x00000080, HasLastEffectFlag = 0x00000100, UndoAvailableFlag = 0x00000200, @@ -32,9 +31,9 @@ enum CommandFlag : unsigned long long ZoomInAvailableFlag = 0x00000800, ZoomOutAvailableFlag = 0x00001000, StereoRequiredFlag = 0x00002000, //lda - TopDockHasFocus = 0x00004000, //lll + TrackPanelHasFocus = 0x00008000, //lll - BotDockHasFocus = 0x00010000, //lll + LabelsSelectedFlag = 0x00020000, AudioIOBusyFlag = 0x00040000, //lll PlayRegionLockedFlag = 0x00080000, //msmeyer @@ -43,16 +42,16 @@ enum CommandFlag : unsigned long long WaveTracksExistFlag = 0x00400000, NoteTracksExistFlag = 0x00800000, //gsw NoteTracksSelectedFlag = 0x01000000, //gsw - HaveRecentFiles = 0x02000000, + IsNotSyncLockedFlag = 0x04000000, //awd IsSyncLockedFlag = 0x08000000, //awd IsRealtimeNotActiveFlag= 0x10000000, //lll CaptureNotBusyFlag = 0x20000000, CanStopAudioStreamFlag = 0x40000000, - RulerHasFocus = 0x80000000ULL, // prl + NotMinimizedFlag = 0x100000000ULL, // prl PausedFlag = 0x200000000ULL, // jkc - NotPausedFlag = 0x400000000ULL, // jkc + HasWaveDataFlag = 0x800000000ULL, // jkc PlayableTracksExistFlag = 0x1000000000ULL, AudioTracksSelectedFlag = 0x2000000000ULL, diff --git a/src/menus/TransportMenus.cpp b/src/menus/TransportMenus.cpp index 47f423af4..67d8c61bd 100644 --- a/src/menus/TransportMenus.cpp +++ b/src/menus/TransportMenus.cpp @@ -208,8 +208,7 @@ namespace TransportActions { // Stop playing or recording, if paused. void StopIfPaused( AudacityProject &project ) { - auto flags = MenuManager::Get( project ).GetUpdateFlags(); - if( flags & PausedFlag ) + if( AudioIOBase::Get()->IsPaused() ) DoStop( project ); } From 36e3a03c7ff8f503bbac7361a396952cf2d9d8bc Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 13 Jun 2019 12:29:15 -0400 Subject: [PATCH 02/23] Redo CommandFlags as std::bitset, allow registration of values --- src/Menus.cpp | 94 ++++++++++++---- src/Menus.h | 2 +- src/commands/CommandFlag.h | 186 +++++++++++--------------------- src/commands/CommandManager.cpp | 20 ++-- src/effects/Effect.cpp | 1 - src/menus/EditMenus.cpp | 4 +- src/menus/LabelMenus.cpp | 2 +- src/menus/NavigationMenus.cpp | 2 +- src/menus/SelectMenus.cpp | 4 +- src/menus/TransportMenus.cpp | 2 +- 10 files changed, 157 insertions(+), 160 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index 68b019d89..a97079400 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -414,6 +414,57 @@ CommandFlag MenuManager::GetFocusedFrame(AudacityProject &project) } +ReservedCommandFlag::ReservedCommandFlag() +{ + static size_t sNextReservedFlag = 0; + // This will throw std::out_of_range if the constant NCommandFlags is too + // small + set( sNextReservedFlag++ ); +} + +const ReservedCommandFlag + AudioIONotBusyFlag, + TimeSelectedFlag, // This is equivalent to check if there is a valid selection, so it's used for Zoom to Selection too + TracksSelectedFlag, + TracksExistFlag, + LabelTracksExistFlag, + WaveTracksSelectedFlag, + ClipboardFlag, + TextClipFlag, // Same as Clipboard flag for now. + UnsavedChangesFlag, + HasLastEffectFlag, + UndoAvailableFlag, + RedoAvailableFlag, + ZoomInAvailableFlag, + ZoomOutAvailableFlag, + StereoRequiredFlag, //lda + TopDockHasFocus, //lll + TrackPanelHasFocus, //lll + BotDockHasFocus, //lll + LabelsSelectedFlag, + AudioIOBusyFlag, //lll + PlayRegionLockedFlag, //msmeyer + PlayRegionNotLockedFlag, //msmeyer + CutCopyAvailableFlag, + WaveTracksExistFlag, + NoteTracksExistFlag, //gsw + NoteTracksSelectedFlag, //gsw + HaveRecentFiles, + IsNotSyncLockedFlag, //awd + IsSyncLockedFlag, //awd + IsRealtimeNotActiveFlag, //lll + CaptureNotBusyFlag, + CanStopAudioStreamFlag, + RulerHasFocus, // prl + NotMinimizedFlag, // prl + PausedFlag, // jkc + NotPausedFlag, // jkc + HasWaveDataFlag, // jkc + PlayableTracksExistFlag, + AudioTracksSelectedFlag, + NoAutoSelect // jkc +; + CommandFlag MenuManager::GetUpdateFlags( bool checkActive ) { auto &project = mProject; @@ -535,10 +586,12 @@ CommandFlag MenuManager::GetUpdateFlags( bool checkActive ) if (history.RedoAvailable()) flags |= RedoAvailableFlag; - if (ViewInfo::Get( project ).ZoomInAvailable() && (flags & TracksExistFlag)) + if (ViewInfo::Get( project ).ZoomInAvailable() && + (flags & TracksExistFlag).any()) flags |= ZoomInAvailableFlag; - if (ViewInfo::Get( project ).ZoomOutAvailable() && (flags & TracksExistFlag)) + if (ViewInfo::Get( project ).ZoomOutAvailable() && + (flags & TracksExistFlag).any()) flags |= ZoomOutAvailableFlag; flags |= GetFocusedFrame(project); @@ -549,9 +602,9 @@ CommandFlag MenuManager::GetUpdateFlags( bool checkActive ) else if (!playRegion.Empty()) flags |= PlayRegionNotLockedFlag; - if (flags & AudioIONotBusyFlag) { - if (flags & TimeSelectedFlag) { - if (flags & TracksSelectedFlag) { + if ( (flags & AudioIONotBusyFlag).any() ) { + if ( (flags & TimeSelectedFlag).any() ) { + if ( (flags & TracksSelectedFlag).any() ) { flags |= CutCopyAvailableFlag; } } @@ -681,10 +734,10 @@ void MenuManager::UpdateMenus( bool checkActive ) //to actually do the 'select all' to make the command valid. if (mWhatIfNoSelection != 0) { - if ((flags & TracksExistFlag)) + if ( (flags & TracksExistFlag).any() ) { flags2 |= TracksSelectedFlag; - if ((flags & WaveTracksExistFlag)) + if ( (flags & WaveTracksExistFlag).any() ) { flags2 |= TimeSelectedFlag | WaveTracksSelectedFlag @@ -695,7 +748,7 @@ void MenuManager::UpdateMenus( bool checkActive ) if( mStopIfWasPaused ) { - if( flags & PausedFlag ){ + if( (flags & PausedFlag).any() ){ flags2 |= AudioIONotBusyFlag; } } @@ -715,28 +768,31 @@ void MenuManager::UpdateMenus( bool checkActive ) // 0 is grey out, 1 is Autoselect, 2 is Give warnings. if (mWhatIfNoSelection != 0) { - if (!(flags & TimeSelectedFlag) | !(flags & TracksSelectedFlag)) + if ( (flags & TimeSelectedFlag).none() || + (flags & TracksSelectedFlag).none ()) { commandManager.Enable(wxT("SplitCut"), false); commandManager.Enable(wxT("SplitDelete"), false); } - if (!(flags & WaveTracksSelectedFlag)) + if ( (flags & WaveTracksSelectedFlag).none() ) { commandManager.Enable(wxT("Split"), false); } - if (!(flags & TimeSelectedFlag) | !(flags & WaveTracksSelectedFlag)) + if ( (flags & TimeSelectedFlag).none() || + (flags & WaveTracksSelectedFlag).none() ) { commandManager.Enable(wxT("ExportSel"), false); commandManager.Enable(wxT("SplitNew"), false); } - if (!(flags & TimeSelectedFlag) | !(flags & AudioTracksSelectedFlag)) + if ( (flags & TimeSelectedFlag).none() || + (flags & AudioTracksSelectedFlag).none() ) { commandManager.Enable(wxT("Trim"), false); } } #if 0 - if (flags & CutCopyAvailableFlag) { + if ( (flags & CutCopyAvailableFlag).any() ) { GetCommandManager()->Enable(wxT("Copy"), true); GetCommandManager()->Enable(wxT("Cut"), true); } @@ -789,7 +845,7 @@ bool MenuManager::TryToMakeActionAllowed( auto &project = mProject; bool bAllowed; - if( !flags ) + if( flags.none() ) flags = GetUpdateFlags(); bAllowed = ((flags & mask) == (flagsRqd & mask)); @@ -800,7 +856,7 @@ bool MenuManager::TryToMakeActionAllowed( // 1's wherever a required flag is missing. auto MissingFlags = (~flags & flagsRqd) & mask; - if( mStopIfWasPaused && (MissingFlags & AudioIONotBusyFlag ) ){ + if( mStopIfWasPaused && (MissingFlags & AudioIONotBusyFlag ).any() ){ TransportActions::StopIfPaused( project ); // Hope this will now reflect stopped audio. flags = GetUpdateFlags(); @@ -815,7 +871,7 @@ bool MenuManager::TryToMakeActionAllowed( return false; // Some effects disallow autoselection. - if( flagsRqd & NoAutoSelect ) + if( (flagsRqd & NoAutoSelect).any() ) return false; // Why is action still not allowed? @@ -823,11 +879,13 @@ bool MenuManager::TryToMakeActionAllowed( MissingFlags = (flags & ~flagsRqd) & mask; // IF selecting all audio won't do any good, THEN return with failure. - if( !(flags & WaveTracksExistFlag) ) + if( (flags & WaveTracksExistFlag).none() ) 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)) ) + if( (MissingFlags & + ~( TimeSelectedFlag | WaveTracksSelectedFlag ) + ).any() ) return false; // This was 'DoSelectSomething()'. diff --git a/src/Menus.h b/src/Menus.h index 3ba1e79c7..280551629 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -15,6 +15,7 @@ #include // member variable #include "Prefs.h" #include "ClientData.h" +#include "commands/CommandFlag.h" class wxArrayString; class wxCommandEvent; @@ -29,7 +30,6 @@ class ViewInfo; class WaveClip; class WaveTrack; -enum CommandFlag : unsigned long long; enum EffectType : int; typedef wxString PluginID; diff --git a/src/commands/CommandFlag.h b/src/commands/CommandFlag.h index 204e44f95..f570abaa6 100644 --- a/src/commands/CommandFlag.h +++ b/src/commands/CommandFlag.h @@ -11,133 +11,69 @@ // Flags used in command handling. -// These flags represent the majority of the states that affect -// whether or not items in menus are enabled or disabled. -enum CommandFlag : unsigned long long -{ - AlwaysEnabledFlag = 0x00000000, +#include - AudioIONotBusyFlag = 0x00000001, - TimeSelectedFlag = 0x00000002, // This is equivalent to check if there is a valid selection, so it's used for Zoom to Selection too - TracksSelectedFlag = 0x00000004, - TracksExistFlag = 0x00000008, - LabelTracksExistFlag = 0x00000010, - WaveTracksSelectedFlag = 0x00000020, - - UnsavedChangesFlag = 0x00000080, - HasLastEffectFlag = 0x00000100, - UndoAvailableFlag = 0x00000200, - RedoAvailableFlag = 0x00000400, - ZoomInAvailableFlag = 0x00000800, - ZoomOutAvailableFlag = 0x00001000, - StereoRequiredFlag = 0x00002000, //lda - - TrackPanelHasFocus = 0x00008000, //lll - - LabelsSelectedFlag = 0x00020000, - AudioIOBusyFlag = 0x00040000, //lll - PlayRegionLockedFlag = 0x00080000, //msmeyer - PlayRegionNotLockedFlag= 0x00100000, //msmeyer - CutCopyAvailableFlag = 0x00200000, - WaveTracksExistFlag = 0x00400000, - NoteTracksExistFlag = 0x00800000, //gsw - NoteTracksSelectedFlag = 0x01000000, //gsw - - IsNotSyncLockedFlag = 0x04000000, //awd - IsSyncLockedFlag = 0x08000000, //awd - IsRealtimeNotActiveFlag= 0x10000000, //lll - CaptureNotBusyFlag = 0x20000000, - CanStopAudioStreamFlag = 0x40000000, - - NotMinimizedFlag = 0x100000000ULL, // prl - PausedFlag = 0x200000000ULL, // jkc - - HasWaveDataFlag = 0x800000000ULL, // jkc - PlayableTracksExistFlag = 0x1000000000ULL, - AudioTracksSelectedFlag = 0x2000000000ULL, - NoAutoSelect = 0x4000000000ULL, // jkc - - NoFlagsSpecified = ~0ULL -}; - -// Prevent accidental misuse with narrower types - -bool operator == (CommandFlag, unsigned long) PROHIBITED; -bool operator == (CommandFlag, long) PROHIBITED; -bool operator == (unsigned long, CommandFlag) PROHIBITED; -bool operator == (long, CommandFlag) PROHIBITED; - -bool operator != (CommandFlag, unsigned long) PROHIBITED; -bool operator != (CommandFlag, long) PROHIBITED; -bool operator != (unsigned long, CommandFlag) PROHIBITED; -bool operator != (long, CommandFlag) PROHIBITED; - -CommandFlag operator & (CommandFlag, unsigned long) PROHIBITED; -CommandFlag operator & (CommandFlag, long) PROHIBITED; -CommandFlag operator & (unsigned long, CommandFlag) PROHIBITED; -CommandFlag operator & (long, CommandFlag) PROHIBITED; - -CommandFlag operator | (CommandFlag, unsigned long) PROHIBITED; -CommandFlag operator | (CommandFlag, long) PROHIBITED; -CommandFlag operator | (unsigned long, CommandFlag) PROHIBITED; -CommandFlag operator | (long, CommandFlag) PROHIBITED; - -CommandFlag operator ^ (CommandFlag, unsigned long) PROHIBITED; -CommandFlag operator ^ (CommandFlag, long) PROHIBITED; -CommandFlag operator ^ (unsigned long, CommandFlag) PROHIBITED; -CommandFlag operator ^ (long, CommandFlag) PROHIBITED; - -bool operator == (CommandFlag, unsigned int) PROHIBITED; -bool operator == (CommandFlag, int) PROHIBITED; -bool operator == (unsigned int, CommandFlag) PROHIBITED; -bool operator == (int, CommandFlag) PROHIBITED; - -bool operator != (CommandFlag, unsigned int) PROHIBITED; -bool operator != (CommandFlag, int) PROHIBITED; -bool operator != (unsigned int, CommandFlag) PROHIBITED; -bool operator != (int, CommandFlag) PROHIBITED; - -CommandFlag operator & (CommandFlag, unsigned int) PROHIBITED; -CommandFlag operator & (CommandFlag, int) PROHIBITED; -CommandFlag operator & (unsigned int, CommandFlag) PROHIBITED; -CommandFlag operator & (int, CommandFlag) PROHIBITED; - -CommandFlag operator | (CommandFlag, unsigned int) PROHIBITED; -CommandFlag operator | (CommandFlag, int) PROHIBITED; -CommandFlag operator | (unsigned int, CommandFlag) PROHIBITED; -CommandFlag operator | (int, CommandFlag) PROHIBITED; - -CommandFlag operator ^ (CommandFlag, unsigned int) PROHIBITED; -CommandFlag operator ^ (CommandFlag, int) PROHIBITED; -CommandFlag operator ^ (unsigned int, CommandFlag) PROHIBITED; -CommandFlag operator ^ (int, CommandFlag) PROHIBITED; - -// Supply the bitwise operations - -inline constexpr CommandFlag operator ~ (CommandFlag flag) -{ - return static_cast( ~ static_cast (flag) ); -} -inline constexpr CommandFlag operator & (CommandFlag lhs, CommandFlag rhs) -{ - return static_cast ( - static_cast(lhs) & - static_cast(rhs) - ); -} -inline constexpr CommandFlag operator | (CommandFlag lhs, CommandFlag rhs) -{ - return static_cast ( - static_cast(lhs) | - static_cast(rhs) - ); -} -inline CommandFlag & operator |= (CommandFlag &lhs, CommandFlag rhs) -{ - lhs = lhs | rhs; - return lhs; -} +// Increase the template parameter as needed to allow more flags +constexpr size_t NCommandFlags = 64; +static_assert( + NCommandFlags <= 8 * sizeof( unsigned long long ), + "NoFlagsSpecified may have incorrect value" +); +// Type to specify conditions for enabling of a menu item +using CommandFlag = std::bitset; using CommandMask = CommandFlag; +// Special constant values +constexpr CommandFlag + AlwaysEnabledFlag{}, // all zeroes + NoFlagsSpecified{ ~0ULL }; // all ones + +// Construct one statically to register (and reserve) a bit position in the set +class ReservedCommandFlag : public CommandFlag +{ +public: + ReservedCommandFlag(); +}; + +// Widely used command flags, but this list need not be exhaustive. It may be +// extended, with special purpose flags of limited use, by constucting static +// ReservedCommandFlag values + +extern const ReservedCommandFlag + AudioIONotBusyFlag, + TimeSelectedFlag, // This is equivalent to check if there is a valid selection, so it's used for Zoom to Selection too + TracksSelectedFlag, + TracksExistFlag, + LabelTracksExistFlag, + WaveTracksSelectedFlag, + UnsavedChangesFlag, + HasLastEffectFlag, + UndoAvailableFlag, + RedoAvailableFlag, + ZoomInAvailableFlag, + ZoomOutAvailableFlag, + StereoRequiredFlag, //lda + TrackPanelHasFocus, //lll + LabelsSelectedFlag, + AudioIOBusyFlag, //lll + PlayRegionLockedFlag, //msmeyer + PlayRegionNotLockedFlag, //msmeyer + CutCopyAvailableFlag, + WaveTracksExistFlag, + NoteTracksExistFlag, //gsw + NoteTracksSelectedFlag, //gsw + IsNotSyncLockedFlag, //awd + IsSyncLockedFlag, //awd + IsRealtimeNotActiveFlag, //lll + CaptureNotBusyFlag, + CanStopAudioStreamFlag, + NotMinimizedFlag, // prl + PausedFlag, // jkc + HasWaveDataFlag, // jkc + PlayableTracksExistFlag, + AudioTracksSelectedFlag, + NoAutoSelect // jkc +; + #endif diff --git a/src/commands/CommandManager.cpp b/src/commands/CommandManager.cpp index 8b17d02c9..7d1caeacc 100644 --- a/src/commands/CommandManager.cpp +++ b/src/commands/CommandManager.cpp @@ -964,7 +964,7 @@ void CommandManager::EnableUsingFlags(CommandFlag flags, CommandMask mask) continue; auto combinedMask = (mask & entry->mask); - if (combinedMask) { + if (combinedMask.any()) { bool enable = ((flags & combinedMask) == (entry->flags & combinedMask)); Enable(entry.get(), enable); @@ -1027,15 +1027,18 @@ void CommandManager::TellUserWhyDisallowed( const wxString & Name, CommandFlag f wxString title = _("Disallowed"); wxString helpPage; - auto missingFlags = flagsRequired & (~flagsGot ); - if( missingFlags & AudioIONotBusyFlag ) + auto missingFlags = flagsRequired & ~flagsGot; + if( (missingFlags & AudioIONotBusyFlag).any() ) // This reason will not be shown, because options that require it will be greyed our. reason = _("You can only do this when playing and recording are\nstopped. (Pausing is not sufficient.)"); - else if( missingFlags & StereoRequiredFlag ) + else if( (missingFlags & StereoRequiredFlag).any() ) // This reason will not be shown, because the stereo-to-mono is greyed out if not allowed. reason = _("You must first select some stereo audio to perform this\naction. (You cannot use this with mono.)"); // In reporting the issue with cut or copy, we don't tell the user they could also select some text in a label. - else if(( missingFlags & TimeSelectedFlag ) || (missingFlags &CutCopyAvailableFlag )){ + else if( ( + ( missingFlags & TimeSelectedFlag ) | + ( missingFlags & CutCopyAvailableFlag ) + ).any() ){ title = _("No Audio Selected"); #ifdef EXPERIMENTAL_DA // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. @@ -1058,9 +1061,9 @@ void CommandManager::TellUserWhyDisallowed( const wxString & Name, CommandFlag f #endif helpPage = "Selecting_Audio_-_the_basics"; } - else if( missingFlags & WaveTracksSelectedFlag) + else if( (missingFlags & WaveTracksSelectedFlag).any() ) reason = _("You must first select some audio to perform this action.\n(Selecting other kinds of track won't work.)"); - else if ( missingFlags & TracksSelectedFlag ) + else if ( (missingFlags & TracksSelectedFlag).any() ) // i18n-hint: %s will be replaced by the name of an action, such as "Remove Tracks". reason = wxString::Format(_("\"%s\" requires one or more tracks to be selected."), Name); // If the only thing wrong was no tracks, we do nothing and don't report a problem @@ -1254,7 +1257,7 @@ bool CommandManager::HandleCommandEntry(const CommandListEntry * entry, auto proj = GetActiveProject(); auto combinedMask = (mask & entry->mask); - if (combinedMask) { + if (combinedMask.any()) { wxASSERT( proj ); if( !proj ) @@ -1671,3 +1674,4 @@ static struct InstallHandlers } ); } } installHandlers; + diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index e0482a09e..4090cd362 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -63,7 +63,6 @@ greater use in future. #include "../ViewInfo.h" #include "../WaveTrack.h" #include "../commands/Command.h" -#include "../commands/CommandFlag.h" #include "../toolbars/ControlToolBar.h" #include "../widgets/AButton.h" #include "../widgets/ProgressDialog.h" diff --git a/src/menus/EditMenus.cpp b/src/menus/EditMenus.cpp index 501ea396c..0ce0641e3 100644 --- a/src/menus/EditMenus.cpp +++ b/src/menus/EditMenus.cpp @@ -1095,7 +1095,7 @@ MenuTable::BaseItemPtr EditMenu( AudacityProject & ) using namespace MenuTable; using Options = CommandManager::Options; - constexpr auto NotBusyTimeAndTracksFlags = + static const auto NotBusyTimeAndTracksFlags = AudioIONotBusyFlag | TimeSelectedFlag | TracksSelectedFlag; // The default shortcut key for Redo is different on different platforms. @@ -1216,7 +1216,7 @@ MenuTable::BaseItemPtr ExtraEditMenu( AudacityProject & ) { using namespace MenuTable; using Options = CommandManager::Options; - constexpr auto flags = + static const auto flags = AudioIONotBusyFlag | TracksSelectedFlag | TimeSelectedFlag; return Menu( _("&Edit"), Command( wxT("DeleteKey"), XXO("&Delete Key"), FN(OnDelete), diff --git a/src/menus/LabelMenus.cpp b/src/menus/LabelMenus.cpp index 30212029b..7b2dd4a23 100644 --- a/src/menus/LabelMenus.cpp +++ b/src/menus/LabelMenus.cpp @@ -586,7 +586,7 @@ MenuTable::BaseItemPtr LabelEditMenus( AudacityProject & ) static const auto checkOff = Options{}.CheckState( false ); - constexpr auto NotBusyLabelsAndWaveFlags = + static const auto NotBusyLabelsAndWaveFlags = AudioIONotBusyFlag | LabelsSelectedFlag | WaveTracksExistFlag | TimeSelectedFlag; diff --git a/src/menus/NavigationMenus.cpp b/src/menus/NavigationMenus.cpp index e385e3aa6..045fc743b 100644 --- a/src/menus/NavigationMenus.cpp +++ b/src/menus/NavigationMenus.cpp @@ -580,7 +580,7 @@ MenuTable::BaseItemPtr ExtraGlobalCommands( AudacityProject & ) MenuTable::BaseItemPtr ExtraFocusMenu( AudacityProject & ) { using namespace MenuTable; - constexpr auto FocusedTracksFlags = TracksExistFlag | TrackPanelHasFocus; + static const auto FocusedTracksFlags = TracksExistFlag | TrackPanelHasFocus; return Menu( _("F&ocus"), Command( wxT("PrevFrame"), diff --git a/src/menus/SelectMenus.cpp b/src/menus/SelectMenus.cpp index 282a8c7fe..ad2c63dfc 100644 --- a/src/menus/SelectMenus.cpp +++ b/src/menus/SelectMenus.cpp @@ -513,7 +513,7 @@ void SelectAllIfNone( AudacityProject &project ) { auto &viewInfo = ViewInfo::Get( project ); auto flags = MenuManager::Get( project ).GetUpdateFlags(); - if(!(flags & TracksSelectedFlag) || + if((flags & TracksSelectedFlag).none() || viewInfo.selectedRegion.isPoint()) DoSelectAllAudio( project ); } @@ -1332,7 +1332,7 @@ MenuTable::BaseItemPtr CursorMenu( AudacityProject & ) { using namespace MenuTable; using Options = CommandManager::Options; - constexpr auto CanStopFlags = AudioIONotBusyFlag | CanStopAudioStreamFlag; + static const auto CanStopFlags = AudioIONotBusyFlag | CanStopAudioStreamFlag; // JKC: ANSWER-ME: How is 'cursor to' different to 'Skip To' and how is it // useful? diff --git a/src/menus/TransportMenus.cpp b/src/menus/TransportMenus.cpp index 67d8c61bd..f1a600c6e 100644 --- a/src/menus/TransportMenus.cpp +++ b/src/menus/TransportMenus.cpp @@ -1075,7 +1075,7 @@ MenuTable::BaseItemPtr TransportMenu( AudacityProject &project ) static const auto checkOff = Options{}.CheckState( false ); static const auto checkOn = Options{}.CheckState( true ); - constexpr auto CanStopFlags = AudioIONotBusyFlag | CanStopAudioStreamFlag; + static const auto CanStopFlags = AudioIONotBusyFlag | CanStopAudioStreamFlag; /* i18n-hint: 'Transport' is the name given to the set of controls that play, record, pause etc. */ From 705b4b28e79ab24a7ab751ad3a47ecf8f779ec54 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sat, 8 Jun 2019 20:35:21 -0400 Subject: [PATCH 03/23] Register a predicate with each CommandFlag bit --- src/LabelTrack.cpp | 2 +- src/LabelTrack.h | 3 +- src/Menus.cpp | 315 +++++++++++++++++++++++++++----- src/ProjectFileIO.cpp | 2 +- src/ProjectFileIO.h | 2 +- src/ProjectHistory.cpp | 4 +- src/ProjectHistory.h | 4 +- src/UndoManager.cpp | 4 +- src/UndoManager.h | 6 +- src/commands/CommandFlag.h | 7 +- src/toolbars/ControlToolBar.cpp | 2 +- src/toolbars/ControlToolBar.h | 2 +- src/toolbars/ToolManager.cpp | 10 + src/toolbars/ToolManager.h | 2 + 14 files changed, 308 insertions(+), 57 deletions(-) diff --git a/src/LabelTrack.cpp b/src/LabelTrack.cpp index f1b872841..c47065617 100644 --- a/src/LabelTrack.cpp +++ b/src/LabelTrack.cpp @@ -1012,7 +1012,7 @@ void LabelTrack::calculateFontHeight(wxDC & dc) const mFontHeight += CursorExtraHeight - (charLeading+charDescent); } -bool LabelTrack::IsTextSelected() +bool LabelTrack::IsTextSelected() const { if (mSelIndex == -1) return false; diff --git a/src/LabelTrack.h b/src/LabelTrack.h index e794cb075..bdd90d529 100644 --- a/src/LabelTrack.h +++ b/src/LabelTrack.h @@ -117,7 +117,7 @@ class AUDACITY_DLL_API LabelTrack final : public Track bool IsGoodLabelFirstKey(const wxKeyEvent & evt); bool IsGoodLabelEditKey(const wxKeyEvent & evt); - bool IsTextSelected(); + bool IsTextSelected() const; void CreateCustomGlyphs(); LabelTrack(const std::shared_ptr &projDirManager); @@ -228,6 +228,7 @@ class AUDACITY_DLL_API LabelTrack final : public Track int GetNumLabels() const; const LabelStruct *GetLabel(int index) const; + const LabelArray &GetLabels() const { return mLabels; } //This returns the index of the label we just added. int AddLabel(const SelectedRegion ®ion, const wxString &title = {}, diff --git a/src/Menus.cpp b/src/Menus.cpp index a97079400..767638328 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -30,6 +30,8 @@ #include "Experimental.h" +#include + #include "AdornedRulerPanel.h" #include "AudioIO.h" #include "LabelTrack.h" @@ -414,55 +416,286 @@ CommandFlag MenuManager::GetFocusedFrame(AudacityProject &project) } -ReservedCommandFlag::ReservedCommandFlag() +// Really means, some track is selected, that isn't a time track +const auto TracksSelectedPred = + [](const AudacityProject &project){ + auto range = TrackList::Get( project ).Selected() + - []( const Track *pTrack ){ + return track_cast( pTrack ); }; + return !range.empty(); + }; + +const auto AudioIOBusyPred = + [](const AudacityProject &project ){ + return AudioIOBase::Get()->IsAudioTokenActive( + ProjectAudioIO::Get( project ).GetAudioIOToken()); + }; + +const auto TimeSelectedPred = + [](const AudacityProject &project){ + // This is equivalent to check if there is a valid selection, + // so it's used for Zoom to Selection too + return !ViewInfo::Get( project ).selectedRegion.isPoint(); + }; + +namespace{ + using Predicates = std::vector< ReservedCommandFlag::Predicate >; + Predicates &RegisteredPredicates() + { + static Predicates thePredicates; + return thePredicates; + } +} + +ReservedCommandFlag::ReservedCommandFlag( const Predicate &predicate ) { static size_t sNextReservedFlag = 0; // This will throw std::out_of_range if the constant NCommandFlags is too // small set( sNextReservedFlag++ ); + RegisteredPredicates().emplace_back( predicate ); } const ReservedCommandFlag - AudioIONotBusyFlag, - TimeSelectedFlag, // This is equivalent to check if there is a valid selection, so it's used for Zoom to Selection too - TracksSelectedFlag, - TracksExistFlag, - LabelTracksExistFlag, - WaveTracksSelectedFlag, - ClipboardFlag, - TextClipFlag, // Same as Clipboard flag for now. - UnsavedChangesFlag, - HasLastEffectFlag, - UndoAvailableFlag, - RedoAvailableFlag, - ZoomInAvailableFlag, - ZoomOutAvailableFlag, - StereoRequiredFlag, //lda - TopDockHasFocus, //lll - TrackPanelHasFocus, //lll - BotDockHasFocus, //lll - LabelsSelectedFlag, - AudioIOBusyFlag, //lll - PlayRegionLockedFlag, //msmeyer - PlayRegionNotLockedFlag, //msmeyer - CutCopyAvailableFlag, - WaveTracksExistFlag, - NoteTracksExistFlag, //gsw - NoteTracksSelectedFlag, //gsw - HaveRecentFiles, - IsNotSyncLockedFlag, //awd - IsSyncLockedFlag, //awd - IsRealtimeNotActiveFlag, //lll - CaptureNotBusyFlag, - CanStopAudioStreamFlag, - RulerHasFocus, // prl - NotMinimizedFlag, // prl - PausedFlag, // jkc - NotPausedFlag, // jkc - HasWaveDataFlag, // jkc - PlayableTracksExistFlag, - AudioTracksSelectedFlag, - NoAutoSelect // jkc + AudioIONotBusyFlag{ + [](const AudacityProject &project ){ + return !AudioIOBusyPred( project ); + } + }, + TimeSelectedFlag{ + TimeSelectedPred + }, + TracksSelectedFlag{ + TracksSelectedPred + }, + TracksExistFlag{ + [](const AudacityProject &project){ + return !TrackList::Get( project ).Any().empty(); + } + }, + LabelTracksExistFlag{ + [](const AudacityProject &project){ + return !TrackList::Get( project ).Any().empty(); + } + }, + WaveTracksSelectedFlag{ + [](const AudacityProject &project){ + return !TrackList::Get( project ).Selected().empty(); + } + }, + UnsavedChangesFlag{ + [](const AudacityProject &project){ + auto &undoManager = UndoManager::Get( project ); + return + undoManager.UnsavedChanges() + || + !ProjectFileIO::Get( project ).IsProjectSaved() + ; + } + }, + HasLastEffectFlag{ + [](const AudacityProject &project){ + return !MenuManager::Get( project ).mLastEffect.empty(); + } + }, + UndoAvailableFlag{ + [](const AudacityProject &project){ + return ProjectHistory::Get( project ).UndoAvailable(); + } + }, + RedoAvailableFlag{ + [](const AudacityProject &project){ + return ProjectHistory::Get( project ).RedoAvailable(); + } + }, + ZoomInAvailableFlag{ + [](const AudacityProject &project){ + return + ViewInfo::Get( project ).ZoomInAvailable() + && + !TrackList::Get( project ).Any().empty() + ; + } + }, + ZoomOutAvailableFlag{ + [](const AudacityProject &project){ + return + ViewInfo::Get( project ).ZoomOutAvailable() + && + !TrackList::Get( project ).Any().empty() + ; + } + }, + StereoRequiredFlag{ + [](const AudacityProject &project){ + // True iff at least one stereo track is selected, i.e., at least + // one right channel is selected. + // TODO: more-than-two-channels + auto range = TrackList::Get( project ).Selected() + - &Track::IsLeader; + return !range.empty(); + } + }, //lda + TrackPanelHasFocus{ + [](const AudacityProject &project){ + for (auto w = wxWindow::FindFocus(); w; w = w->GetParent()) { + if (dynamic_cast(w)) + return true; + } + return false; + } + }, //lll + LabelsSelectedFlag{ + [](const AudacityProject &project){ + // At least one label track selected, having at least one label + // completely within the time selection. + const auto &selectedRegion = ViewInfo::Get( project ).selectedRegion; + const auto &test = [&]( const LabelTrack *pTrack ){ + const auto &labels = pTrack->GetLabels(); + return std::any_of( labels.begin(), labels.end(), + [&](const LabelStruct &label){ + return + label.getT0() >= selectedRegion.t0() + && + label.getT1() <= selectedRegion.t1() + ; + } + ); + }; + auto range = TrackList::Get( project ).Selected() + + test; + return !range.empty(); + } + }, + AudioIOBusyFlag{ + AudioIOBusyPred + }, //lll + PlayRegionLockedFlag{ + [](const AudacityProject &project){ + return ViewInfo::Get(project).playRegion.Locked(); + } + }, //msmeyer + PlayRegionNotLockedFlag{ + [](const AudacityProject &project){ + const auto &playRegion = ViewInfo::Get(project).playRegion; + return !playRegion.Locked() && !playRegion.Empty(); + } + }, //msmeyer + CutCopyAvailableFlag{ + [](const AudacityProject &project){ + auto range = TrackList::Get( project ).Any() + + &LabelTrack::IsTextSelected; + if ( !range.empty() ) + return true; + + if ( + !AudioIOBusyPred( project ) + && + TimeSelectedPred( project ) + && + TracksSelectedPred( project ) + ) + return true; + + return false; + } + }, + WaveTracksExistFlag{ + [](const AudacityProject &project){ + return !TrackList::Get( project ).Any().empty(); + } + }, + NoteTracksExistFlag{ + [](const AudacityProject &project){ + return !TrackList::Get( project ).Any().empty(); + } + }, //gsw + NoteTracksSelectedFlag{ + [](const AudacityProject &project){ + return !TrackList::Get( project ).Selected().empty(); + } + }, //gsw + IsNotSyncLockedFlag{ + [](const AudacityProject &project){ + return !ProjectSettings::Get( project ).IsSyncLocked(); + } + }, //awd + IsSyncLockedFlag{ + [](const AudacityProject &project){ + return ProjectSettings::Get( project ).IsSyncLocked(); + } + }, //awd + IsRealtimeNotActiveFlag{ + [](const AudacityProject &){ + return !EffectManager::Get().RealtimeIsActive(); + } + }, //lll + CaptureNotBusyFlag{ + [](const AudacityProject &){ + auto gAudioIO = AudioIO::Get(); + return !( + gAudioIO->IsBusy() && + gAudioIO->GetNumCaptureChannels() > 0 + ); + } + }, + CanStopAudioStreamFlag{ + [](const AudacityProject &project){ + return ControlToolBar::Get( project ).CanStopAudioStream(); + } + }, + NotMinimizedFlag{ + [](const AudacityProject &project){ + const wxWindow *focus = FindProjectFrame( &project ); + if (focus) { + while (focus && focus->GetParent()) + focus = focus->GetParent(); + } + return (focus && + !static_cast(focus)->IsIconized() + ); + } + }, // prl + PausedFlag{ + [](const AudacityProject&){ + return AudioIOBase::Get()->IsPaused(); + } + }, + HasWaveDataFlag{ + [](const AudacityProject &project){ + auto range = TrackList::Get( project ).Any() + + [](const WaveTrack *pTrack){ + return pTrack->GetEndTime() > pTrack->GetStartTime(); + }; + return !range.empty(); + } + }, // jkc + PlayableTracksExistFlag{ + [](const AudacityProject &project){ + auto &tracks = TrackList::Get( project ); + return +#ifdef EXPERIMENTAL_MIDI_OUT + !tracks.Any().empty() + || +#endif + !tracks.Any().empty() + ; + } + }, + AudioTracksSelectedFlag{ + [](const AudacityProject &project){ + auto &tracks = TrackList::Get( project ); + return + !tracks.Selected().empty() + // even if not EXPERIMENTAL_MIDI_OUT + || + tracks.Selected().empty() + ; + } + }, + NoAutoSelect{ + [](const AudacityProject &){ return false; } + } // jkc ; CommandFlag MenuManager::GetUpdateFlags( bool checkActive ) diff --git a/src/ProjectFileIO.cpp b/src/ProjectFileIO.cpp index a45f031c0..918ea8902 100644 --- a/src/ProjectFileIO.cpp +++ b/src/ProjectFileIO.cpp @@ -642,7 +642,7 @@ void ProjectFileIO::DeleteCurrentAutoSaveFile() } } -bool ProjectFileIO::IsProjectSaved() { +bool ProjectFileIO::IsProjectSaved() const { auto &project = mProject; auto &dirManager = DirManager::Get( project ); // This is true if a project was opened from an .aup diff --git a/src/ProjectFileIO.h b/src/ProjectFileIO.h index 3130c09e6..c13f9d49a 100644 --- a/src/ProjectFileIO.h +++ b/src/ProjectFileIO.h @@ -40,7 +40,7 @@ public: // recovery file void SetProjectTitle( int number = -1 ); - bool IsProjectSaved(); + bool IsProjectSaved() const; void Reset(); diff --git a/src/ProjectHistory.cpp b/src/ProjectHistory.cpp index 32356d29a..f5674dda3 100644 --- a/src/ProjectHistory.cpp +++ b/src/ProjectHistory.cpp @@ -59,7 +59,7 @@ void ProjectHistory::InitialState() undoManager.StateSaved(); } -bool ProjectHistory::UndoAvailable() +bool ProjectHistory::UndoAvailable() const { auto &project = mProject; auto &tracks = TrackList::Get( project ); @@ -68,7 +68,7 @@ bool ProjectHistory::UndoAvailable() !tracks.HasPendingTracks(); } -bool ProjectHistory::RedoAvailable() +bool ProjectHistory::RedoAvailable() const { auto &project = mProject; auto &tracks = TrackList::Get( project ); diff --git a/src/ProjectHistory.h b/src/ProjectHistory.h index d425854e4..6e86d626e 100644 --- a/src/ProjectHistory.h +++ b/src/ProjectHistory.h @@ -31,8 +31,8 @@ public: void InitialState(); void SetStateTo(unsigned int n); - bool UndoAvailable(); - bool RedoAvailable(); + bool UndoAvailable() const; + bool RedoAvailable() const; void PushState(const wxString &desc, const wxString &shortDesc); // use UndoPush::AUTOSAVE void PushState(const wxString &desc, const wxString &shortDesc, UndoPush flags); void RollbackState(); diff --git a/src/UndoManager.cpp b/src/UndoManager.cpp index fa6ab15fa..c552581a3 100644 --- a/src/UndoManager.cpp +++ b/src/UndoManager.cpp @@ -388,7 +388,7 @@ void UndoManager::Redo(const Consumer &consumer) mProject.QueueEvent( safenew wxCommandEvent{ EVT_UNDO_OR_REDO } ); } -bool UndoManager::UnsavedChanges() +bool UndoManager::UnsavedChanges() const { return (saved != current) || HasODChangesFlag(); } @@ -418,7 +418,7 @@ void UndoManager::SetODChangesFlag() mODChangesMutex.Unlock(); } -bool UndoManager::HasODChangesFlag() +bool UndoManager::HasODChangesFlag() const { bool ret; mODChangesMutex.Lock(); diff --git a/src/UndoManager.h b/src/UndoManager.h index fa383e3ce..a7b9b475b 100644 --- a/src/UndoManager.h +++ b/src/UndoManager.h @@ -152,7 +152,7 @@ class AUDACITY_DLL_API UndoManager bool UndoAvailable(); bool RedoAvailable(); - bool UnsavedChanges(); + bool UnsavedChanges() const; void StateSaved(); // Return value must first be calculated by CalculateSpaceUsage(): @@ -167,7 +167,7 @@ class AUDACITY_DLL_API UndoManager ///to mark as unsaved changes without changing the state/tracks. void SetODChangesFlag(); - bool HasODChangesFlag(); + bool HasODChangesFlag() const; void ResetODChangesFlag(); private: @@ -184,7 +184,7 @@ class AUDACITY_DLL_API UndoManager unsigned long long mClipboardSpaceUsage {}; bool mODChanges; - ODLock mODChangesMutex;//mODChanges is accessed from many threads. + mutable ODLock mODChangesMutex;//mODChanges is accessed from many threads. }; diff --git a/src/commands/CommandFlag.h b/src/commands/CommandFlag.h index f570abaa6..7260314a4 100644 --- a/src/commands/CommandFlag.h +++ b/src/commands/CommandFlag.h @@ -12,6 +12,9 @@ // Flags used in command handling. #include +#include + +class AudacityProject; // Increase the template parameter as needed to allow more flags constexpr size_t NCommandFlags = 64; @@ -30,10 +33,12 @@ constexpr CommandFlag NoFlagsSpecified{ ~0ULL }; // all ones // Construct one statically to register (and reserve) a bit position in the set +// an associate it with a test function class ReservedCommandFlag : public CommandFlag { public: - ReservedCommandFlag(); + using Predicate = std::function< bool( const AudacityProject& ) >; + ReservedCommandFlag( const Predicate & ); }; // Widely used command flags, but this list need not be exhaustive. It may be diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index 5c4541dff..adcd3ae9e 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -824,7 +824,7 @@ void ControlToolBar::OnStop(wxCommandEvent & WXUNUSED(evt)) } } -bool ControlToolBar::CanStopAudioStream() +bool ControlToolBar::CanStopAudioStream() const { auto gAudioIO = AudioIO::Get(); return (!gAudioIO->IsStreamActive() || diff --git a/src/toolbars/ControlToolBar.h b/src/toolbars/ControlToolBar.h index 6324bce82..d4d438490 100644 --- a/src/toolbars/ControlToolBar.h +++ b/src/toolbars/ControlToolBar.h @@ -99,7 +99,7 @@ class ControlToolBar final : public ToolBar { bool IsRecordDown() const; // A project is only allowed to stop an audio stream that it owns. - bool CanStopAudioStream (); + bool CanStopAudioStream () const; // Play currently selected region, or if nothing selected, // play from current cursor. diff --git a/src/toolbars/ToolManager.cpp b/src/toolbars/ToolManager.cpp index 35e2d79e9..696c9d0cf 100644 --- a/src/toolbars/ToolManager.cpp +++ b/src/toolbars/ToolManager.cpp @@ -1022,6 +1022,11 @@ ToolDock *ToolManager::GetTopDock() return mTopDock; } +const ToolDock *ToolManager::GetTopDock() const +{ + return mTopDock; +} + // // Return a pointer to the bottom dock // @@ -1030,6 +1035,11 @@ ToolDock *ToolManager::GetBotDock() return mBotDock; } +const ToolDock *ToolManager::GetBotDock() const +{ + return mBotDock; +} + // // Queues an EVT_TOOLBAR_UPDATED command event to notify any // interest parties of an updated toolbar or dock layout diff --git a/src/toolbars/ToolManager.h b/src/toolbars/ToolManager.h index 673b76caf..820512645 100644 --- a/src/toolbars/ToolManager.h +++ b/src/toolbars/ToolManager.h @@ -68,7 +68,9 @@ class ToolManager final ToolBar *GetToolBar( int type ) const; ToolDock *GetTopDock(); + const ToolDock *GetTopDock() const; ToolDock *GetBotDock(); + const ToolDock *GetBotDock() const; void Reset(); void Destroy(); From 82ea023843328af53f93e4900db502cf64159ffc Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sat, 8 Jun 2019 20:53:01 -0400 Subject: [PATCH 04/23] Distinguish the quick-to-compute command flags --- src/Menus.cpp | 21 ++++++++++++++++----- src/commands/CommandFlag.h | 18 ++++++++++++++++-- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index 767638328..4929456bd 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -445,22 +445,30 @@ namespace{ static Predicates thePredicates; return thePredicates; } + std::vector< CommandFlagOptions > &Options() + { + static std::vector< CommandFlagOptions > options; + return options; + } } -ReservedCommandFlag::ReservedCommandFlag( const Predicate &predicate ) +ReservedCommandFlag::ReservedCommandFlag( + const Predicate &predicate, const CommandFlagOptions &options ) { static size_t sNextReservedFlag = 0; // This will throw std::out_of_range if the constant NCommandFlags is too // small set( sNextReservedFlag++ ); RegisteredPredicates().emplace_back( predicate ); + Options().emplace_back( options ); } const ReservedCommandFlag AudioIONotBusyFlag{ [](const AudacityProject &project ){ return !AudioIOBusyPred( project ); - } + }, + CommandFlagOptions{}.QuickTest() }, TimeSelectedFlag{ TimeSelectedPred @@ -568,7 +576,8 @@ const ReservedCommandFlag } }, AudioIOBusyFlag{ - AudioIOBusyPred + AudioIOBusyPred, + CommandFlagOptions{}.QuickTest() }, //lll PlayRegionLockedFlag{ [](const AudacityProject &project){ @@ -654,12 +663,14 @@ const ReservedCommandFlag return (focus && !static_cast(focus)->IsIconized() ); - } + }, + CommandFlagOptions{}.QuickTest() }, // prl PausedFlag{ [](const AudacityProject&){ return AudioIOBase::Get()->IsPaused(); - } + }, + CommandFlagOptions{}.QuickTest() }, HasWaveDataFlag{ [](const AudacityProject &project){ diff --git a/src/commands/CommandFlag.h b/src/commands/CommandFlag.h index 7260314a4..d4694cbad 100644 --- a/src/commands/CommandFlag.h +++ b/src/commands/CommandFlag.h @@ -13,6 +13,7 @@ #include #include +#include class AudacityProject; @@ -32,13 +33,26 @@ constexpr CommandFlag AlwaysEnabledFlag{}, // all zeroes NoFlagsSpecified{ ~0ULL }; // all ones +struct CommandFlagOptions{ + CommandFlagOptions() = default; + CommandFlagOptions && QuickTest() && + { quickTest = true; return std::move( *this ); } + + // If true, assume this is a cheap test to be done always. If false, the + // test may be skipped and the condition assumed to be unchanged since the + // last more comprehensive testing + bool quickTest = false; +}; + // Construct one statically to register (and reserve) a bit position in the set -// an associate it with a test function +// an associate it with a test function; those with quickTest = true are cheap +// to compute and always checked class ReservedCommandFlag : public CommandFlag { public: using Predicate = std::function< bool( const AudacityProject& ) >; - ReservedCommandFlag( const Predicate & ); + ReservedCommandFlag( const Predicate &predicate, + const CommandFlagOptions &options = {} ); }; // Widely used command flags, but this list need not be exhaustive. It may be From 3847b66638a89fba0b870087b7126fc9bb2bd6c1 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sat, 8 Jun 2019 23:23:43 -0400 Subject: [PATCH 05/23] MenuManager::GetUpdateFlags is now table-driven --- src/Menus.cpp | 186 ++++++-------------------------------------------- src/Menus.h | 2 - 2 files changed, 22 insertions(+), 166 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index 4929456bd..082b160f3 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -400,22 +400,6 @@ void MenuManager::OnUndoRedo( wxCommandEvent &evt ) UpdateMenus(); } -CommandFlag MenuManager::GetFocusedFrame(AudacityProject &project) -{ - wxWindow *w = wxWindow::FindFocus(); - - while (w) { - if (dynamic_cast(w)) { - return TrackPanelHasFocus; - } - - w = w->GetParent(); - } - - return AlwaysEnabledFlag; -} - - // Really means, some track is selected, that isn't a time track const auto TracksSelectedPred = [](const AudacityProject &project){ @@ -711,165 +695,39 @@ const ReservedCommandFlag CommandFlag MenuManager::GetUpdateFlags( bool checkActive ) { - auto &project = mProject; - // 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; + static CommandFlag lastFlags; - // if (auto focus = wxWindow::FindFocus()) { - auto &window = GetProjectFrame( project ); - if (wxWindow * focus = &window) { - while (focus && focus->GetParent()) - focus = focus->GetParent(); - if (focus && !static_cast(focus)->IsIconized()) - flags |= NotMinimizedFlag; + CommandFlag flags, quickFlags; + + const auto &options = Options(); + size_t ii = 0; + for ( const auto &predicate : RegisteredPredicates() ) { + if ( options[ii].quickTest ) { + quickFlags[ii] = true; + if( predicate( mProject ) ) + flags[ii] = true; + } + ++ii; } - // These flags are cheap to calculate. - auto gAudioIO = AudioIO::Get(); - if (!gAudioIO->IsAudioTokenActive(ProjectAudioIO::Get( project ) - .GetAudioIOToken())) - flags |= AudioIONotBusyFlag; - else - flags |= AudioIOBusyFlag; - - if( gAudioIO->IsPaused() ) - flags |= PausedFlag; - - // quick 'short-circuit' return. - if ( checkActive && !window.IsActive() ){ - const auto checkedFlags = - NotMinimizedFlag | AudioIONotBusyFlag | AudioIOBusyFlag | - PausedFlag; - // 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 - ); - - auto &undoManager = UndoManager::Get( project ); - - if (undoManager.UnsavedChanges() || - !ProjectFileIO::Get( project ).IsProjectSaved()) - flags |= UnsavedChangesFlag; - - if (!mLastEffect.empty()) - flags |= HasLastEffectFlag; - - auto &history = ProjectHistory::Get( project ); - if (history.UndoAvailable()) - flags |= UndoAvailableFlag; - - if (history.RedoAvailable()) - flags |= RedoAvailableFlag; - - if (ViewInfo::Get( project ).ZoomInAvailable() && - (flags & TracksExistFlag).any()) - flags |= ZoomInAvailableFlag; - - if (ViewInfo::Get( project ).ZoomOutAvailable() && - (flags & TracksExistFlag).any()) - flags |= ZoomOutAvailableFlag; - - flags |= GetFocusedFrame(project); - - const auto &playRegion = viewInfo.playRegion; - if (playRegion.Locked()) - flags |= PlayRegionLockedFlag; - else if (!playRegion.Empty()) - flags |= PlayRegionNotLockedFlag; - - if ( (flags & AudioIONotBusyFlag).any() ) { - if ( (flags & TimeSelectedFlag).any() ) { - if ( (flags & TracksSelectedFlag).any() ) { - flags |= CutCopyAvailableFlag; - } + if ( checkActive && !GetProjectFrame( mProject ).IsActive() ) + // quick 'short-circuit' return. + flags = (lastFlags & ~quickFlags) | flags; + else { + ii = 0; + for ( const auto &predicate : RegisteredPredicates() ) { + if ( !options[ii].quickTest && predicate( mProject ) ) + flags[ii] = true; + ++ii; } } - 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; } diff --git a/src/Menus.h b/src/Menus.h index 280551629..46840d0b1 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -90,8 +90,6 @@ public: private: void OnUndoRedo( wxCommandEvent &evt ); - CommandFlag GetFocusedFrame(AudacityProject &project); - AudacityProject &mProject; // 0 is grey out, 1 is Autoselect, 2 is Give warnings. From a34f1cbba5deb86ebaf704042e750519c40b3503 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sat, 8 Jun 2019 23:48:42 -0400 Subject: [PATCH 06/23] Reimplement the rejection of select-all-on-none for certain commands... ... Specify it in the menu descriptions. Don't put special ad hoc logic in Menus.cpp. --- src/Menus.cpp | 38 +++++---------------------------- src/commands/CommandManager.cpp | 14 ++++++++++-- src/commands/CommandManager.h | 7 +++++- src/menus/EditMenus.cpp | 13 ++++++----- src/menus/FileMenus.cpp | 4 +++- 5 files changed, 34 insertions(+), 42 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index 082b160f3..9627193e9 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -863,42 +863,14 @@ void MenuManager::UpdateMenus( bool checkActive ) 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).none() || - (flags & TracksSelectedFlag).none ()) - { - commandManager.Enable(wxT("SplitCut"), false); - commandManager.Enable(wxT("SplitDelete"), false); - } - if ( (flags & WaveTracksSelectedFlag).none() ) - { - commandManager.Enable(wxT("Split"), false); - } - if ( (flags & TimeSelectedFlag).none() || - (flags & WaveTracksSelectedFlag).none() ) - { - commandManager.Enable(wxT("ExportSel"), false); - commandManager.Enable(wxT("SplitNew"), false); - } - if ( (flags & TimeSelectedFlag).none() || - (flags & AudioTracksSelectedFlag).none() ) - { - commandManager.Enable(wxT("Trim"), false); - } - } - -#if 0 - if ( (flags & CutCopyAvailableFlag).any() ) { - GetCommandManager()->Enable(wxT("Copy"), true); - GetCommandManager()->Enable(wxT("Cut"), true); - } -#endif + commandManager.EnableUsingFlags( + flags2, // the "lax" flags + (mWhatIfNoSelection == 0 ? flags2 : flags), // the "strict" flags + NoFlagsSpecified + ); MenuManager::ModifyToolbarMenus(project); } diff --git a/src/commands/CommandManager.cpp b/src/commands/CommandManager.cpp index 7d1caeacc..13fe82ca7 100644 --- a/src/commands/CommandManager.cpp +++ b/src/commands/CommandManager.cpp @@ -542,6 +542,7 @@ void CommandManager::AddItem(const CommandID &name, hasDialog, options.accel, CurrentMenu(), finder, callback, {}, 0, 0, options.bIsEffect, cookedParameter); + entry->useStrictFlags = options.useStrictFlags; int ID = entry->id; wxString label = GetLabelWithDisabledAccel(entry); @@ -955,17 +956,26 @@ void CommandManager::Enable(const wxString &name, bool enabled) Enable(entry, enabled); } -void CommandManager::EnableUsingFlags(CommandFlag flags, CommandMask mask) +void CommandManager::EnableUsingFlags( + CommandFlag flags, CommandFlag strictFlags, CommandMask mask) { + // strictFlags are a subset of flags. strictFlags represent the real + // conditions now, but flags are the conditions that could be made true. + // Some commands use strict flags only, refusing the chance to fix + // conditions + wxASSERT( (strictFlags & ~flags).none() ); + for(const auto &entry : mCommandList) { if (entry->multi && entry->index != 0) continue; if( entry->isOccult ) continue; + auto useFlags = entry->useStrictFlags ? strictFlags : flags; + auto combinedMask = (mask & entry->mask); if (combinedMask.any()) { - bool enable = ((flags & combinedMask) == + bool enable = ((useFlags & combinedMask) == (entry->flags & combinedMask)); Enable(entry.get(), enable); } diff --git a/src/commands/CommandManager.h b/src/commands/CommandManager.h index 792d0b020..a122d1c28 100644 --- a/src/commands/CommandManager.h +++ b/src/commands/CommandManager.h @@ -82,6 +82,7 @@ struct CommandListEntry bool hasDialog; CommandFlag flags; CommandMask mask; + bool useStrictFlags{ false }; }; using MenuBarList = std::vector < MenuBarListEntry >; @@ -164,6 +165,8 @@ class AUDACITY_DLL_API CommandManager final { longName = value; return std::move(*this); } Options &&IsGlobal () && { global = true; return std::move(*this); } + Options &&UseStrictFlags () && + { useStrictFlags = true; return std::move(*this); } const wxChar *accel{ wxT("") }; int check{ -1 }; // default value means it's not a check item @@ -172,6 +175,7 @@ class AUDACITY_DLL_API CommandManager final CommandMask mask{ NoFlagsSpecified }; wxString longName{}; // translated bool global{ false }; + bool useStrictFlags{ false }; }; void AddItemList(const CommandID & name, @@ -218,7 +222,8 @@ class AUDACITY_DLL_API CommandManager final // Modifying menus // - void EnableUsingFlags(CommandFlag flags, CommandMask mask); + void EnableUsingFlags( + CommandFlag flags, CommandFlag strictFlags, CommandMask mask); void Enable(const wxString &name, bool enabled); void Check(const CommandID &name, bool checked); void Modify(const wxString &name, const wxString &newLabel); diff --git a/src/menus/EditMenus.cpp b/src/menus/EditMenus.cpp index 0ce0641e3..3194c6c3a 100644 --- a/src/menus/EditMenus.cpp +++ b/src/menus/EditMenus.cpp @@ -1156,10 +1156,12 @@ MenuTable::BaseItemPtr EditMenu( AudacityProject & ) Menu( _("R&emove Special"), /* i18n-hint: (verb) Do a special kind of cut*/ Command( wxT("SplitCut"), XXO("Spl&it Cut"), FN(OnSplitCut), - NotBusyTimeAndTracksFlags, wxT("Ctrl+Alt+X") ), + NotBusyTimeAndTracksFlags, + Options{ wxT("Ctrl+Alt+X") }.UseStrictFlags() ), /* i18n-hint: (verb) Do a special kind of DELETE*/ Command( wxT("SplitDelete"), XXO("Split D&elete"), FN(OnSplitDelete), - NotBusyTimeAndTracksFlags, wxT("Ctrl+Alt+K") ), + NotBusyTimeAndTracksFlags, + Options{ wxT("Ctrl+Alt+K") }.UseStrictFlags() ), Separator(), @@ -1170,7 +1172,7 @@ MenuTable::BaseItemPtr EditMenu( AudacityProject & ) /* i18n-hint: (verb)*/ Command( wxT("Trim"), XXO("Tri&m Audio"), FN(OnTrim), AudioIONotBusyFlag | TimeSelectedFlag | AudioTracksSelectedFlag, - wxT("Ctrl+T") ) + Options{ wxT("Ctrl+T") }.UseStrictFlags() ) ), Separator(), @@ -1180,10 +1182,11 @@ MenuTable::BaseItemPtr EditMenu( AudacityProject & ) Menu( _("Clip B&oundaries"), /* i18n-hint: (verb) It's an item on a menu. */ Command( wxT("Split"), XXO("Sp&lit"), FN(OnSplit), - AudioIONotBusyFlag | WaveTracksSelectedFlag, wxT("Ctrl+I") ), + AudioIONotBusyFlag | WaveTracksSelectedFlag, + Options{ wxT("Ctrl+I") }.UseStrictFlags() ), Command( wxT("SplitNew"), XXO("Split Ne&w"), FN(OnSplitNew), AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag, - wxT("Ctrl+Alt+I") ), + Options{ wxT("Ctrl+Alt+I") }.UseStrictFlags() ), Separator(), diff --git a/src/menus/FileMenus.cpp b/src/menus/FileMenus.cpp index 87a21699c..20b03e8cc 100644 --- a/src/menus/FileMenus.cpp +++ b/src/menus/FileMenus.cpp @@ -589,6 +589,7 @@ static CommandHandlerObject &findCommandHandler(AudacityProject &) { MenuTable::BaseItemPtr FileMenu( AudacityProject& ) { using namespace MenuTable; + using Options = CommandManager::Options; return Menu( _("&File"), /*i18n-hint: "New" is an action (verb) to create a NEW project*/ @@ -683,7 +684,8 @@ MenuTable::BaseItemPtr FileMenu( AudacityProject& ) // Enable Export Selection commands only when there's a selection. Command( wxT("ExportSel"), XXO("Expo&rt Selected Audio..."), FN(OnExportSelection), - AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag ), + AudioIONotBusyFlag | TimeSelectedFlag | WaveTracksSelectedFlag, + Options{}.UseStrictFlags() ), Command( wxT("ExportLabels"), XXO("Export &Labels..."), FN(OnExportLabels), From 64650b9a550b6e45240232e224aff0446bfc95fd Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 13 Jun 2019 16:55:01 -0400 Subject: [PATCH 07/23] Third argument of EnableUsingFlags was always all-ones; eliminate it --- src/Menus.cpp | 3 +-- src/commands/CommandManager.cpp | 9 ++++----- src/commands/CommandManager.h | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index 9627193e9..b6924982d 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -868,8 +868,7 @@ void MenuManager::UpdateMenus( bool checkActive ) // 0 is grey out, 1 is Autoselect, 2 is Give warnings. commandManager.EnableUsingFlags( flags2, // the "lax" flags - (mWhatIfNoSelection == 0 ? flags2 : flags), // the "strict" flags - NoFlagsSpecified + (mWhatIfNoSelection == 0 ? flags2 : flags) // the "strict" flags ); MenuManager::ModifyToolbarMenus(project); diff --git a/src/commands/CommandManager.cpp b/src/commands/CommandManager.cpp index 13fe82ca7..f0d5bc35a 100644 --- a/src/commands/CommandManager.cpp +++ b/src/commands/CommandManager.cpp @@ -957,7 +957,7 @@ void CommandManager::Enable(const wxString &name, bool enabled) } void CommandManager::EnableUsingFlags( - CommandFlag flags, CommandFlag strictFlags, CommandMask mask) + CommandFlag flags, CommandFlag strictFlags) { // strictFlags are a subset of flags. strictFlags represent the real // conditions now, but flags are the conditions that could be made true. @@ -973,10 +973,9 @@ void CommandManager::EnableUsingFlags( auto useFlags = entry->useStrictFlags ? strictFlags : flags; - auto combinedMask = (mask & entry->mask); - if (combinedMask.any()) { - bool enable = ((useFlags & combinedMask) == - (entry->flags & combinedMask)); + if (entry->mask.any()) { + bool enable = ((useFlags & entry->mask) == + (entry->flags & entry->mask)); Enable(entry.get(), enable); } } diff --git a/src/commands/CommandManager.h b/src/commands/CommandManager.h index a122d1c28..47c845211 100644 --- a/src/commands/CommandManager.h +++ b/src/commands/CommandManager.h @@ -223,7 +223,7 @@ class AUDACITY_DLL_API CommandManager final // void EnableUsingFlags( - CommandFlag flags, CommandFlag strictFlags, CommandMask mask); + CommandFlag flags, CommandFlag strictFlags); void Enable(const wxString &name, bool enabled); void Check(const CommandID &name, bool checked); void Modify(const wxString &name, const wxString &newLabel); From 0f2278d394326d4b682097946dfde88cf8ad5a43 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 13 Jun 2019 16:56:05 -0400 Subject: [PATCH 08/23] Eliminate the confusing mask from CommandManager entries... ... In fact it was only ever different from flags when flags had the special NoAutoSelect and mask did not. Now put that bit in the mask too, and make the special NoAutoSelect always true in MenuManager::GetUpdateFlags(). This still preserves the intended effects of NoAutoSelect. --- src/Menus.cpp | 2 +- src/commands/CommandManager.cpp | 26 +++++++++----------------- src/commands/CommandManager.h | 6 +----- src/menus/EditMenus.cpp | 10 ++++------ 4 files changed, 15 insertions(+), 29 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index b6924982d..061e76623 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -689,7 +689,7 @@ const ReservedCommandFlag } }, NoAutoSelect{ - [](const AudacityProject &){ return false; } + [](const AudacityProject &){ return true; } } // jkc ; diff --git a/src/commands/CommandManager.cpp b/src/commands/CommandManager.cpp index f0d5bc35a..ee0048d2e 100644 --- a/src/commands/CommandManager.cpp +++ b/src/commands/CommandManager.cpp @@ -525,10 +525,6 @@ void CommandManager::AddItem(const CommandID &name, wxASSERT( flags != NoFlagsSpecified ); - auto mask = options.mask; - if (mask == NoFlagsSpecified) - mask = flags; - CommandParameter cookedParameter; const auto ¶meter = options.parameter; if( parameter.empty() ) @@ -546,7 +542,7 @@ void CommandManager::AddItem(const CommandID &name, int ID = entry->id; wxString label = GetLabelWithDisabledAccel(entry); - SetCommandFlags(name, flags, mask); + SetCommandFlags(name, flags); auto checkmark = options.check; @@ -589,7 +585,7 @@ void CommandManager::AddItemList(const CommandID & name, i, cnt, bIsEffect); - entry->mask = entry->flags = flags; + entry->flags = flags; CurrentMenu()->Append(entry->id, GetLabel(entry)); mbSeparatorAllowed = true; } @@ -618,7 +614,7 @@ void CommandManager::AddCommand(const CommandID &name, NewIdentifier(name, label_in, label_in, false, accel, NULL, finder, callback, {}, 0, 0, false, {}); - SetCommandFlags(name, flags, flags); + SetCommandFlags(name, flags); } void CommandManager::AddGlobalCommand(const CommandID &name, @@ -635,7 +631,6 @@ void CommandManager::AddGlobalCommand(const CommandID &name, entry->enabled = false; entry->isGlobal = true; entry->flags = AlwaysEnabledFlag; - entry->mask = AlwaysEnabledFlag; } void CommandManager::AddSeparator() @@ -764,7 +759,7 @@ CommandListEntry *CommandManager::NewIdentifier(const CommandID & nameIn, entry->multi = multi; entry->index = index; entry->count = count; - entry->flags = entry->mask = AlwaysEnabledFlag; + entry->flags = AlwaysEnabledFlag; entry->enabled = true; entry->skipKeydown = (accel.Find(wxT("\tskipKeydown")) != wxNOT_FOUND); entry->wantKeyup = (accel.Find(wxT("\twantKeyup")) != wxNOT_FOUND) || entry->skipKeydown; @@ -973,9 +968,8 @@ void CommandManager::EnableUsingFlags( auto useFlags = entry->useStrictFlags ? strictFlags : flags; - if (entry->mask.any()) { - bool enable = ((useFlags & entry->mask) == - (entry->flags & entry->mask)); + if (entry->flags.any()) { + bool enable = ((useFlags & entry->flags) == entry->flags); Enable(entry.get(), enable); } } @@ -1265,7 +1259,7 @@ bool CommandManager::HandleCommandEntry(const CommandListEntry * entry, auto proj = GetActiveProject(); - auto combinedMask = (mask & entry->mask); + auto combinedMask = (mask & entry->flags); if (combinedMask.any()) { wxASSERT( proj ); @@ -1621,13 +1615,11 @@ void CommandManager::EndOccultCommands() } void CommandManager::SetCommandFlags(const CommandID &name, - CommandFlag flags, CommandMask mask) + CommandFlag flags) { CommandListEntry *entry = mCommandNameHash[name]; - if (entry) { + if (entry) entry->flags = flags; - entry->mask = mask; - } } #if defined(__WXDEBUG__) diff --git a/src/commands/CommandManager.h b/src/commands/CommandManager.h index 47c845211..291909971 100644 --- a/src/commands/CommandManager.h +++ b/src/commands/CommandManager.h @@ -81,7 +81,6 @@ struct CommandListEntry bool isEffect; bool hasDialog; CommandFlag flags; - CommandMask mask; bool useStrictFlags{ false }; }; @@ -159,8 +158,6 @@ class AUDACITY_DLL_API CommandManager final { bIsEffect = true; return std::move(*this); } Options &&Parameter (const CommandParameter &value) && { parameter = value; return std::move(*this); } - Options &&Mask (CommandMask value) && - { mask = value; return std::move(*this); } Options &&LongName (const wxString &value) && { longName = value; return std::move(*this); } Options &&IsGlobal () && @@ -172,7 +169,6 @@ class AUDACITY_DLL_API CommandManager final int check{ -1 }; // default value means it's not a check item bool bIsEffect{ false }; CommandParameter parameter{}; - CommandMask mask{ NoFlagsSpecified }; wxString longName{}; // translated bool global{ false }; bool useStrictFlags{ false }; @@ -216,7 +212,7 @@ class AUDACITY_DLL_API CommandManager final void EndOccultCommands(); - void SetCommandFlags(const CommandID &name, CommandFlag flags, CommandMask mask); + void SetCommandFlags(const CommandID &name, CommandFlag flags); // // Modifying menus diff --git a/src/menus/EditMenus.cpp b/src/menus/EditMenus.cpp index 3194c6c3a..b82050116 100644 --- a/src/menus/EditMenus.cpp +++ b/src/menus/EditMenus.cpp @@ -1135,12 +1135,10 @@ MenuTable::BaseItemPtr EditMenu( AudacityProject & ) /* i18n-hint: (verb)*/ Command( wxT("Cut"), XXO("Cu&t"), FN(OnCut), AudioIONotBusyFlag | CutCopyAvailableFlag | NoAutoSelect, - Options{ wxT("Ctrl+X") } - .Mask( AudioIONotBusyFlag | CutCopyAvailableFlag ) ), + wxT("Ctrl+X") ), Command( wxT("Delete"), XXO("&Delete"), FN(OnDelete), AudioIONotBusyFlag | NoAutoSelect, - Options{ wxT("Ctrl+K") } - .Mask( AudioIONotBusyFlag ) ), + wxT("Ctrl+K") ), /* i18n-hint: (verb)*/ Command( wxT("Copy"), XXO("&Copy"), FN(OnCopy), AudioIONotBusyFlag | CutCopyAvailableFlag, wxT("Ctrl+C") ), @@ -1224,10 +1222,10 @@ MenuTable::BaseItemPtr ExtraEditMenu( AudacityProject & ) return Menu( _("&Edit"), Command( wxT("DeleteKey"), XXO("&Delete Key"), FN(OnDelete), (flags | NoAutoSelect), - Options{ wxT("Backspace") }.Mask( flags ) ), + wxT("Backspace") ), Command( wxT("DeleteKey2"), XXO("Delete Key&2"), FN(OnDelete), (flags | NoAutoSelect), - Options{ wxT("Delete") }.Mask( flags ) ) + wxT("Delete") ) ); } From ca5259712c52f2cafa41aad3fdcb7ad876dd420d Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 9 Jun 2019 02:02:36 -0400 Subject: [PATCH 09/23] HandleCommandEntry's 3rd parameter was two-valued, make it bool... ... false when it was NoFlagsSpecified, true when it was AlwaysEnabledFlag --- src/BatchCommands.cpp | 4 ++-- src/ProjectWindow.cpp | 2 +- src/commands/CommandManager.cpp | 23 +++++++++++------------ src/commands/CommandManager.h | 7 ++++--- src/commands/ScreenshotCommand.cpp | 4 ++-- src/toolbars/EditToolBar.cpp | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/BatchCommands.cpp b/src/BatchCommands.cpp index 5725b9d45..a6f0ad835 100644 --- a/src/BatchCommands.cpp +++ b/src/BatchCommands.cpp @@ -778,7 +778,7 @@ bool MacroCommands::ApplyCommand( const wxString &friendlyCommand, AudacityProject *project = GetActiveProject(); auto &manager = CommandManager::Get( *project ); if( pContext ){ - if( manager.HandleTextualCommand( command, *pContext, AlwaysEnabledFlag, AlwaysEnabledFlag ) ) + if( manager.HandleTextualCommand( command, *pContext, AlwaysEnabledFlag, true ) ) return true; pContext->Status( wxString::Format( _("Your batch command of %s was not recognized."), friendlyCommand )); @@ -787,7 +787,7 @@ bool MacroCommands::ApplyCommand( const wxString &friendlyCommand, else { const CommandContext context( *GetActiveProject() ); - if( manager.HandleTextualCommand( command, context, AlwaysEnabledFlag, AlwaysEnabledFlag ) ) + if( manager.HandleTextualCommand( command, context, AlwaysEnabledFlag, true ) ) return true; } diff --git a/src/ProjectWindow.cpp b/src/ProjectWindow.cpp index e22ae3d49..0663da850 100644 --- a/src/ProjectWindow.cpp +++ b/src/ProjectWindow.cpp @@ -1513,7 +1513,7 @@ void ProjectWindow::OnMenu(wxCommandEvent & event) auto &commandManager = CommandManager::Get( project ); bool handled = commandManager.HandleMenuID( event.GetId(), MenuManager::Get( project ).GetUpdateFlags(), - NoFlagsSpecified); + false); if (handled) event.Skip(false); diff --git a/src/commands/CommandManager.cpp b/src/commands/CommandManager.cpp index ee0048d2e..2d1016103 100644 --- a/src/commands/CommandManager.cpp +++ b/src/commands/CommandManager.cpp @@ -1159,7 +1159,7 @@ bool CommandManager::FilterKeyEvent(AudacityProject *project, const wxKeyEvent & // LL: Why do they need to be disabled??? entry->enabled = false; auto cleanup = valueRestorer( entry->enabled, true ); - return HandleCommandEntry(entry, NoFlagsSpecified, NoFlagsSpecified, &evt); + return HandleCommandEntry(entry, NoFlagsSpecified, false, &evt); } wxWindow * pFocus = wxWindow::FindFocus(); @@ -1233,12 +1233,12 @@ bool CommandManager::FilterKeyEvent(AudacityProject *project, const wxKeyEvent & { return true; } - return HandleCommandEntry(entry, flags, NoFlagsSpecified, &temp); + return HandleCommandEntry(entry, flags, false, &temp); } if (type == wxEVT_KEY_UP && entry->wantKeyup) { - return HandleCommandEntry(entry, flags, NoFlagsSpecified, &temp); + return HandleCommandEntry(entry, flags, false, &temp); } return false; @@ -1249,7 +1249,7 @@ bool CommandManager::FilterKeyEvent(AudacityProject *project, const wxKeyEvent & ///the command won't be executed unless the flags are compatible ///with the command's flags. bool CommandManager::HandleCommandEntry(const CommandListEntry * entry, - CommandFlag flags, CommandMask mask, const wxEvent * evt) + CommandFlag flags, bool alwaysEnabled, const wxEvent * evt) { if (!entry ) return false; @@ -1259,8 +1259,7 @@ bool CommandManager::HandleCommandEntry(const CommandListEntry * entry, auto proj = GetActiveProject(); - auto combinedMask = (mask & entry->flags); - if (combinedMask.any()) { + if (!alwaysEnabled && entry->flags.any()) { wxASSERT( proj ); if( !proj ) @@ -1272,7 +1271,7 @@ bool CommandManager::HandleCommandEntry(const CommandListEntry * entry, // NB: The call may have the side effect of changing flags. bool allowed = MenuManager::Get(*proj).ReportIfActionNotAllowed( - NiceName, flags, entry->flags, combinedMask ); + NiceName, flags, entry->flags, entry->flags ); // If the function was disallowed, it STILL should count as having been // handled (by doing nothing or by telling the user of the problem). // Otherwise we may get other handlers having a go at obeying the command. @@ -1292,7 +1291,7 @@ bool CommandManager::HandleCommandEntry(const CommandListEntry * entry, ///CommandManagerListener function. If you pass any flags, ///the command won't be executed unless the flags are compatible ///with the command's flags. -bool CommandManager::HandleMenuID(int id, CommandFlag flags, CommandMask mask) +bool CommandManager::HandleMenuID(int id, CommandFlag flags, bool alwaysEnabled) { CommandListEntry *entry = mCommandNumericIDHash[id]; @@ -1300,13 +1299,13 @@ bool CommandManager::HandleMenuID(int id, CommandFlag flags, CommandMask mask) if (hook && hook(entry->name)) return true; - return HandleCommandEntry( entry, flags, mask ); + return HandleCommandEntry( entry, flags, alwaysEnabled ); } /// HandleTextualCommand() allows us a limitted version of script/batch /// behavior, since we can get from a string command name to the actual /// code to run. -bool CommandManager::HandleTextualCommand(const CommandID & Str, const CommandContext & context, CommandFlag flags, CommandMask mask) +bool CommandManager::HandleTextualCommand(const CommandID & Str, const CommandContext & context, CommandFlag flags, bool alwaysEnabled) { if( Str.empty() ) return false; @@ -1322,7 +1321,7 @@ bool CommandManager::HandleTextualCommand(const CommandID & Str, const CommandCo // sub-menu name) Str == entry->labelPrefix ) { - return HandleCommandEntry( entry.get(), flags, mask); + return HandleCommandEntry( entry.get(), flags, alwaysEnabled); } } else @@ -1330,7 +1329,7 @@ bool CommandManager::HandleTextualCommand(const CommandID & Str, const CommandCo // Handle multis too... if( Str == entry->name ) { - return HandleCommandEntry( entry.get(), flags, mask); + return HandleCommandEntry( entry.get(), flags, alwaysEnabled); } } } diff --git a/src/commands/CommandManager.h b/src/commands/CommandManager.h index 291909971..12707ab14 100644 --- a/src/commands/CommandManager.h +++ b/src/commands/CommandManager.h @@ -243,8 +243,8 @@ class AUDACITY_DLL_API CommandManager final // "permit" allows filtering even if the active window isn't a child of the project. // Lyrics and MixerTrackCluster classes use it. bool FilterKeyEvent(AudacityProject *project, const wxKeyEvent & evt, bool permit = false); - bool HandleMenuID(int id, CommandFlag flags, CommandMask mask); - bool HandleTextualCommand(const CommandID & Str, const CommandContext & context, CommandFlag flags, CommandMask mask); + bool HandleMenuID(int id, CommandFlag flags, bool alwaysEnabled); + bool HandleTextualCommand(const CommandID & Str, const CommandContext & context, CommandFlag flags, bool alwaysEnabled); // // Accessing @@ -343,7 +343,8 @@ private: // Executing commands // - bool HandleCommandEntry(const CommandListEntry * entry, CommandFlag flags, CommandMask mask, const wxEvent * evt = NULL); + bool HandleCommandEntry(const CommandListEntry * entry, CommandFlag flags, + bool alwaysEnabled, const wxEvent * evt = NULL); // // Modifying diff --git a/src/commands/ScreenshotCommand.cpp b/src/commands/ScreenshotCommand.cpp index 04207fff3..51aa901a8 100644 --- a/src/commands/ScreenshotCommand.cpp +++ b/src/commands/ScreenshotCommand.cpp @@ -454,7 +454,7 @@ void ScreenshotCommand::CapturePreferences( gPrefs->Flush(); CommandID Command{ wxT("Preferences") }; const CommandContext projectContext( *pProject ); - if( !commandManager.HandleTextualCommand( Command, projectContext, AlwaysEnabledFlag, AlwaysEnabledFlag ) ) + if( !commandManager.HandleTextualCommand( Command, projectContext, AlwaysEnabledFlag, true ) ) { // using GET in a log message for devs' eyes only wxLogDebug("Command %s not found", Command.GET() ); @@ -614,7 +614,7 @@ void ScreenshotCommand::CaptureCommands( SetIdleHandler( IdleHandler ); Str = Commands[i]; const CommandContext projectContext( *pProject ); - if( !manager.HandleTextualCommand( Str, projectContext, AlwaysEnabledFlag, AlwaysEnabledFlag ) ) + if( !manager.HandleTextualCommand( Str, projectContext, AlwaysEnabledFlag, true ) ) { wxLogDebug("Command %s not found", Str); } diff --git a/src/toolbars/EditToolBar.cpp b/src/toolbars/EditToolBar.cpp index 64d7ec325..098ee19e7 100644 --- a/src/toolbars/EditToolBar.cpp +++ b/src/toolbars/EditToolBar.cpp @@ -299,7 +299,7 @@ void EditToolBar::OnButton(wxCommandEvent &event) auto flags = MenuManager::Get(*p).GetUpdateFlags(); const CommandContext context( *p ); - cm.HandleTextualCommand(EditToolbarButtonList[id].commandName, context, flags, NoFlagsSpecified); + cm.HandleTextualCommand(EditToolbarButtonList[id].commandName, context, flags, false); } static RegisteredToolbarFactory factory{ EditBarID, From e5a786c420ffff4bfca79bcb4e59635157e97727 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 9 Jun 2019 02:05:39 -0400 Subject: [PATCH 10/23] 4th arg of ReportIfActionNotAllowed always == 3rd; eliminate it --- src/Menus.cpp | 6 +++--- src/Menus.h | 3 +-- src/commands/CommandManager.cpp | 2 +- src/effects/Effect.cpp | 1 - 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index 061e76623..52bff34c6 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -897,14 +897,14 @@ void MenuCreator::RebuildAllMenuBars() } bool MenuManager::ReportIfActionNotAllowed( - const wxString & Name, CommandFlag & flags, CommandFlag flagsRqd, CommandFlag mask ) + const wxString & Name, CommandFlag & flags, CommandFlag flagsRqd ) { auto &project = mProject; - bool bAllowed = TryToMakeActionAllowed( flags, flagsRqd, mask ); + bool bAllowed = TryToMakeActionAllowed( flags, flagsRqd, flagsRqd ); if( bAllowed ) return true; auto &cm = CommandManager::Get( project ); - cm.TellUserWhyDisallowed( Name, flags & mask, flagsRqd & mask); + cm.TellUserWhyDisallowed( Name, flags & flagsRqd, flagsRqd); return false; } diff --git a/src/Menus.h b/src/Menus.h index 46840d0b1..6a6ab56ce 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -81,8 +81,7 @@ public: // Command Handling bool ReportIfActionNotAllowed( - const wxString & Name, CommandFlag & flags, CommandFlag flagsRqd, - CommandFlag mask ); + const wxString & Name, CommandFlag & flags, CommandFlag flagsRqd ); bool TryToMakeActionAllowed( CommandFlag & flags, CommandFlag flagsRqd, CommandFlag mask ); diff --git a/src/commands/CommandManager.cpp b/src/commands/CommandManager.cpp index 2d1016103..76a6eb1b9 100644 --- a/src/commands/CommandManager.cpp +++ b/src/commands/CommandManager.cpp @@ -1271,7 +1271,7 @@ bool CommandManager::HandleCommandEntry(const CommandListEntry * entry, // NB: The call may have the side effect of changing flags. bool allowed = MenuManager::Get(*proj).ReportIfActionNotAllowed( - NiceName, flags, entry->flags, entry->flags ); + NiceName, flags, entry->flags ); // If the function was disallowed, it STILL should count as having been // handled (by doing nothing or by telling the user of the problem). // Otherwise we may get other handlers having a go at obeying the command. diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 4090cd362..bb22da2a2 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -3278,7 +3278,6 @@ void EffectUIHost::OnApply(wxCommandEvent & evt) MenuManager::Get(*mProject).ReportIfActionNotAllowed( mEffect->GetTranslatedName(), flags, - WaveTracksSelectedFlag | TimeSelectedFlag, WaveTracksSelectedFlag | TimeSelectedFlag); if (!allowed) return; From 94d9593df86cea75bbc3dc62820c41efb65406ed Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 9 Jun 2019 02:06:49 -0400 Subject: [PATCH 11/23] 3rd arg of TryToMakeActionAllows always == 2nd; eliminate it --- src/Menus.cpp | 14 +++++++------- src/Menus.h | 2 +- src/toolbars/ControlToolBar.cpp | 1 - 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index 52bff34c6..51cec1825 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -900,7 +900,7 @@ bool MenuManager::ReportIfActionNotAllowed( const wxString & Name, CommandFlag & flags, CommandFlag flagsRqd ) { auto &project = mProject; - bool bAllowed = TryToMakeActionAllowed( flags, flagsRqd, flagsRqd ); + bool bAllowed = TryToMakeActionAllowed( flags, flagsRqd ); if( bAllowed ) return true; auto &cm = CommandManager::Get( project ); @@ -913,7 +913,7 @@ bool MenuManager::ReportIfActionNotAllowed( /// If not, then try some recovery action to make it so. /// @return whether compatible or not after any actions taken. bool MenuManager::TryToMakeActionAllowed( - CommandFlag & flags, CommandFlag flagsRqd, CommandFlag mask ) + CommandFlag & flags, CommandFlag flagsRqd ) { auto &project = mProject; bool bAllowed; @@ -921,19 +921,19 @@ bool MenuManager::TryToMakeActionAllowed( if( flags.none() ) flags = GetUpdateFlags(); - bAllowed = ((flags & mask) == (flagsRqd & mask)); + bAllowed = ((flags & flagsRqd) == flagsRqd); if( bAllowed ) return true; // Why is action not allowed? // 1's wherever a required flag is missing. - auto MissingFlags = (~flags & flagsRqd) & mask; + auto MissingFlags = (~flags & flagsRqd); if( mStopIfWasPaused && (MissingFlags & AudioIONotBusyFlag ).any() ){ TransportActions::StopIfPaused( project ); // Hope this will now reflect stopped audio. flags = GetUpdateFlags(); - bAllowed = ((flags & mask) == (flagsRqd & mask)); + bAllowed = ((flags & flagsRqd) == flagsRqd); if( bAllowed ) return true; } @@ -949,7 +949,7 @@ bool MenuManager::TryToMakeActionAllowed( // Why is action still not allowed? // 0's wherever a required flag is missing (or is don't care) - MissingFlags = (flags & ~flagsRqd) & mask; + MissingFlags = (flags & ~flagsRqd) & flagsRqd; // IF selecting all audio won't do any good, THEN return with failure. if( (flags & WaveTracksExistFlag).none() ) @@ -967,6 +967,6 @@ bool MenuManager::TryToMakeActionAllowed( // So changed to DoSelectAllAudio. SelectActions::DoSelectAllAudio(project); flags = GetUpdateFlags(); - bAllowed = ((flags & mask) == (flagsRqd & mask)); + bAllowed = ((flags & flagsRqd) == flagsRqd); return bAllowed; } diff --git a/src/Menus.h b/src/Menus.h index 6a6ab56ce..e92dd7d93 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -83,7 +83,7 @@ public: bool ReportIfActionNotAllowed( const wxString & Name, CommandFlag & flags, CommandFlag flagsRqd ); bool TryToMakeActionAllowed( - CommandFlag & flags, CommandFlag flagsRqd, CommandFlag mask ); + CommandFlag & flags, CommandFlag flagsRqd ); private: diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index adcd3ae9e..b455f28f4 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -1082,7 +1082,6 @@ bool ControlToolBar::DoRecord(AudacityProject &project, // NB: The call may have the side effect of changing flags. bool allowed = MenuManager::Get(project).TryToMakeActionAllowed( flags, - AudioIONotBusyFlag | CanStopAudioStreamFlag, AudioIONotBusyFlag | CanStopAudioStreamFlag); if (!allowed) From 6c6d0b869ee8be591a7d87c06e7e1aae81334734 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 9 Jun 2019 02:09:48 -0400 Subject: [PATCH 12/23] So we prove that MissingFlags was always reassigned zero... ... so remove this piece of confused logic that did nothing --- src/Menus.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index 51cec1825..92dd43a44 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -947,19 +947,9 @@ bool MenuManager::TryToMakeActionAllowed( if( (flagsRqd & NoAutoSelect).any() ) return false; - // Why is action still not allowed? - // 0's wherever a required flag is missing (or is don't care) - MissingFlags = (flags & ~flagsRqd) & flagsRqd; - // IF selecting all audio won't do any good, THEN return with failure. if( (flags & WaveTracksExistFlag).none() ) 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 ) - ).any() ) - return false; // This was 'DoSelectSomething()'. // This made autoselect more confusing. From 9a4c18255e08b32f52e001c956817cbf4211b834 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 9 Jun 2019 02:13:47 -0400 Subject: [PATCH 13/23] Eliminate type alias CommandMask --- src/commands/CommandFlag.h | 1 - src/commands/CommandManager.cpp | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/CommandFlag.h b/src/commands/CommandFlag.h index d4694cbad..22a3a35ff 100644 --- a/src/commands/CommandFlag.h +++ b/src/commands/CommandFlag.h @@ -26,7 +26,6 @@ static_assert( // Type to specify conditions for enabling of a menu item using CommandFlag = std::bitset; -using CommandMask = CommandFlag; // Special constant values constexpr CommandFlag diff --git a/src/commands/CommandManager.cpp b/src/commands/CommandManager.cpp index 76a6eb1b9..cee0c5fd9 100644 --- a/src/commands/CommandManager.cpp +++ b/src/commands/CommandManager.cpp @@ -1021,7 +1021,8 @@ void CommandManager::SetKeyFromIndex(int i, const NormalizedKeyString &key) entry->key = key; } -void CommandManager::TellUserWhyDisallowed( const wxString & Name, CommandFlag flagsGot, CommandMask flagsRequired ) +void CommandManager::TellUserWhyDisallowed( + const wxString & Name, CommandFlag flagsGot, CommandFlag flagsRequired ) { // The default string for 'reason' is a catch all. I hope it won't ever be seen // and that we will get something more specific. From 76996bf0cd7e35c708b74ad7b16c71aa0f0ff3eb Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 13 Jun 2019 17:19:38 -0400 Subject: [PATCH 14/23] TryToMakeActionAllowed is also table driven... ... and also the logic for relaxing the conditions for enabling the menu items --- src/Menus.cpp | 142 +++++++++++++++++++++---------------- src/Menus.h | 1 + src/commands/CommandFlag.h | 22 ++++++ 3 files changed, 103 insertions(+), 62 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index 92dd43a44..0b1ecfca9 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -814,6 +814,55 @@ void MenuManager::ModifyToolbarMenus(AudacityProject &project) commandManager.Check(wxT("TypeToCreateLabel"), active); } +namespace +{ + using MenuItemEnablers = std::vector; + MenuItemEnablers &Enablers() + { + static MenuItemEnablers enablers; + return enablers; + } +} + +RegisteredMenuItemEnabler::RegisteredMenuItemEnabler( + const MenuItemEnabler &enabler ) +{ + Enablers().emplace_back( enabler ); +} + +RegisteredMenuItemEnabler stopIfPaused{{ + PausedFlag, + AudioIONotBusyFlag, + []( const AudacityProject &project ){ + return MenuManager::Get( project ).mStopIfWasPaused; }, + []( AudacityProject &project, CommandFlag ){ + if ( MenuManager::Get( project ).mStopIfWasPaused ) + TransportActions::StopIfPaused( project ); + } +}}; + +auto canSelectAll = [](const AudacityProject &project){ + return MenuManager::Get( project ).mWhatIfNoSelection != 0; }; +auto selectAll = []( AudacityProject &project, CommandFlag flagsRqd ){ + if ( MenuManager::Get( project ).mWhatIfNoSelection == 1 && + (flagsRqd & NoAutoSelect).none() ) + SelectActions::DoSelectAllAudio(project); +}; + +RegisteredMenuItemEnabler selectTracks{{ + TracksExistFlag, + TracksSelectedFlag, + canSelectAll, + selectAll +}}; + +RegisteredMenuItemEnabler selectWaveTracks{{ + WaveTracksExistFlag, + TimeSelectedFlag | WaveTracksSelectedFlag | CutCopyAvailableFlag, + canSelectAll, + selectAll +}}; + // checkActive is a temporary hack that should be removed as soon as we // get multiple effect preview working void MenuManager::UpdateMenus( bool checkActive ) @@ -827,6 +876,12 @@ void MenuManager::UpdateMenus( bool checkActive ) return; auto flags = GetUpdateFlags(checkActive); + // Return from this function if nothing's changed since + // the last time we were here. + if (flags == mLastFlags) + return; + mLastFlags = flags; + auto flags2 = flags; // We can enable some extra items if we have select-all-on-none. @@ -834,33 +889,15 @@ void MenuManager::UpdateMenus( bool checkActive ) //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).any() ) - { - flags2 |= TracksSelectedFlag; - if ( (flags & WaveTracksExistFlag).any() ) - { - flags2 |= TimeSelectedFlag - | WaveTracksSelectedFlag - | CutCopyAvailableFlag; - } - } - } - if( mStopIfWasPaused ) - { - if( (flags & PausedFlag).any() ){ - flags2 |= AudioIONotBusyFlag; - } + for ( const auto &enabler : Enablers() ) { + if ( + enabler.applicable( project ) && + (flags & enabler.actualFlags) == enabler.actualFlags + ) + flags2 |= enabler.possibleFlags; } - // 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 ); // With select-all-on-none, some items that we don't want enabled may have @@ -908,7 +945,6 @@ bool MenuManager::ReportIfActionNotAllowed( 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. @@ -921,42 +957,24 @@ bool MenuManager::TryToMakeActionAllowed( if( flags.none() ) flags = GetUpdateFlags(); - bAllowed = ((flags & flagsRqd) == flagsRqd); - if( bAllowed ) - return true; - - // Why is action not allowed? - // 1's wherever a required flag is missing. - auto MissingFlags = (~flags & flagsRqd); - - if( mStopIfWasPaused && (MissingFlags & AudioIONotBusyFlag ).any() ){ - TransportActions::StopIfPaused( project ); - // Hope this will now reflect stopped audio. - flags = GetUpdateFlags(); - bAllowed = ((flags & flagsRqd) == flagsRqd); - if( bAllowed ) - return true; + // Visit the table of recovery actions + auto &enablers = Enablers(); + auto iter = enablers.begin(), end = enablers.end(); + while ((flags & flagsRqd) != flagsRqd && iter != end) { + const auto &enabler = *iter; + auto MissingFlags = (~flags & flagsRqd); + if ( + // Do we have the right precondition? + (flags & enabler.actualFlags) == enabler.actualFlags + && + // Can we get the condition we need? + (MissingFlags & enabler.possibleFlags) == MissingFlags + ) { + // Then try the function + enabler.tryEnable( project, flagsRqd ); + flags = GetUpdateFlags(); + } + ++iter; } - - //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).any() ) - return false; - - // IF selecting all audio won't do any good, THEN return with failure. - if( (flags & WaveTracksExistFlag).none() ) - 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 = GetUpdateFlags(); - bAllowed = ((flags & flagsRqd) == flagsRqd); - return bAllowed; + return (flags & flagsRqd) == flagsRqd; } diff --git a/src/Menus.h b/src/Menus.h index e92dd7d93..e1248e8c8 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -91,6 +91,7 @@ private: AudacityProject &mProject; +public: // 0 is grey out, 1 is Autoselect, 2 is Give warnings. int mWhatIfNoSelection; bool mStopIfWasPaused; diff --git a/src/commands/CommandFlag.h b/src/commands/CommandFlag.h index 22a3a35ff..d2f277deb 100644 --- a/src/commands/CommandFlag.h +++ b/src/commands/CommandFlag.h @@ -94,4 +94,26 @@ extern const ReservedCommandFlag NoAutoSelect // jkc ; +// To describe auto-selection, stop-if-paused, etc.: +// A structure describing a set of conditions, another set that might be +// made true given the first, and the function that may make them true. +// If a menu item requires the second set, while the first set is true, +// then the enabler will be invoked (unless the menu item is constructed with +// the useStrictFlags option, or the applicability test first returns false). +// The item's full set of required flags is passed to the function. +struct MenuItemEnabler { + using Test = std::function< bool( const AudacityProject& ) >; + using Action = std::function< void( AudacityProject&, CommandFlag ) >; + + const CommandFlag &actualFlags; + const CommandFlag &possibleFlags; + Test applicable; + Action tryEnable; +}; + +// Typically this is statically constructed: +struct RegisteredMenuItemEnabler{ + RegisteredMenuItemEnabler( const MenuItemEnabler &enabler ); +}; + #endif From f9b0281b477ee38c66b8f29ef101420e5915243c Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Fri, 14 Jun 2019 08:35:36 -0400 Subject: [PATCH 15/23] Move TellUserWhyDisallowed into MenuManager... ... so we don't have MenuManager calling CommandManager which calls it back. --- src/Menus.cpp | 70 ++++++++++++++++++++++++++++++++- src/Menus.h | 3 ++ src/commands/CommandManager.cpp | 68 -------------------------------- src/commands/CommandManager.h | 1 - 4 files changed, 72 insertions(+), 70 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index 0b1ecfca9..aaad190da 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -54,6 +54,7 @@ #include "prefs/TracksPrefs.h" #include "toolbars/ControlToolBar.h" #include "toolbars/ToolManager.h" +#include "widgets/ErrorDialog.h" #include #include @@ -941,7 +942,7 @@ bool MenuManager::ReportIfActionNotAllowed( if( bAllowed ) return true; auto &cm = CommandManager::Get( project ); - cm.TellUserWhyDisallowed( Name, flags & flagsRqd, flagsRqd); + TellUserWhyDisallowed( Name, flags & flagsRqd, flagsRqd); return false; } @@ -978,3 +979,70 @@ bool MenuManager::TryToMakeActionAllowed( } return (flags & flagsRqd) == flagsRqd; } + +void MenuManager::TellUserWhyDisallowed( + const wxString & Name, CommandFlag flagsGot, CommandFlag flagsRequired ) +{ + // The default string for 'reason' is a catch all. I hope it won't ever be seen + // and that we will get something more specific. + wxString reason = _("There was a problem with your last action. If you think\nthis is a bug, please tell us exactly where it occurred."); + // The default title string is 'Disallowed'. + wxString title = _("Disallowed"); + wxString helpPage; + + auto missingFlags = flagsRequired & ~flagsGot; + if( (missingFlags & AudioIONotBusyFlag).any() ) + // This reason will not be shown, because options that require it will be greyed our. + reason = _("You can only do this when playing and recording are\nstopped. (Pausing is not sufficient.)"); + else if( (missingFlags & StereoRequiredFlag).any() ) + // This reason will not be shown, because the stereo-to-mono is greyed out if not allowed. + reason = _("You must first select some stereo audio to perform this\naction. (You cannot use this with mono.)"); + // In reporting the issue with cut or copy, we don't tell the user they could also select some text in a label. + else if( ( + ( missingFlags & TimeSelectedFlag ) | + ( missingFlags & CutCopyAvailableFlag ) + ).any() ){ + title = _("No Audio Selected"); +#ifdef EXPERIMENTAL_DA + // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. + reason = wxString::Format( _("You must first select some audio for '%s' to act on.\n\nCtrl + A selects all audio."), Name ); +#else +#ifdef __WXMAC__ + // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. + reason = wxString::Format( _("Select the audio for %s to use (for example, Cmd + A to Select All) then try again." + // No need to explain what a help button is for. + // "\n\nClick the Help button to learn more about selection methods." + ), Name ); + +#else + // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. + reason = wxString::Format( _("Select the audio for %s to use (for example, Ctrl + A to Select All) then try again." + // No need to explain what a help button is for. + // "\n\nClick the Help button to learn more about selection methods." + ), Name ); +#endif +#endif + helpPage = "Selecting_Audio_-_the_basics"; + } + else if( (missingFlags & WaveTracksSelectedFlag).any() ) + reason = _("You must first select some audio to perform this action.\n(Selecting other kinds of track won't work.)"); + else if ( (missingFlags & TracksSelectedFlag).any() ) + // i18n-hint: %s will be replaced by the name of an action, such as "Remove Tracks". + reason = wxString::Format(_("\"%s\" requires one or more tracks to be selected."), Name); + // If the only thing wrong was no tracks, we do nothing and don't report a problem + else if( missingFlags == TracksExistFlag ) + return; + // Likewise return if it was just no tracks, and track panel did not have focus. (e.g. up-arrow to move track) + else if( missingFlags == (TracksExistFlag | TrackPanelHasFocus) ) + return; + // Likewise as above too... + else if( missingFlags == TrackPanelHasFocus ) + return; + + // Does not have the warning icon... + ShowErrorDialog( + NULL, + title, + reason, + helpPage); +} diff --git a/src/Menus.h b/src/Menus.h index e1248e8c8..85f6a182b 100644 --- a/src/Menus.h +++ b/src/Menus.h @@ -87,6 +87,9 @@ public: private: + void TellUserWhyDisallowed(const wxString & Name, CommandFlag flagsGot, + CommandFlag flagsRequired); + void OnUndoRedo( wxCommandEvent &evt ); AudacityProject &mProject; diff --git a/src/commands/CommandManager.cpp b/src/commands/CommandManager.cpp index cee0c5fd9..babad283a 100644 --- a/src/commands/CommandManager.cpp +++ b/src/commands/CommandManager.cpp @@ -101,7 +101,6 @@ CommandManager. It holds the callback for one command. #include "../effects/EffectManager.h" #include "../widgets/LinkingHtmlWindow.h" #include "../widgets/AudacityMessageBox.h" -#include "../widgets/ErrorDialog.h" #include "../widgets/HelpSystem.h" @@ -1021,73 +1020,6 @@ void CommandManager::SetKeyFromIndex(int i, const NormalizedKeyString &key) entry->key = key; } -void CommandManager::TellUserWhyDisallowed( - const wxString & Name, CommandFlag flagsGot, CommandFlag flagsRequired ) -{ - // The default string for 'reason' is a catch all. I hope it won't ever be seen - // and that we will get something more specific. - wxString reason = _("There was a problem with your last action. If you think\nthis is a bug, please tell us exactly where it occurred."); - // The default title string is 'Disallowed'. - wxString title = _("Disallowed"); - wxString helpPage; - - auto missingFlags = flagsRequired & ~flagsGot; - if( (missingFlags & AudioIONotBusyFlag).any() ) - // This reason will not be shown, because options that require it will be greyed our. - reason = _("You can only do this when playing and recording are\nstopped. (Pausing is not sufficient.)"); - else if( (missingFlags & StereoRequiredFlag).any() ) - // This reason will not be shown, because the stereo-to-mono is greyed out if not allowed. - reason = _("You must first select some stereo audio to perform this\naction. (You cannot use this with mono.)"); - // In reporting the issue with cut or copy, we don't tell the user they could also select some text in a label. - else if( ( - ( missingFlags & TimeSelectedFlag ) | - ( missingFlags & CutCopyAvailableFlag ) - ).any() ){ - title = _("No Audio Selected"); -#ifdef EXPERIMENTAL_DA - // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. - reason = wxString::Format( _("You must first select some audio for '%s' to act on.\n\nCtrl + A selects all audio."), Name ); -#else -#ifdef __WXMAC__ - // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. - reason = wxString::Format( _("Select the audio for %s to use (for example, Cmd + A to Select All) then try again." - // No need to explain what a help button is for. - // "\n\nClick the Help button to learn more about selection methods." - ), Name ); - -#else - // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. - reason = wxString::Format( _("Select the audio for %s to use (for example, Ctrl + A to Select All) then try again." - // No need to explain what a help button is for. - // "\n\nClick the Help button to learn more about selection methods." - ), Name ); -#endif -#endif - helpPage = "Selecting_Audio_-_the_basics"; - } - else if( (missingFlags & WaveTracksSelectedFlag).any() ) - reason = _("You must first select some audio to perform this action.\n(Selecting other kinds of track won't work.)"); - else if ( (missingFlags & TracksSelectedFlag).any() ) - // i18n-hint: %s will be replaced by the name of an action, such as "Remove Tracks". - reason = wxString::Format(_("\"%s\" requires one or more tracks to be selected."), Name); - // If the only thing wrong was no tracks, we do nothing and don't report a problem - else if( missingFlags == TracksExistFlag ) - return; - // Likewise return if it was just no tracks, and track panel did not have focus. (e.g. up-arrow to move track) - else if( missingFlags == (TracksExistFlag | TrackPanelHasFocus) ) - return; - // Likewise as above too... - else if( missingFlags == TrackPanelHasFocus ) - return; - - // Does not have the warning icon... - ShowErrorDialog( - NULL, - title, - reason, - helpPage); -} - wxString CommandManager::DescribeCommandsAndShortcuts (const TranslatedInternalString commands[], size_t nCommands) const { diff --git a/src/commands/CommandManager.h b/src/commands/CommandManager.h index 12707ab14..986175e35 100644 --- a/src/commands/CommandManager.h +++ b/src/commands/CommandManager.h @@ -286,7 +286,6 @@ class AUDACITY_DLL_API CommandManager final // void WriteXML(XMLWriter &xmlFile) const /* not override */; - void TellUserWhyDisallowed(const wxString & Name, CommandFlag flagsGot, CommandFlag flagsRequired); /// /// Formatting summaries that include shortcut keys From b09a1af5644938532444fcf256ff7a2bc465f76e Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Fri, 14 Jun 2019 10:14:56 -0400 Subject: [PATCH 16/23] Separate the flags mentioned in TellUserWhyDisallowed --- src/Menus.cpp | 94 +++++++++++++++++++------------------- src/commands/CommandFlag.h | 17 +++---- 2 files changed, 57 insertions(+), 54 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index aaad190da..cb37a8769 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -454,28 +454,68 @@ const ReservedCommandFlag return !AudioIOBusyPred( project ); }, CommandFlagOptions{}.QuickTest() - }, + }, //lll + StereoRequiredFlag{ + [](const AudacityProject &project){ + // True iff at least one stereo track is selected, i.e., at least + // one right channel is selected. + // TODO: more-than-two-channels + auto range = TrackList::Get( project ).Selected() + - &Track::IsLeader; + return !range.empty(); + } + }, //lda TimeSelectedFlag{ TimeSelectedPred }, - TracksSelectedFlag{ - TracksSelectedPred + CutCopyAvailableFlag{ + [](const AudacityProject &project){ + auto range = TrackList::Get( project ).Any() + + &LabelTrack::IsTextSelected; + if ( !range.empty() ) + return true; + + if ( + !AudioIOBusyPred( project ) + && + TimeSelectedPred( project ) + && + TracksSelectedPred( project ) + ) + return true; + + return false; + } + }, + WaveTracksSelectedFlag{ + [](const AudacityProject &project){ + return !TrackList::Get( project ).Selected().empty(); + } }, TracksExistFlag{ [](const AudacityProject &project){ return !TrackList::Get( project ).Any().empty(); } }, + TracksSelectedFlag{ + TracksSelectedPred + }, + TrackPanelHasFocus{ + [](const AudacityProject &project){ + for (auto w = wxWindow::FindFocus(); w; w = w->GetParent()) { + if (dynamic_cast(w)) + return true; + } + return false; + } + }; //lll + +const ReservedCommandFlag LabelTracksExistFlag{ [](const AudacityProject &project){ return !TrackList::Get( project ).Any().empty(); } }, - WaveTracksSelectedFlag{ - [](const AudacityProject &project){ - return !TrackList::Get( project ).Selected().empty(); - } - }, UnsavedChangesFlag{ [](const AudacityProject &project){ auto &undoManager = UndoManager::Get( project ); @@ -519,25 +559,6 @@ const ReservedCommandFlag ; } }, - StereoRequiredFlag{ - [](const AudacityProject &project){ - // True iff at least one stereo track is selected, i.e., at least - // one right channel is selected. - // TODO: more-than-two-channels - auto range = TrackList::Get( project ).Selected() - - &Track::IsLeader; - return !range.empty(); - } - }, //lda - TrackPanelHasFocus{ - [](const AudacityProject &project){ - for (auto w = wxWindow::FindFocus(); w; w = w->GetParent()) { - if (dynamic_cast(w)) - return true; - } - return false; - } - }, //lll LabelsSelectedFlag{ [](const AudacityProject &project){ // At least one label track selected, having at least one label @@ -575,25 +596,6 @@ const ReservedCommandFlag return !playRegion.Locked() && !playRegion.Empty(); } }, //msmeyer - CutCopyAvailableFlag{ - [](const AudacityProject &project){ - auto range = TrackList::Get( project ).Any() - + &LabelTrack::IsTextSelected; - if ( !range.empty() ) - return true; - - if ( - !AudioIOBusyPred( project ) - && - TimeSelectedPred( project ) - && - TracksSelectedPred( project ) - ) - return true; - - return false; - } - }, WaveTracksExistFlag{ [](const AudacityProject &project){ return !TrackList::Get( project ).Any().empty(); diff --git a/src/commands/CommandFlag.h b/src/commands/CommandFlag.h index d2f277deb..def0dceb6 100644 --- a/src/commands/CommandFlag.h +++ b/src/commands/CommandFlag.h @@ -59,25 +59,26 @@ public: // ReservedCommandFlag values extern const ReservedCommandFlag - AudioIONotBusyFlag, + AudioIOBusyFlag, //lll + StereoRequiredFlag, //lda TimeSelectedFlag, // This is equivalent to check if there is a valid selection, so it's used for Zoom to Selection too - TracksSelectedFlag, - TracksExistFlag, - LabelTracksExistFlag, + CutCopyAvailableFlag, WaveTracksSelectedFlag, + TracksExistFlag, + TracksSelectedFlag, + TrackPanelHasFocus, //lll + + AudioIONotBusyFlag, + LabelTracksExistFlag, UnsavedChangesFlag, HasLastEffectFlag, UndoAvailableFlag, RedoAvailableFlag, ZoomInAvailableFlag, ZoomOutAvailableFlag, - StereoRequiredFlag, //lda - TrackPanelHasFocus, //lll LabelsSelectedFlag, - AudioIOBusyFlag, //lll PlayRegionLockedFlag, //msmeyer PlayRegionNotLockedFlag, //msmeyer - CutCopyAvailableFlag, WaveTracksExistFlag, NoteTracksExistFlag, //gsw NoteTracksSelectedFlag, //gsw From 1b329b0e363d903665b4b759fee9d4b486cbe9ec Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Fri, 14 Jun 2019 10:40:59 -0400 Subject: [PATCH 17/23] Error messages for disallowed commands are table-driven too --- src/Menus.cpp | 186 ++++++++++++++++++++++++++----------- src/commands/CommandFlag.h | 36 +++++++ 2 files changed, 166 insertions(+), 56 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index cb37a8769..bd333a40e 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -448,13 +448,62 @@ ReservedCommandFlag::ReservedCommandFlag( Options().emplace_back( options ); } +static const CommandFlagOptions cutCopyOptions{ +// In reporting the issue with cut or copy, we don't tell the user they could also select some text in a label. + []( const wxString &Name ) { + // PRL: These strings have hard-coded mention of a certain shortcut key, + // thus assuming the default shortcuts. That is questionable. + wxString format; +#ifdef EXPERIMENTAL_DA + // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. + format = _("You must first select some audio for '%s' to act on.\n\nCtrl + A selects all audio."); +#else +#ifdef __WXMAC__ + // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. + format = _("Select the audio for %s to use (for example, Cmd + A to Select All) then try again." + // No need to explain what a help button is for. + // "\n\nClick the Help button to learn more about selection methods." + ); + +#else + // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. + format = _("Select the audio for %s to use (for example, Ctrl + A to Select All) then try again." + // No need to explain what a help button is for. + // "\n\nClick the Help button to learn more about selection methods." + ); +#endif + return wxString::Format( format, Name ); +#endif + }, + "Selecting_Audio_-_the_basics", + XO("No Audio Selected") +}; + const ReservedCommandFlag + // This flag has higher priority than others for purposes of help messages. + // It was important to arrange that for reasons of breaking dependency + // cycles, giving more freedom in the choice of file in which to implement + // this flag. AudioIONotBusyFlag{ [](const AudacityProject &project ){ return !AudioIOBusyPred( project ); }, - CommandFlagOptions{}.QuickTest() - }, //lll + CommandFlagOptions{ []( const wxString& ) { return + // This reason will not be shown, because options that require it will be greyed out. + _("You can only do this when playing and recording are\nstopped. (Pausing is not sufficient.)"); + } } + .QuickTest() + .Priority( 1 ) + }; //lll + +const ReservedCommandFlag + // The sequence of these definitions has a minor significance in determining + // which user error message has precedence if more than one might apply, so + // they should be kept in this sequence in one .cpp file if it is important + // to preserve that behavior. If they are dispersed to more than one file, + // then the precedence will be unspecified. + // The ordering of the flags that only disable the default message is not + // significant. StereoRequiredFlag{ [](const AudacityProject &project){ // True iff at least one stereo track is selected, i.e., at least @@ -463,10 +512,15 @@ const ReservedCommandFlag auto range = TrackList::Get( project ).Selected() - &Track::IsLeader; return !range.empty(); - } + }, + { []( const wxString& ) { return + // This reason will not be shown, because the stereo-to-mono is greyed out if not allowed. + _("You must first select some stereo audio to perform this\naction. (You cannot use this with mono.)"); + } } }, //lda TimeSelectedFlag{ - TimeSelectedPred + TimeSelectedPred, + cutCopyOptions }, CutCopyAvailableFlag{ [](const AudacityProject &project){ @@ -485,20 +539,30 @@ const ReservedCommandFlag return true; return false; - } + }, + cutCopyOptions }, WaveTracksSelectedFlag{ [](const AudacityProject &project){ return !TrackList::Get( project ).Selected().empty(); - } + }, + { []( const wxString& ) { return + _("You must first select some audio to perform this action.\n(Selecting other kinds of track won't work.)"); + } } }, TracksExistFlag{ [](const AudacityProject &project){ return !TrackList::Get( project ).Any().empty(); - } + }, + CommandFlagOptions{}.DisableDefaultMessage() }, TracksSelectedFlag{ - TracksSelectedPred + TracksSelectedPred, + { []( const wxString &Name ){ return wxString::Format( + // i18n-hint: %s will be replaced by the name of an action, such as "Remove Tracks". + _("\"%s\" requires one or more tracks to be selected."), + Name + ); } } }, TrackPanelHasFocus{ [](const AudacityProject &project){ @@ -507,7 +571,8 @@ const ReservedCommandFlag return true; } return false; - } + }, + CommandFlagOptions{}.DisableDefaultMessage() }; //lll const ReservedCommandFlag @@ -987,60 +1052,69 @@ void MenuManager::TellUserWhyDisallowed( { // The default string for 'reason' is a catch all. I hope it won't ever be seen // and that we will get something more specific. - wxString reason = _("There was a problem with your last action. If you think\nthis is a bug, please tell us exactly where it occurred."); + auto reason = _("There was a problem with your last action. If you think\nthis is a bug, please tell us exactly where it occurred."); // The default title string is 'Disallowed'. - wxString title = _("Disallowed"); + auto untranslatedTitle = XO("Disallowed"); wxString helpPage; - auto missingFlags = flagsRequired & ~flagsGot; - if( (missingFlags & AudioIONotBusyFlag).any() ) - // This reason will not be shown, because options that require it will be greyed our. - reason = _("You can only do this when playing and recording are\nstopped. (Pausing is not sufficient.)"); - else if( (missingFlags & StereoRequiredFlag).any() ) - // This reason will not be shown, because the stereo-to-mono is greyed out if not allowed. - reason = _("You must first select some stereo audio to perform this\naction. (You cannot use this with mono.)"); - // In reporting the issue with cut or copy, we don't tell the user they could also select some text in a label. - else if( ( - ( missingFlags & TimeSelectedFlag ) | - ( missingFlags & CutCopyAvailableFlag ) - ).any() ){ - title = _("No Audio Selected"); -#ifdef EXPERIMENTAL_DA - // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. - reason = wxString::Format( _("You must first select some audio for '%s' to act on.\n\nCtrl + A selects all audio."), Name ); -#else -#ifdef __WXMAC__ - // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. - reason = wxString::Format( _("Select the audio for %s to use (for example, Cmd + A to Select All) then try again." - // No need to explain what a help button is for. - // "\n\nClick the Help button to learn more about selection methods." - ), Name ); + bool enableDefaultMessage = true; + bool defaultMessage = true; -#else - // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. - reason = wxString::Format( _("Select the audio for %s to use (for example, Ctrl + A to Select All) then try again." - // No need to explain what a help button is for. - // "\n\nClick the Help button to learn more about selection methods." - ), Name ); -#endif -#endif - helpPage = "Selecting_Audio_-_the_basics"; + auto doOption = [&](const CommandFlagOptions &options) { + if ( options.message ) { + reason = options.message( Name ); + defaultMessage = false; + if ( !options.title.empty() ) + untranslatedTitle = options.title; + helpPage = options.helpPage; + return true; + } + else { + enableDefaultMessage = + enableDefaultMessage && options.enableDefaultMessage; + return false; + } + }; + + const auto &alloptions = Options(); + auto missingFlags = flagsRequired & ~flagsGot; + + // Find greatest priority + unsigned priority = 0; + for ( const auto &options : alloptions ) + priority = std::max( priority, options.priority ); + + // Visit all unsatisfied conditions' options, by descending priority, + // stopping when we find a message + ++priority; + while( priority-- ) { + size_t ii = 0; + for ( const auto &options : alloptions ) { + if ( + priority == options.priority + && + missingFlags[ii] + && + doOption( options ) ) + goto done; + + ++ii; + } } - else if( (missingFlags & WaveTracksSelectedFlag).any() ) - reason = _("You must first select some audio to perform this action.\n(Selecting other kinds of track won't work.)"); - else if ( (missingFlags & TracksSelectedFlag).any() ) - // i18n-hint: %s will be replaced by the name of an action, such as "Remove Tracks". - reason = wxString::Format(_("\"%s\" requires one or more tracks to be selected."), Name); - // If the only thing wrong was no tracks, we do nothing and don't report a problem - else if( missingFlags == TracksExistFlag ) - return; - // Likewise return if it was just no tracks, and track panel did not have focus. (e.g. up-arrow to move track) - else if( missingFlags == (TracksExistFlag | TrackPanelHasFocus) ) - return; - // Likewise as above too... - else if( missingFlags == TrackPanelHasFocus ) + done: + + if ( + // didn't find a message + defaultMessage + && + // did find a condition that suppresses the default message + !enableDefaultMessage + ) return; + // Message is already translated but title is not yet + auto title = ::GetCustomTranslation( untranslatedTitle ); + // Does not have the warning icon... ShowErrorDialog( NULL, diff --git a/src/commands/CommandFlag.h b/src/commands/CommandFlag.h index def0dceb6..784dd5039 100644 --- a/src/commands/CommandFlag.h +++ b/src/commands/CommandFlag.h @@ -14,6 +14,7 @@ #include #include #include +#include class AudacityProject; @@ -33,9 +34,44 @@ constexpr CommandFlag NoFlagsSpecified{ ~0ULL }; // all ones struct CommandFlagOptions{ + // Supplied the translated name of the command, returns a translated + // error message + using MessageFormatter = std::function< wxString( const wxString& ) >; + CommandFlagOptions() = default; + CommandFlagOptions( + const MessageFormatter &message_, + const wxString &helpPage_ = {}, + const wxString &title_ = {} + ) : message{ message_ }, helpPage{ helpPage_ }, title{ title_ } + {} + CommandFlagOptions && QuickTest() && { quickTest = true; return std::move( *this ); } + CommandFlagOptions && DisableDefaultMessage() && + { enableDefaultMessage = false; return std::move( *this ); } + CommandFlagOptions && Priority( unsigned priority_ ) && + { priority = priority_; return std::move( *this ); } + + // null, or else computes non-default message for the dialog box when the + // condition is not satisfied for the selected command + MessageFormatter message; + + // Title and help page are used only if a message function is given + wxString helpPage; + + // Empty, or non-default title for the dialog box when the + // condition is not satisfied for the selected command + // This string must be given UN-translated. + wxString title; + + // Conditions with higher "priority" are preferred over others in choosing + // the help message + unsigned priority = 0; + + // If false, and no other condition with a message is unsatisfied, then + // display no dialog box at all when this condition is not satisfied + bool enableDefaultMessage = true; // If true, assume this is a cheap test to be done always. If false, the // test may be skipped and the condition assumed to be unchanged since the From 45436db166c11c99406600fabc190a513e31151a Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Fri, 14 Jun 2019 11:44:00 -0400 Subject: [PATCH 18/23] New files for CommonCommandFlags --- locale/POTFILES.in | 2 + mac/Audacity.xcodeproj/project.pbxproj | 12 +- src/CommonCommandFlags.cpp | 0 src/CommonCommandFlags.h | 0 src/Makefile.am | 2 + src/Makefile.in | 162 ++++++++++-------- win/Projects/Audacity/Audacity.vcxproj | 2 + .../Audacity/Audacity.vcxproj.filters | 6 + 8 files changed, 111 insertions(+), 75 deletions(-) create mode 100644 src/CommonCommandFlags.cpp create mode 100644 src/CommonCommandFlags.h diff --git a/locale/POTFILES.in b/locale/POTFILES.in index 66e2060b7..912f13194 100644 --- a/locale/POTFILES.in +++ b/locale/POTFILES.in @@ -62,6 +62,8 @@ src/CellularPanel.cpp src/CellularPanel.h src/Clipboard.cpp src/Clipboard.h +src/CommonCommandFlags.cpp +src/CommonCommandFlags.h src/CrashReport.cpp src/CrashReport.h src/ClassicThemeAsCeeCode.h diff --git a/mac/Audacity.xcodeproj/project.pbxproj b/mac/Audacity.xcodeproj/project.pbxproj index 10d8c0587..df5d46fdc 100644 --- a/mac/Audacity.xcodeproj/project.pbxproj +++ b/mac/Audacity.xcodeproj/project.pbxproj @@ -1216,9 +1216,9 @@ 5E135A3C229EDF2E0076E983 /* ProjectManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E135A3A229EDF2E0076E983 /* ProjectManager.cpp */; }; 5E135A45229EE4DE0076E983 /* ProjectFileIO.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E135A43229EE4DE0076E983 /* ProjectFileIO.cpp */; }; 5E135A48229EE5530076E983 /* ProjectWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E135A46229EE5530076E983 /* ProjectWindow.cpp */; }; - 5E135A5122A93DC60076E983 /* ProjectAudioManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E135A4F22A93DC60076E983 /* ProjectAudioManager.cpp */; }; 5E135A4B22A5F7560076E983 /* AudioIOBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E135A4922A5F7560076E983 /* AudioIOBase.cpp */; }; 5E135A4E22A62B7E0076E983 /* MeterPanelBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E135A4C22A62B7E0076E983 /* MeterPanelBase.cpp */; }; + 5E135A5122A93DC60076E983 /* ProjectAudioManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E135A4F22A93DC60076E983 /* ProjectAudioManager.cpp */; }; 5E15123D1DB000C000702E29 /* UIHandle.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E15123B1DB000C000702E29 /* UIHandle.cpp */; }; 5E15125A1DB000DC00702E29 /* LabelTrackControls.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E1512401DB000DC00702E29 /* LabelTrackControls.cpp */; }; 5E15125B1DB000DC00702E29 /* LabelTrackUI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E1512421DB000DC00702E29 /* LabelTrackUI.cpp */; }; @@ -1230,6 +1230,7 @@ 5E15126E1DB0010C00702E29 /* TrackControls.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E1512681DB0010C00702E29 /* TrackControls.cpp */; }; 5E15126F1DB0010C00702E29 /* TrackUI.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E15126A1DB0010C00702E29 /* TrackUI.cpp */; }; 5E1512701DB0010C00702E29 /* TrackVRulerControls.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E15126B1DB0010C00702E29 /* TrackVRulerControls.cpp */; }; + 5E15A9AF22B3F7710007CC43 /* CommonCommandFlags.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E15A9AD22B3F7710007CC43 /* CommonCommandFlags.cpp */; }; 5E16FF4D1FF9CE0B0085E1B8 /* LanguageNames.txt in Resources */ = {isa = PBXBuildFile; fileRef = 5E16FF4C1FF9CE0B0085E1B8 /* LanguageNames.txt */; }; 5E17EF712298372D00B47301 /* EnvelopeEditor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E17EF6F2298372D00B47301 /* EnvelopeEditor.cpp */; }; 5E18CFF02291C31000E75250 /* ProjectFileIORegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E18CFEE2291C31000E75250 /* ProjectFileIORegistry.cpp */; }; @@ -3191,12 +3192,12 @@ 5E135A44229EE4DE0076E983 /* ProjectFileIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProjectFileIO.h; sourceTree = ""; }; 5E135A46229EE5530076E983 /* ProjectWindow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProjectWindow.cpp; sourceTree = ""; }; 5E135A47229EE5530076E983 /* ProjectWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProjectWindow.h; sourceTree = ""; }; - 5E135A4F22A93DC60076E983 /* ProjectAudioManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProjectAudioManager.cpp; sourceTree = ""; }; - 5E135A5022A93DC60076E983 /* ProjectAudioManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProjectAudioManager.h; sourceTree = ""; }; 5E135A4922A5F7560076E983 /* AudioIOBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AudioIOBase.cpp; sourceTree = ""; }; 5E135A4A22A5F7560076E983 /* AudioIOBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioIOBase.h; sourceTree = ""; }; 5E135A4C22A62B7E0076E983 /* MeterPanelBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MeterPanelBase.cpp; sourceTree = ""; }; 5E135A4D22A62B7E0076E983 /* MeterPanelBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MeterPanelBase.h; sourceTree = ""; }; + 5E135A4F22A93DC60076E983 /* ProjectAudioManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProjectAudioManager.cpp; sourceTree = ""; }; + 5E135A5022A93DC60076E983 /* ProjectAudioManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProjectAudioManager.h; sourceTree = ""; }; 5E1512381DB000C000702E29 /* HitTestResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HitTestResult.h; sourceTree = ""; }; 5E1512391DB000C000702E29 /* RefreshCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RefreshCode.h; sourceTree = ""; }; 5E15123A1DB000C000702E29 /* TrackPanelMouseEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackPanelMouseEvent.h; sourceTree = ""; }; @@ -3219,6 +3220,8 @@ 5E15126A1DB0010C00702E29 /* TrackUI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrackUI.cpp; sourceTree = ""; }; 5E15126B1DB0010C00702E29 /* TrackVRulerControls.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrackVRulerControls.cpp; sourceTree = ""; }; 5E15126C1DB0010C00702E29 /* TrackVRulerControls.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackVRulerControls.h; sourceTree = ""; }; + 5E15A9AD22B3F7710007CC43 /* CommonCommandFlags.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CommonCommandFlags.cpp; sourceTree = ""; }; + 5E15A9AE22B3F7710007CC43 /* CommonCommandFlags.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonCommandFlags.h; sourceTree = ""; }; 5E16FF4C1FF9CE0B0085E1B8 /* LanguageNames.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = LanguageNames.txt; path = ../locale/LanguageNames.txt; sourceTree = ""; }; 5E17EF6F2298372D00B47301 /* EnvelopeEditor.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EnvelopeEditor.cpp; sourceTree = ""; }; 5E17EF702298372D00B47301 /* EnvelopeEditor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EnvelopeEditor.h; sourceTree = ""; }; @@ -4347,6 +4350,7 @@ 1790AFE809883BFD008A330A /* BlockFile.cpp */, 5E0A1CDB20E95FF7001AAF8D /* CellularPanel.cpp */, 5EFEAD9C22723E390077DFF6 /* Clipboard.cpp */, + 5E15A9AD22B3F7710007CC43 /* CommonCommandFlags.cpp */, 5EFEADA122733DD30077DFF6 /* CrashReport.cpp */, 1790AFF409883BFD008A330A /* CrossFade.cpp */, 2849B4600A7444BE00ECF12D /* Dependencies.cpp */, @@ -4464,6 +4468,7 @@ 5E0D233E21B468BF0057D7C3 /* ClientData.h */, 5E6E060321BD98E700130DE0 /* ClientDataHelpers.h */, 5EFEAD9D22723E390077DFF6 /* Clipboard.h */, + 5E15A9AE22B3F7710007CC43 /* CommonCommandFlags.h */, 1790AFF009883BFD008A330A /* configtemplate.h */, 5EFEADA222733DD30077DFF6 /* CrashReport.h */, 1790AFF509883BFD008A330A /* CrossFade.h */, @@ -8405,6 +8410,7 @@ 1790B18009883BFD008A330A /* BatchPrefs.cpp in Sources */, 5E135A39229EDEBA0076E983 /* ProjectAudioIO.cpp in Sources */, 1790B18109883BFD008A330A /* DirectoriesPrefs.cpp in Sources */, + 5E15A9AF22B3F7710007CC43 /* CommonCommandFlags.cpp in Sources */, 1790B18309883BFD008A330A /* GUIPrefs.cpp in Sources */, 1790B18409883BFD008A330A /* KeyConfigPrefs.cpp in Sources */, 2806EF7A1B32532A00D1AB9A /* FileDialogPrivate.mm in Sources */, diff --git a/src/CommonCommandFlags.cpp b/src/CommonCommandFlags.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/CommonCommandFlags.h b/src/CommonCommandFlags.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/Makefile.am b/src/Makefile.am index 53f88a338..4514ff451 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -133,6 +133,8 @@ audacity_SOURCES = \ ClientDataHelpers.h \ Clipboard.cpp \ Clipboard.h \ + CommonCommandFlags.cpp \ + CommonCommandFlags.h \ CrashReport.cpp \ CrashReport.h \ Dependencies.cpp \ diff --git a/src/Makefile.in b/src/Makefile.in index 4f0de09ed..380298ea2 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -297,41 +297,42 @@ am__audacity_SOURCES_DIST = BlockFile.cpp BlockFile.h DirManager.cpp \ BatchCommands.h BatchProcessDialog.cpp BatchProcessDialog.h \ Benchmark.cpp Benchmark.h CellularPanel.cpp CellularPanel.h \ ClientData.h ClientDataHelpers.h Clipboard.cpp Clipboard.h \ - CrashReport.cpp CrashReport.h Dependencies.cpp Dependencies.h \ - DeviceChange.cpp DeviceChange.h DeviceManager.cpp \ - DeviceManager.h Diags.cpp Diags.h Envelope.cpp Envelope.h \ - EnvelopeEditor.cpp EnvelopeEditor.h Experimental.h FFmpeg.cpp \ - FFmpeg.h FFT.cpp FFT.h FileException.cpp FileException.h \ - FileIO.cpp FileIO.h FileNames.cpp FileNames.h float_cast.h \ - FreqWindow.cpp FreqWindow.h HelpText.cpp HelpText.h \ - HistoryWindow.cpp HistoryWindow.h HitTestResult.h \ - ImageManipulation.cpp ImageManipulation.h \ - InconsistencyException.cpp InconsistencyException.h \ - InterpolateAudio.cpp InterpolateAudio.h KeyboardCapture.cpp \ - KeyboardCapture.h LabelDialog.cpp LabelDialog.h LabelTrack.cpp \ - LabelTrack.h LangChoice.cpp LangChoice.h Languages.cpp \ - Languages.h Legacy.cpp Legacy.h Lyrics.cpp Lyrics.h \ - LyricsWindow.cpp LyricsWindow.h MacroMagic.h Matrix.cpp \ - Matrix.h MemoryX.h Menus.cpp Menus.h \ - MissingAliasFileDialog.cpp MissingAliasFileDialog.h Mix.cpp \ - Mix.h MixerBoard.cpp MixerBoard.h ModuleManager.cpp \ - ModuleManager.h NumberScale.h PitchName.cpp PitchName.h \ - PlatformCompatibility.cpp PlatformCompatibility.h \ - PluginManager.cpp PluginManager.h Printing.cpp Printing.h \ - Profiler.cpp Profiler.h Project.cpp Project.h \ - ProjectAudioIO.cpp ProjectAudioIO.h ProjectAudioManager.cpp \ - ProjectAudioManager.h ProjectFileIO.cpp ProjectFileIO.h \ - ProjectFileIORegistry.cpp ProjectFileIORegistry.h \ - ProjectFileManager.cpp ProjectFileManager.h ProjectFSCK.cpp \ - ProjectFSCK.h ProjectHistory.cpp ProjectHistory.h \ - ProjectManager.cpp ProjectManager.h \ - ProjectSelectionManager.cpp ProjectSelectionManager.h \ - ProjectSettings.cpp ProjectSettings.h ProjectWindow.cpp \ - ProjectWindow.h RealFFTf.cpp RealFFTf.h RealFFTf48x.cpp \ - RealFFTf48x.h RefreshCode.h Resample.cpp Resample.h \ - RevisionIdent.h RingBuffer.cpp RingBuffer.h Screenshot.cpp \ - Screenshot.h SelectedRegion.cpp SelectedRegion.h \ - SelectionState.cpp SelectionState.h Shuttle.cpp Shuttle.h \ + CommonCommandFlags.cpp CommonCommandFlags.h CrashReport.cpp \ + CrashReport.h Dependencies.cpp Dependencies.h DeviceChange.cpp \ + DeviceChange.h DeviceManager.cpp DeviceManager.h Diags.cpp \ + Diags.h Envelope.cpp Envelope.h EnvelopeEditor.cpp \ + EnvelopeEditor.h Experimental.h FFmpeg.cpp FFmpeg.h FFT.cpp \ + FFT.h FileException.cpp FileException.h FileIO.cpp FileIO.h \ + FileNames.cpp FileNames.h float_cast.h FreqWindow.cpp \ + FreqWindow.h HelpText.cpp HelpText.h HistoryWindow.cpp \ + HistoryWindow.h HitTestResult.h ImageManipulation.cpp \ + ImageManipulation.h InconsistencyException.cpp \ + InconsistencyException.h InterpolateAudio.cpp \ + InterpolateAudio.h KeyboardCapture.cpp KeyboardCapture.h \ + LabelDialog.cpp LabelDialog.h LabelTrack.cpp LabelTrack.h \ + LangChoice.cpp LangChoice.h Languages.cpp Languages.h \ + Legacy.cpp Legacy.h Lyrics.cpp Lyrics.h LyricsWindow.cpp \ + LyricsWindow.h MacroMagic.h Matrix.cpp Matrix.h MemoryX.h \ + Menus.cpp Menus.h MissingAliasFileDialog.cpp \ + MissingAliasFileDialog.h Mix.cpp Mix.h MixerBoard.cpp \ + MixerBoard.h ModuleManager.cpp ModuleManager.h NumberScale.h \ + PitchName.cpp PitchName.h PlatformCompatibility.cpp \ + PlatformCompatibility.h PluginManager.cpp PluginManager.h \ + Printing.cpp Printing.h Profiler.cpp Profiler.h Project.cpp \ + Project.h ProjectAudioIO.cpp ProjectAudioIO.h \ + ProjectAudioManager.cpp ProjectAudioManager.h \ + ProjectFileIO.cpp ProjectFileIO.h ProjectFileIORegistry.cpp \ + ProjectFileIORegistry.h ProjectFileManager.cpp \ + ProjectFileManager.h ProjectFSCK.cpp ProjectFSCK.h \ + ProjectHistory.cpp ProjectHistory.h ProjectManager.cpp \ + ProjectManager.h ProjectSelectionManager.cpp \ + ProjectSelectionManager.h ProjectSettings.cpp \ + ProjectSettings.h ProjectWindow.cpp ProjectWindow.h \ + RealFFTf.cpp RealFFTf.h RealFFTf48x.cpp RealFFTf48x.h \ + RefreshCode.h Resample.cpp Resample.h RevisionIdent.h \ + RingBuffer.cpp RingBuffer.h Screenshot.cpp Screenshot.h \ + SelectedRegion.cpp SelectedRegion.h SelectionState.cpp \ + SelectionState.h Shuttle.cpp Shuttle.h \ ShuttleGetDefinition.cpp ShuttleGetDefinition.h ShuttleGui.cpp \ ShuttleGui.h ShuttlePrefs.cpp ShuttlePrefs.h Snap.cpp Snap.h \ SoundActivatedRecord.cpp SoundActivatedRecord.h Spectrum.cpp \ @@ -644,8 +645,9 @@ am_audacity_OBJECTS = $(am__objects_1) audacity-AboutDialog.$(OBJEXT) \ audacity-BatchCommands.$(OBJEXT) \ audacity-BatchProcessDialog.$(OBJEXT) \ audacity-Benchmark.$(OBJEXT) audacity-CellularPanel.$(OBJEXT) \ - audacity-Clipboard.$(OBJEXT) audacity-CrashReport.$(OBJEXT) \ - audacity-Dependencies.$(OBJEXT) \ + audacity-Clipboard.$(OBJEXT) \ + audacity-CommonCommandFlags.$(OBJEXT) \ + audacity-CrashReport.$(OBJEXT) audacity-Dependencies.$(OBJEXT) \ audacity-DeviceChange.$(OBJEXT) \ audacity-DeviceManager.$(OBJEXT) audacity-Diags.$(OBJEXT) \ audacity-Envelope.$(OBJEXT) audacity-EnvelopeEditor.$(OBJEXT) \ @@ -1376,41 +1378,42 @@ audacity_SOURCES = $(libaudacity_la_SOURCES) AboutDialog.cpp \ BatchCommands.h BatchProcessDialog.cpp BatchProcessDialog.h \ Benchmark.cpp Benchmark.h CellularPanel.cpp CellularPanel.h \ ClientData.h ClientDataHelpers.h Clipboard.cpp Clipboard.h \ - CrashReport.cpp CrashReport.h Dependencies.cpp Dependencies.h \ - DeviceChange.cpp DeviceChange.h DeviceManager.cpp \ - DeviceManager.h Diags.cpp Diags.h Envelope.cpp Envelope.h \ - EnvelopeEditor.cpp EnvelopeEditor.h Experimental.h FFmpeg.cpp \ - FFmpeg.h FFT.cpp FFT.h FileException.cpp FileException.h \ - FileIO.cpp FileIO.h FileNames.cpp FileNames.h float_cast.h \ - FreqWindow.cpp FreqWindow.h HelpText.cpp HelpText.h \ - HistoryWindow.cpp HistoryWindow.h HitTestResult.h \ - ImageManipulation.cpp ImageManipulation.h \ - InconsistencyException.cpp InconsistencyException.h \ - InterpolateAudio.cpp InterpolateAudio.h KeyboardCapture.cpp \ - KeyboardCapture.h LabelDialog.cpp LabelDialog.h LabelTrack.cpp \ - LabelTrack.h LangChoice.cpp LangChoice.h Languages.cpp \ - Languages.h Legacy.cpp Legacy.h Lyrics.cpp Lyrics.h \ - LyricsWindow.cpp LyricsWindow.h MacroMagic.h Matrix.cpp \ - Matrix.h MemoryX.h Menus.cpp Menus.h \ - MissingAliasFileDialog.cpp MissingAliasFileDialog.h Mix.cpp \ - Mix.h MixerBoard.cpp MixerBoard.h ModuleManager.cpp \ - ModuleManager.h NumberScale.h PitchName.cpp PitchName.h \ - PlatformCompatibility.cpp PlatformCompatibility.h \ - PluginManager.cpp PluginManager.h Printing.cpp Printing.h \ - Profiler.cpp Profiler.h Project.cpp Project.h \ - ProjectAudioIO.cpp ProjectAudioIO.h ProjectAudioManager.cpp \ - ProjectAudioManager.h ProjectFileIO.cpp ProjectFileIO.h \ - ProjectFileIORegistry.cpp ProjectFileIORegistry.h \ - ProjectFileManager.cpp ProjectFileManager.h ProjectFSCK.cpp \ - ProjectFSCK.h ProjectHistory.cpp ProjectHistory.h \ - ProjectManager.cpp ProjectManager.h \ - ProjectSelectionManager.cpp ProjectSelectionManager.h \ - ProjectSettings.cpp ProjectSettings.h ProjectWindow.cpp \ - ProjectWindow.h RealFFTf.cpp RealFFTf.h RealFFTf48x.cpp \ - RealFFTf48x.h RefreshCode.h Resample.cpp Resample.h \ - RevisionIdent.h RingBuffer.cpp RingBuffer.h Screenshot.cpp \ - Screenshot.h SelectedRegion.cpp SelectedRegion.h \ - SelectionState.cpp SelectionState.h Shuttle.cpp Shuttle.h \ + CommonCommandFlags.cpp CommonCommandFlags.h CrashReport.cpp \ + CrashReport.h Dependencies.cpp Dependencies.h DeviceChange.cpp \ + DeviceChange.h DeviceManager.cpp DeviceManager.h Diags.cpp \ + Diags.h Envelope.cpp Envelope.h EnvelopeEditor.cpp \ + EnvelopeEditor.h Experimental.h FFmpeg.cpp FFmpeg.h FFT.cpp \ + FFT.h FileException.cpp FileException.h FileIO.cpp FileIO.h \ + FileNames.cpp FileNames.h float_cast.h FreqWindow.cpp \ + FreqWindow.h HelpText.cpp HelpText.h HistoryWindow.cpp \ + HistoryWindow.h HitTestResult.h ImageManipulation.cpp \ + ImageManipulation.h InconsistencyException.cpp \ + InconsistencyException.h InterpolateAudio.cpp \ + InterpolateAudio.h KeyboardCapture.cpp KeyboardCapture.h \ + LabelDialog.cpp LabelDialog.h LabelTrack.cpp LabelTrack.h \ + LangChoice.cpp LangChoice.h Languages.cpp Languages.h \ + Legacy.cpp Legacy.h Lyrics.cpp Lyrics.h LyricsWindow.cpp \ + LyricsWindow.h MacroMagic.h Matrix.cpp Matrix.h MemoryX.h \ + Menus.cpp Menus.h MissingAliasFileDialog.cpp \ + MissingAliasFileDialog.h Mix.cpp Mix.h MixerBoard.cpp \ + MixerBoard.h ModuleManager.cpp ModuleManager.h NumberScale.h \ + PitchName.cpp PitchName.h PlatformCompatibility.cpp \ + PlatformCompatibility.h PluginManager.cpp PluginManager.h \ + Printing.cpp Printing.h Profiler.cpp Profiler.h Project.cpp \ + Project.h ProjectAudioIO.cpp ProjectAudioIO.h \ + ProjectAudioManager.cpp ProjectAudioManager.h \ + ProjectFileIO.cpp ProjectFileIO.h ProjectFileIORegistry.cpp \ + ProjectFileIORegistry.h ProjectFileManager.cpp \ + ProjectFileManager.h ProjectFSCK.cpp ProjectFSCK.h \ + ProjectHistory.cpp ProjectHistory.h ProjectManager.cpp \ + ProjectManager.h ProjectSelectionManager.cpp \ + ProjectSelectionManager.h ProjectSettings.cpp \ + ProjectSettings.h ProjectWindow.cpp ProjectWindow.h \ + RealFFTf.cpp RealFFTf.h RealFFTf48x.cpp RealFFTf48x.h \ + RefreshCode.h Resample.cpp Resample.h RevisionIdent.h \ + RingBuffer.cpp RingBuffer.h Screenshot.cpp Screenshot.h \ + SelectedRegion.cpp SelectedRegion.h SelectionState.cpp \ + SelectionState.h Shuttle.cpp Shuttle.h \ ShuttleGetDefinition.cpp ShuttleGetDefinition.h ShuttleGui.cpp \ ShuttleGui.h ShuttlePrefs.cpp ShuttlePrefs.h Snap.cpp Snap.h \ SoundActivatedRecord.cpp SoundActivatedRecord.h Spectrum.cpp \ @@ -2539,6 +2542,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-BlockFile.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-CellularPanel.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-Clipboard.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-CommonCommandFlags.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-CrashReport.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-Dependencies.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audacity-DeviceChange.Po@am__quote@ @@ -3506,6 +3510,20 @@ audacity-Clipboard.obj: Clipboard.cpp @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -c -o audacity-Clipboard.obj `if test -f 'Clipboard.cpp'; then $(CYGPATH_W) 'Clipboard.cpp'; else $(CYGPATH_W) '$(srcdir)/Clipboard.cpp'; fi` +audacity-CommonCommandFlags.o: CommonCommandFlags.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -MT audacity-CommonCommandFlags.o -MD -MP -MF $(DEPDIR)/audacity-CommonCommandFlags.Tpo -c -o audacity-CommonCommandFlags.o `test -f 'CommonCommandFlags.cpp' || echo '$(srcdir)/'`CommonCommandFlags.cpp +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/audacity-CommonCommandFlags.Tpo $(DEPDIR)/audacity-CommonCommandFlags.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='CommonCommandFlags.cpp' object='audacity-CommonCommandFlags.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -c -o audacity-CommonCommandFlags.o `test -f 'CommonCommandFlags.cpp' || echo '$(srcdir)/'`CommonCommandFlags.cpp + +audacity-CommonCommandFlags.obj: CommonCommandFlags.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -MT audacity-CommonCommandFlags.obj -MD -MP -MF $(DEPDIR)/audacity-CommonCommandFlags.Tpo -c -o audacity-CommonCommandFlags.obj `if test -f 'CommonCommandFlags.cpp'; then $(CYGPATH_W) 'CommonCommandFlags.cpp'; else $(CYGPATH_W) '$(srcdir)/CommonCommandFlags.cpp'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/audacity-CommonCommandFlags.Tpo $(DEPDIR)/audacity-CommonCommandFlags.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='CommonCommandFlags.cpp' object='audacity-CommonCommandFlags.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -c -o audacity-CommonCommandFlags.obj `if test -f 'CommonCommandFlags.cpp'; then $(CYGPATH_W) 'CommonCommandFlags.cpp'; else $(CYGPATH_W) '$(srcdir)/CommonCommandFlags.cpp'; fi` + audacity-CrashReport.o: CrashReport.cpp @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(audacity_CPPFLAGS) $(CPPFLAGS) $(audacity_CXXFLAGS) $(CXXFLAGS) -MT audacity-CrashReport.o -MD -MP -MF $(DEPDIR)/audacity-CrashReport.Tpo -c -o audacity-CrashReport.o `test -f 'CrashReport.cpp' || echo '$(srcdir)/'`CrashReport.cpp @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/audacity-CrashReport.Tpo $(DEPDIR)/audacity-CrashReport.Po diff --git a/win/Projects/Audacity/Audacity.vcxproj b/win/Projects/Audacity/Audacity.vcxproj index f9dafa385..7e0005e08 100755 --- a/win/Projects/Audacity/Audacity.vcxproj +++ b/win/Projects/Audacity/Audacity.vcxproj @@ -140,6 +140,7 @@ + @@ -522,6 +523,7 @@ + diff --git a/win/Projects/Audacity/Audacity.vcxproj.filters b/win/Projects/Audacity/Audacity.vcxproj.filters index c59a49683..70d2245b0 100755 --- a/win/Projects/Audacity/Audacity.vcxproj.filters +++ b/win/Projects/Audacity/Audacity.vcxproj.filters @@ -1148,6 +1148,9 @@ src + + src + src @@ -2314,6 +2317,9 @@ src + + src + src From c0adb358396672f14df79977bd36b991eb57b8c8 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Fri, 14 Jun 2019 11:39:12 -0400 Subject: [PATCH 19/23] Command flag functions out of Menus.cpp, reducing its dependencies... ... Mostly into CommonCommandFlags.cpp, but some elsewhere, to avoid giving that new file problematic dependencies on LabelTrack, ControlToolBar, and EffectManager. Note that CutCopyAvailableFlag is critically ordered, for message purposes, only with AudioIONotAvailableFlag, the only flag with a message that it combines with in menu item definitions. The dependency on LabelTrack.cpp might not be a bad one later, if the track and its view can be separated, and that would allow CutCopyAvailableFlag to be put with the others. But much other work on LabelTrack must happen first. --- lib-src/mod-null/ModNullCallback.cpp | 1 + lib-src/mod-nyq-bench/NyqBench.cpp | 1 + src/CommonCommandFlags.cpp | 356 +++++++++++++++++++++++++ src/CommonCommandFlags.h | 58 ++++ src/Menus.cpp | 381 --------------------------- src/commands/CommandFlag.h | 37 --- src/effects/Effect.cpp | 2 +- src/menus/ClipMenus.cpp | 1 + src/menus/EditMenus.cpp | 45 ++++ src/menus/ExtraMenus.cpp | 1 + src/menus/FileMenus.cpp | 1 + src/menus/HelpMenus.cpp | 1 + src/menus/LabelMenus.cpp | 1 + src/menus/NavigationMenus.cpp | 1 + src/menus/PluginMenus.cpp | 8 + src/menus/SelectMenus.cpp | 1 + src/menus/TrackMenus.cpp | 1 + src/menus/TransportMenus.cpp | 1 + src/menus/ViewMenus.cpp | 1 + src/menus/WindowMenus.cpp | 1 + src/toolbars/ControlToolBar.cpp | 8 + src/toolbars/ControlToolBar.h | 5 + src/tracks/ui/Scrubbing.cpp | 1 + 23 files changed, 495 insertions(+), 419 deletions(-) diff --git a/lib-src/mod-null/ModNullCallback.cpp b/lib-src/mod-null/ModNullCallback.cpp index d201c3419..4ded39a1f 100644 --- a/lib-src/mod-null/ModNullCallback.cpp +++ b/lib-src/mod-null/ModNullCallback.cpp @@ -32,6 +32,7 @@ click from the menu into the actaul function to be called. #include "ShuttleGui.h" #include "Project.h" #include "commands/CommandManager.h" +#include "CommonCommandFlags.h" #if defined(__WXMSW__) #include diff --git a/lib-src/mod-nyq-bench/NyqBench.cpp b/lib-src/mod-nyq-bench/NyqBench.cpp index 26c7ebb70..a2d69d004 100755 --- a/lib-src/mod-nyq-bench/NyqBench.cpp +++ b/lib-src/mod-nyq-bench/NyqBench.cpp @@ -24,6 +24,7 @@ #include #include "AudioIOBase.h" +#include "CommonCommandFlags.h" #include "LabelTrack.h" #include "Menus.h" #include "ModuleManager.h" diff --git a/src/CommonCommandFlags.cpp b/src/CommonCommandFlags.cpp index e69de29bb..800e29500 100644 --- a/src/CommonCommandFlags.cpp +++ b/src/CommonCommandFlags.cpp @@ -0,0 +1,356 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +CommonCommandFlags.cpp + +Paul Licameli split from Menus.cpp + +**********************************************************************/ + +#include "Audacity.h" +#include "CommonCommandFlags.h" + +#include "Experimental.h" + +#include + +#include "AudioIO.h" +#include "LabelTrack.h" +#include "Menus.h" +#include "NoteTrack.h" +#include "Project.h" +#include "ProjectAudioIO.h" +#include "ProjectFileIO.h" +#include "ProjectHistory.h" +#include "ProjectSettings.h" +#include "UndoManager.h" +#include "ViewInfo.h" +#include "WaveTrack.h" +#include "commands/CommandManagerWindowClasses.h" +#include "toolbars/ControlToolBar.h" + +/* + +This file registers functions implementing many of the tests for enabling of +menu items. Sequence of a few of them has minor significance, but for most +there is little reason to keep them in one file. Flags used only by one +other file might instead be defined only where used. + +They are collected here because Menus.cpp is too low level to have the +dependencies implied by the include directives above -- it would make dependency +cycles. + +*/ + +// Really means, some track is selected, that isn't a time track +bool TracksSelectedPred( const AudacityProject &project ) +{ + auto range = TrackList::Get( project ).Selected() + - []( const Track *pTrack ){ + return track_cast( pTrack ); }; + return !range.empty(); +}; + +bool AudioIOBusyPred( const AudacityProject &project ) +{ + return AudioIOBase::Get()->IsAudioTokenActive( + ProjectAudioIO::Get( project ).GetAudioIOToken()); +}; + +bool TimeSelectedPred( const AudacityProject &project ) +{ + // This is equivalent to check if there is a valid selection, + // so it's used for Zoom to Selection too + return !ViewInfo::Get( project ).selectedRegion.isPoint(); +}; + +const CommandFlagOptions cutCopyOptions{ +// In reporting the issue with cut or copy, we don't tell the user they could also select some text in a label. + []( const wxString &Name ) { + // PRL: These strings have hard-coded mention of a certain shortcut key, + // thus assuming the default shortcuts. That is questionable. + wxString format; +#ifdef EXPERIMENTAL_DA + // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. + format = _("You must first select some audio for '%s' to act on.\n\nCtrl + A selects all audio."); +#else +#ifdef __WXMAC__ + // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. + format = _("Select the audio for %s to use (for example, Cmd + A to Select All) then try again." + // No need to explain what a help button is for. + // "\n\nClick the Help button to learn more about selection methods." + ); + +#else + // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. + format = _("Select the audio for %s to use (for example, Ctrl + A to Select All) then try again." + // No need to explain what a help button is for. + // "\n\nClick the Help button to learn more about selection methods." + ); +#endif + return wxString::Format( format, Name ); +#endif + }, + "Selecting_Audio_-_the_basics", + XO("No Audio Selected") +}; + +const ReservedCommandFlag + // The sequence of these definitions has a minor significance in determining + // which user error message has precedence if more than one might apply, so + // they should be kept in this sequence in one .cpp file if it is important + // to preserve that behavior. If they are dispersed to more than one file, + // then the precedence will be unspecified. + // The ordering of the flags that only disable the default message is not + // significant. + AudioIONotBusyFlag{ + [](const AudacityProject &project ){ + return !AudioIOBusyPred( project ); + }, + CommandFlagOptions{ []( const wxString& ) { return + // This reason will not be shown, because options that require it will be greyed out. + _("You can only do this when playing and recording are\nstopped. (Pausing is not sufficient.)"); + } } + .QuickTest() + .Priority( 1 ) + }, //lll + StereoRequiredFlag{ + [](const AudacityProject &project){ + // True iff at least one stereo track is selected, i.e., at least + // one right channel is selected. + // TODO: more-than-two-channels + auto range = TrackList::Get( project ).Selected() + - &Track::IsLeader; + return !range.empty(); + }, + { []( const wxString& ) { return + // This reason will not be shown, because the stereo-to-mono is greyed out if not allowed. + _("You must first select some stereo audio to perform this\naction. (You cannot use this with mono.)"); + } } + }, //lda + TimeSelectedFlag{ + TimeSelectedPred, + cutCopyOptions + }, + WaveTracksSelectedFlag{ + [](const AudacityProject &project){ + return !TrackList::Get( project ).Selected().empty(); + }, + { []( const wxString& ) { return + _("You must first select some audio to perform this action.\n(Selecting other kinds of track won't work.)"); + } } + }, + TracksExistFlag{ + [](const AudacityProject &project){ + return !TrackList::Get( project ).Any().empty(); + }, + CommandFlagOptions{}.DisableDefaultMessage() + }, + TracksSelectedFlag{ + TracksSelectedPred, + { []( const wxString &Name ){ return wxString::Format( + // i18n-hint: %s will be replaced by the name of an action, such as "Remove Tracks". + _("\"%s\" requires one or more tracks to be selected."), + Name + ); } } + }, + TrackPanelHasFocus{ + [](const AudacityProject &project){ + for (auto w = wxWindow::FindFocus(); w; w = w->GetParent()) { + if (dynamic_cast(w)) + return true; + } + return false; + }, + CommandFlagOptions{}.DisableDefaultMessage() + }; //lll + +const ReservedCommandFlag + AudioIOBusyFlag{ + AudioIOBusyPred, + CommandFlagOptions{}.QuickTest() + }, //lll + CaptureNotBusyFlag{ + [](const AudacityProject &){ + auto gAudioIO = AudioIO::Get(); + return !( + gAudioIO->IsBusy() && + gAudioIO->GetNumCaptureChannels() > 0 + ); + } + }, + HasWaveDataFlag{ + [](const AudacityProject &project){ + auto range = TrackList::Get( project ).Any() + + [](const WaveTrack *pTrack){ + return pTrack->GetEndTime() > pTrack->GetStartTime(); + }; + return !range.empty(); + } + }; // jkc + +const ReservedCommandFlag + LabelTracksExistFlag{ + [](const AudacityProject &project){ + return !TrackList::Get( project ).Any().empty(); + } + }, + UnsavedChangesFlag{ + [](const AudacityProject &project){ + auto &undoManager = UndoManager::Get( project ); + return + undoManager.UnsavedChanges() + || + !ProjectFileIO::Get( project ).IsProjectSaved() + ; + } + }, + HasLastEffectFlag{ + [](const AudacityProject &project){ + return !MenuManager::Get( project ).mLastEffect.empty(); + } + }, + UndoAvailableFlag{ + [](const AudacityProject &project){ + return ProjectHistory::Get( project ).UndoAvailable(); + } + }, + RedoAvailableFlag{ + [](const AudacityProject &project){ + return ProjectHistory::Get( project ).RedoAvailable(); + } + }, + ZoomInAvailableFlag{ + [](const AudacityProject &project){ + return + ViewInfo::Get( project ).ZoomInAvailable() + && + !TrackList::Get( project ).Any().empty() + ; + } + }, + ZoomOutAvailableFlag{ + [](const AudacityProject &project){ + return + ViewInfo::Get( project ).ZoomOutAvailable() + && + !TrackList::Get( project ).Any().empty() + ; + } + }, + LabelsSelectedFlag{ + [](const AudacityProject &project){ + // At least one label track selected, having at least one label + // completely within the time selection. + const auto &selectedRegion = ViewInfo::Get( project ).selectedRegion; + const auto &test = [&]( const LabelTrack *pTrack ){ + const auto &labels = pTrack->GetLabels(); + return std::any_of( labels.begin(), labels.end(), + [&](const LabelStruct &label){ + return + label.getT0() >= selectedRegion.t0() + && + label.getT1() <= selectedRegion.t1() + ; + } + ); + }; + auto range = TrackList::Get( project ).Selected() + + test; + return !range.empty(); + } + }, + PlayRegionLockedFlag{ + [](const AudacityProject &project){ + return ViewInfo::Get(project).playRegion.Locked(); + } + }, //msmeyer + PlayRegionNotLockedFlag{ + [](const AudacityProject &project){ + const auto &playRegion = ViewInfo::Get(project).playRegion; + return !playRegion.Locked() && !playRegion.Empty(); + } + }, //msmeyer + WaveTracksExistFlag{ + [](const AudacityProject &project){ + return !TrackList::Get( project ).Any().empty(); + } + }, + NoteTracksExistFlag{ + [](const AudacityProject &project){ + return !TrackList::Get( project ).Any().empty(); + } + }, //gsw + NoteTracksSelectedFlag{ + [](const AudacityProject &project){ + return !TrackList::Get( project ).Selected().empty(); + } + }, //gsw + IsNotSyncLockedFlag{ + [](const AudacityProject &project){ + return !ProjectSettings::Get( project ).IsSyncLocked(); + } + }, //awd + IsSyncLockedFlag{ + [](const AudacityProject &project){ + return ProjectSettings::Get( project ).IsSyncLocked(); + } + }, //awd + NotMinimizedFlag{ + [](const AudacityProject &project){ + const wxWindow *focus = FindProjectFrame( &project ); + if (focus) { + while (focus && focus->GetParent()) + focus = focus->GetParent(); + } + return (focus && + !static_cast(focus)->IsIconized() + ); + }, + CommandFlagOptions{}.QuickTest() + }, // prl + PausedFlag{ + [](const AudacityProject&){ + return AudioIOBase::Get()->IsPaused(); + }, + CommandFlagOptions{}.QuickTest() + }, + PlayableTracksExistFlag{ + [](const AudacityProject &project){ + auto &tracks = TrackList::Get( project ); + return +#ifdef EXPERIMENTAL_MIDI_OUT + !tracks.Any().empty() + || +#endif + !tracks.Any().empty() + ; + } + }, + AudioTracksSelectedFlag{ + [](const AudacityProject &project){ + auto &tracks = TrackList::Get( project ); + return + !tracks.Selected().empty() + // even if not EXPERIMENTAL_MIDI_OUT + || + tracks.Selected().empty() + ; + } + }, + NoAutoSelect{ + [](const AudacityProject &){ return true; } + } // jkc +; + +RegisteredMenuItemEnabler stopIfPaused{{ + PausedFlag, + AudioIONotBusyFlag, + []( const AudacityProject &project ){ + return MenuManager::Get( project ).mStopIfWasPaused; }, + []( AudacityProject &project, CommandFlag ){ + if ( MenuManager::Get( project ).mStopIfWasPaused ) + TransportActions::StopIfPaused( project ); + } +}}; diff --git a/src/CommonCommandFlags.h b/src/CommonCommandFlags.h index e69de29bb..4a73d0c79 100644 --- a/src/CommonCommandFlags.h +++ b/src/CommonCommandFlags.h @@ -0,0 +1,58 @@ +/********************************************************************** + +Audacity: A Digital Audio Editor + +CommonCommandFlags.cpp + +Paul Licameli split from Menus.cpp + +**********************************************************************/ + +#ifndef __AUDACITY_COMMON_COMMAND_FLAGS__ +#define __AUDACITY_COMMON_COMMAND_FLAGS__ + +#include "commands/CommandFlag.h" + +bool TracksSelectedPred( const AudacityProject &project ); +bool AudioIOBusyPred( const AudacityProject &project ); +bool TimeSelectedPred( const AudacityProject &project ); +extern const CommandFlagOptions cutCopyOptions; + +extern const ReservedCommandFlag + AudioIONotBusyFlag, + StereoRequiredFlag, //lda + TimeSelectedFlag, // This is equivalent to check if there is a valid selection, so it's used for Zoom to Selection too + WaveTracksSelectedFlag, + TracksExistFlag, + TracksSelectedFlag, + TrackPanelHasFocus; //lll + +extern const ReservedCommandFlag + AudioIOBusyFlag, // lll + CaptureNotBusyFlag, + HasWaveDataFlag; // jkc + +extern const ReservedCommandFlag + LabelTracksExistFlag, + UnsavedChangesFlag, + HasLastEffectFlag, + UndoAvailableFlag, + RedoAvailableFlag, + ZoomInAvailableFlag, + ZoomOutAvailableFlag, + LabelsSelectedFlag, + PlayRegionLockedFlag, //msmeyer + PlayRegionNotLockedFlag, //msmeyer + WaveTracksExistFlag, + NoteTracksExistFlag, //gsw + NoteTracksSelectedFlag, //gsw + IsNotSyncLockedFlag, //awd + IsSyncLockedFlag, //awd + NotMinimizedFlag, // prl + PausedFlag, // jkc + PlayableTracksExistFlag, + AudioTracksSelectedFlag, + NoAutoSelect // jkc +; + +#endif diff --git a/src/Menus.cpp b/src/Menus.cpp index bd333a40e..c1274ac95 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -32,27 +32,14 @@ #include -#include "AdornedRulerPanel.h" -#include "AudioIO.h" -#include "LabelTrack.h" #include "ModuleManager.h" -#ifdef USE_MIDI -#include "NoteTrack.h" -#endif // USE_MIDI -#include "Prefs.h" #include "Project.h" -#include "ProjectAudioIO.h" -#include "ProjectFileIO.h" #include "ProjectHistory.h" #include "ProjectSettings.h" #include "ProjectWindow.h" #include "UndoManager.h" -#include "ViewInfo.h" #include "commands/CommandManager.h" -#include "commands/CommandManagerWindowClasses.h" -#include "effects/EffectManager.h" #include "prefs/TracksPrefs.h" -#include "toolbars/ControlToolBar.h" #include "toolbars/ToolManager.h" #include "widgets/ErrorDialog.h" @@ -401,28 +388,6 @@ void MenuManager::OnUndoRedo( wxCommandEvent &evt ) UpdateMenus(); } -// Really means, some track is selected, that isn't a time track -const auto TracksSelectedPred = - [](const AudacityProject &project){ - auto range = TrackList::Get( project ).Selected() - - []( const Track *pTrack ){ - return track_cast( pTrack ); }; - return !range.empty(); - }; - -const auto AudioIOBusyPred = - [](const AudacityProject &project ){ - return AudioIOBase::Get()->IsAudioTokenActive( - ProjectAudioIO::Get( project ).GetAudioIOToken()); - }; - -const auto TimeSelectedPred = - [](const AudacityProject &project){ - // This is equivalent to check if there is a valid selection, - // so it's used for Zoom to Selection too - return !ViewInfo::Get( project ).selectedRegion.isPoint(); - }; - namespace{ using Predicates = std::vector< ReservedCommandFlag::Predicate >; Predicates &RegisteredPredicates() @@ -448,319 +413,6 @@ ReservedCommandFlag::ReservedCommandFlag( Options().emplace_back( options ); } -static const CommandFlagOptions cutCopyOptions{ -// In reporting the issue with cut or copy, we don't tell the user they could also select some text in a label. - []( const wxString &Name ) { - // PRL: These strings have hard-coded mention of a certain shortcut key, - // thus assuming the default shortcuts. That is questionable. - wxString format; -#ifdef EXPERIMENTAL_DA - // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. - format = _("You must first select some audio for '%s' to act on.\n\nCtrl + A selects all audio."); -#else -#ifdef __WXMAC__ - // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. - format = _("Select the audio for %s to use (for example, Cmd + A to Select All) then try again." - // No need to explain what a help button is for. - // "\n\nClick the Help button to learn more about selection methods." - ); - -#else - // i18n-hint: %s will be replaced by the name of an action, such as Normalize, Cut, Fade. - format = _("Select the audio for %s to use (for example, Ctrl + A to Select All) then try again." - // No need to explain what a help button is for. - // "\n\nClick the Help button to learn more about selection methods." - ); -#endif - return wxString::Format( format, Name ); -#endif - }, - "Selecting_Audio_-_the_basics", - XO("No Audio Selected") -}; - -const ReservedCommandFlag - // This flag has higher priority than others for purposes of help messages. - // It was important to arrange that for reasons of breaking dependency - // cycles, giving more freedom in the choice of file in which to implement - // this flag. - AudioIONotBusyFlag{ - [](const AudacityProject &project ){ - return !AudioIOBusyPred( project ); - }, - CommandFlagOptions{ []( const wxString& ) { return - // This reason will not be shown, because options that require it will be greyed out. - _("You can only do this when playing and recording are\nstopped. (Pausing is not sufficient.)"); - } } - .QuickTest() - .Priority( 1 ) - }; //lll - -const ReservedCommandFlag - // The sequence of these definitions has a minor significance in determining - // which user error message has precedence if more than one might apply, so - // they should be kept in this sequence in one .cpp file if it is important - // to preserve that behavior. If they are dispersed to more than one file, - // then the precedence will be unspecified. - // The ordering of the flags that only disable the default message is not - // significant. - StereoRequiredFlag{ - [](const AudacityProject &project){ - // True iff at least one stereo track is selected, i.e., at least - // one right channel is selected. - // TODO: more-than-two-channels - auto range = TrackList::Get( project ).Selected() - - &Track::IsLeader; - return !range.empty(); - }, - { []( const wxString& ) { return - // This reason will not be shown, because the stereo-to-mono is greyed out if not allowed. - _("You must first select some stereo audio to perform this\naction. (You cannot use this with mono.)"); - } } - }, //lda - TimeSelectedFlag{ - TimeSelectedPred, - cutCopyOptions - }, - CutCopyAvailableFlag{ - [](const AudacityProject &project){ - auto range = TrackList::Get( project ).Any() - + &LabelTrack::IsTextSelected; - if ( !range.empty() ) - return true; - - if ( - !AudioIOBusyPred( project ) - && - TimeSelectedPred( project ) - && - TracksSelectedPred( project ) - ) - return true; - - return false; - }, - cutCopyOptions - }, - WaveTracksSelectedFlag{ - [](const AudacityProject &project){ - return !TrackList::Get( project ).Selected().empty(); - }, - { []( const wxString& ) { return - _("You must first select some audio to perform this action.\n(Selecting other kinds of track won't work.)"); - } } - }, - TracksExistFlag{ - [](const AudacityProject &project){ - return !TrackList::Get( project ).Any().empty(); - }, - CommandFlagOptions{}.DisableDefaultMessage() - }, - TracksSelectedFlag{ - TracksSelectedPred, - { []( const wxString &Name ){ return wxString::Format( - // i18n-hint: %s will be replaced by the name of an action, such as "Remove Tracks". - _("\"%s\" requires one or more tracks to be selected."), - Name - ); } } - }, - TrackPanelHasFocus{ - [](const AudacityProject &project){ - for (auto w = wxWindow::FindFocus(); w; w = w->GetParent()) { - if (dynamic_cast(w)) - return true; - } - return false; - }, - CommandFlagOptions{}.DisableDefaultMessage() - }; //lll - -const ReservedCommandFlag - LabelTracksExistFlag{ - [](const AudacityProject &project){ - return !TrackList::Get( project ).Any().empty(); - } - }, - UnsavedChangesFlag{ - [](const AudacityProject &project){ - auto &undoManager = UndoManager::Get( project ); - return - undoManager.UnsavedChanges() - || - !ProjectFileIO::Get( project ).IsProjectSaved() - ; - } - }, - HasLastEffectFlag{ - [](const AudacityProject &project){ - return !MenuManager::Get( project ).mLastEffect.empty(); - } - }, - UndoAvailableFlag{ - [](const AudacityProject &project){ - return ProjectHistory::Get( project ).UndoAvailable(); - } - }, - RedoAvailableFlag{ - [](const AudacityProject &project){ - return ProjectHistory::Get( project ).RedoAvailable(); - } - }, - ZoomInAvailableFlag{ - [](const AudacityProject &project){ - return - ViewInfo::Get( project ).ZoomInAvailable() - && - !TrackList::Get( project ).Any().empty() - ; - } - }, - ZoomOutAvailableFlag{ - [](const AudacityProject &project){ - return - ViewInfo::Get( project ).ZoomOutAvailable() - && - !TrackList::Get( project ).Any().empty() - ; - } - }, - LabelsSelectedFlag{ - [](const AudacityProject &project){ - // At least one label track selected, having at least one label - // completely within the time selection. - const auto &selectedRegion = ViewInfo::Get( project ).selectedRegion; - const auto &test = [&]( const LabelTrack *pTrack ){ - const auto &labels = pTrack->GetLabels(); - return std::any_of( labels.begin(), labels.end(), - [&](const LabelStruct &label){ - return - label.getT0() >= selectedRegion.t0() - && - label.getT1() <= selectedRegion.t1() - ; - } - ); - }; - auto range = TrackList::Get( project ).Selected() - + test; - return !range.empty(); - } - }, - AudioIOBusyFlag{ - AudioIOBusyPred, - CommandFlagOptions{}.QuickTest() - }, //lll - PlayRegionLockedFlag{ - [](const AudacityProject &project){ - return ViewInfo::Get(project).playRegion.Locked(); - } - }, //msmeyer - PlayRegionNotLockedFlag{ - [](const AudacityProject &project){ - const auto &playRegion = ViewInfo::Get(project).playRegion; - return !playRegion.Locked() && !playRegion.Empty(); - } - }, //msmeyer - WaveTracksExistFlag{ - [](const AudacityProject &project){ - return !TrackList::Get( project ).Any().empty(); - } - }, - NoteTracksExistFlag{ - [](const AudacityProject &project){ - return !TrackList::Get( project ).Any().empty(); - } - }, //gsw - NoteTracksSelectedFlag{ - [](const AudacityProject &project){ - return !TrackList::Get( project ).Selected().empty(); - } - }, //gsw - IsNotSyncLockedFlag{ - [](const AudacityProject &project){ - return !ProjectSettings::Get( project ).IsSyncLocked(); - } - }, //awd - IsSyncLockedFlag{ - [](const AudacityProject &project){ - return ProjectSettings::Get( project ).IsSyncLocked(); - } - }, //awd - IsRealtimeNotActiveFlag{ - [](const AudacityProject &){ - return !EffectManager::Get().RealtimeIsActive(); - } - }, //lll - CaptureNotBusyFlag{ - [](const AudacityProject &){ - auto gAudioIO = AudioIO::Get(); - return !( - gAudioIO->IsBusy() && - gAudioIO->GetNumCaptureChannels() > 0 - ); - } - }, - CanStopAudioStreamFlag{ - [](const AudacityProject &project){ - return ControlToolBar::Get( project ).CanStopAudioStream(); - } - }, - NotMinimizedFlag{ - [](const AudacityProject &project){ - const wxWindow *focus = FindProjectFrame( &project ); - if (focus) { - while (focus && focus->GetParent()) - focus = focus->GetParent(); - } - return (focus && - !static_cast(focus)->IsIconized() - ); - }, - CommandFlagOptions{}.QuickTest() - }, // prl - PausedFlag{ - [](const AudacityProject&){ - return AudioIOBase::Get()->IsPaused(); - }, - CommandFlagOptions{}.QuickTest() - }, - HasWaveDataFlag{ - [](const AudacityProject &project){ - auto range = TrackList::Get( project ).Any() - + [](const WaveTrack *pTrack){ - return pTrack->GetEndTime() > pTrack->GetStartTime(); - }; - return !range.empty(); - } - }, // jkc - PlayableTracksExistFlag{ - [](const AudacityProject &project){ - auto &tracks = TrackList::Get( project ); - return -#ifdef EXPERIMENTAL_MIDI_OUT - !tracks.Any().empty() - || -#endif - !tracks.Any().empty() - ; - } - }, - AudioTracksSelectedFlag{ - [](const AudacityProject &project){ - auto &tracks = TrackList::Get( project ); - return - !tracks.Selected().empty() - // even if not EXPERIMENTAL_MIDI_OUT - || - tracks.Selected().empty() - ; - } - }, - NoAutoSelect{ - [](const AudacityProject &){ return true; } - } // jkc -; - CommandFlag MenuManager::GetUpdateFlags( bool checkActive ) { // This method determines all of the flags that determine whether @@ -898,39 +550,6 @@ RegisteredMenuItemEnabler::RegisteredMenuItemEnabler( Enablers().emplace_back( enabler ); } -RegisteredMenuItemEnabler stopIfPaused{{ - PausedFlag, - AudioIONotBusyFlag, - []( const AudacityProject &project ){ - return MenuManager::Get( project ).mStopIfWasPaused; }, - []( AudacityProject &project, CommandFlag ){ - if ( MenuManager::Get( project ).mStopIfWasPaused ) - TransportActions::StopIfPaused( project ); - } -}}; - -auto canSelectAll = [](const AudacityProject &project){ - return MenuManager::Get( project ).mWhatIfNoSelection != 0; }; -auto selectAll = []( AudacityProject &project, CommandFlag flagsRqd ){ - if ( MenuManager::Get( project ).mWhatIfNoSelection == 1 && - (flagsRqd & NoAutoSelect).none() ) - SelectActions::DoSelectAllAudio(project); -}; - -RegisteredMenuItemEnabler selectTracks{{ - TracksExistFlag, - TracksSelectedFlag, - canSelectAll, - selectAll -}}; - -RegisteredMenuItemEnabler selectWaveTracks{{ - WaveTracksExistFlag, - TimeSelectedFlag | WaveTracksSelectedFlag | CutCopyAvailableFlag, - canSelectAll, - selectAll -}}; - // checkActive is a temporary hack that should be removed as soon as we // get multiple effect preview working void MenuManager::UpdateMenus( bool checkActive ) diff --git a/src/commands/CommandFlag.h b/src/commands/CommandFlag.h index 784dd5039..75cb4acf7 100644 --- a/src/commands/CommandFlag.h +++ b/src/commands/CommandFlag.h @@ -94,43 +94,6 @@ public: // extended, with special purpose flags of limited use, by constucting static // ReservedCommandFlag values -extern const ReservedCommandFlag - AudioIOBusyFlag, //lll - StereoRequiredFlag, //lda - TimeSelectedFlag, // This is equivalent to check if there is a valid selection, so it's used for Zoom to Selection too - CutCopyAvailableFlag, - WaveTracksSelectedFlag, - TracksExistFlag, - TracksSelectedFlag, - TrackPanelHasFocus, //lll - - AudioIONotBusyFlag, - LabelTracksExistFlag, - UnsavedChangesFlag, - HasLastEffectFlag, - UndoAvailableFlag, - RedoAvailableFlag, - ZoomInAvailableFlag, - ZoomOutAvailableFlag, - LabelsSelectedFlag, - PlayRegionLockedFlag, //msmeyer - PlayRegionNotLockedFlag, //msmeyer - WaveTracksExistFlag, - NoteTracksExistFlag, //gsw - NoteTracksSelectedFlag, //gsw - IsNotSyncLockedFlag, //awd - IsSyncLockedFlag, //awd - IsRealtimeNotActiveFlag, //lll - CaptureNotBusyFlag, - CanStopAudioStreamFlag, - NotMinimizedFlag, // prl - PausedFlag, // jkc - HasWaveDataFlag, // jkc - PlayableTracksExistFlag, - AudioTracksSelectedFlag, - NoAutoSelect // jkc -; - // To describe auto-selection, stop-if-paused, etc.: // A structure describing a set of conditions, another set that might be // made true given the first, and the function that may make them true. diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index bb22da2a2..904860299 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -49,6 +49,7 @@ greater use in future. #include "EffectManager.h" #include "../AudioIO.h" +#include "../CommonCommandFlags.h" #include "../LabelTrack.h" #include "../Menus.h" #include "../Mix.h" @@ -57,7 +58,6 @@ greater use in future. #include "../Project.h" #include "../ProjectAudioManager.h" #include "../ProjectSettings.h" -#include "../PluginManager.h" #include "../ShuttleGui.h" #include "../Shuttle.h" #include "../ViewInfo.h" diff --git a/src/menus/ClipMenus.cpp b/src/menus/ClipMenus.cpp index 2068c1545..c94de4b79 100644 --- a/src/menus/ClipMenus.cpp +++ b/src/menus/ClipMenus.cpp @@ -1,3 +1,4 @@ +#include "../CommonCommandFlags.h" #include "../ProjectHistory.h" #include "../ProjectSettings.h" #include "../TrackPanel.h" diff --git a/src/menus/EditMenus.cpp b/src/menus/EditMenus.cpp index b82050116..a807361eb 100644 --- a/src/menus/EditMenus.cpp +++ b/src/menus/EditMenus.cpp @@ -1,6 +1,7 @@ #include "../Audacity.h" // for USE_* macros #include "../AdornedRulerPanel.h" #include "../Clipboard.h" +#include "../CommonCommandFlags.h" #include "../LabelTrack.h" #include "../Menus.h" #include "../NoteTrack.h" @@ -1090,6 +1091,28 @@ static CommandHandlerObject &findCommandHandler(AudacityProject &) { MenuTable::BaseItemPtr LabelEditMenus( AudacityProject &project ); +const ReservedCommandFlag + CutCopyAvailableFlag{ + [](const AudacityProject &project){ + auto range = TrackList::Get( project ).Any() + + &LabelTrack::IsTextSelected; + if ( !range.empty() ) + return true; + + if ( + !AudioIOBusyPred( project ) + && + TimeSelectedPred( project ) + && + TracksSelectedPred( project ) + ) + return true; + + return false; + }, + cutCopyOptions + }; + MenuTable::BaseItemPtr EditMenu( AudacityProject & ) { using namespace MenuTable; @@ -1229,5 +1252,27 @@ MenuTable::BaseItemPtr ExtraEditMenu( AudacityProject & ) ); } +auto canSelectAll = [](const AudacityProject &project){ + return MenuManager::Get( project ).mWhatIfNoSelection != 0; }; +auto selectAll = []( AudacityProject &project, CommandFlag flagsRqd ){ + if ( MenuManager::Get( project ).mWhatIfNoSelection == 1 && + (flagsRqd & NoAutoSelect).none() ) + SelectActions::DoSelectAllAudio(project); +}; + +RegisteredMenuItemEnabler selectTracks{{ + TracksExistFlag, + TracksSelectedFlag, + canSelectAll, + selectAll +}}; + +RegisteredMenuItemEnabler selectWaveTracks{{ + WaveTracksExistFlag, + TimeSelectedFlag | WaveTracksSelectedFlag | CutCopyAvailableFlag, + canSelectAll, + selectAll +}}; + #undef XXO #undef FN diff --git a/src/menus/ExtraMenus.cpp b/src/menus/ExtraMenus.cpp index 11bb81227..a33dd61bf 100644 --- a/src/menus/ExtraMenus.cpp +++ b/src/menus/ExtraMenus.cpp @@ -1,3 +1,4 @@ +#include "../CommonCommandFlags.h" #include "../Prefs.h" #include "../Project.h" #include "../commands/CommandContext.h" diff --git a/src/menus/FileMenus.cpp b/src/menus/FileMenus.cpp index 20b03e8cc..9cf784fd9 100644 --- a/src/menus/FileMenus.cpp +++ b/src/menus/FileMenus.cpp @@ -2,6 +2,7 @@ #include "../Experimental.h" #include "../BatchCommands.h" +#include "../CommonCommandFlags.h" #include "../FileNames.h" #include "../LabelTrack.h" #include "../MissingAliasFileDialog.h" diff --git a/src/menus/HelpMenus.cpp b/src/menus/HelpMenus.cpp index 3320e126f..e9d0577b6 100644 --- a/src/menus/HelpMenus.cpp +++ b/src/menus/HelpMenus.cpp @@ -9,6 +9,7 @@ #include "../AllThemeResources.h" #include "../AudacityLogger.h" #include "../AudioIOBase.h" +#include "../CommonCommandFlags.h" #include "../CrashReport.h" #include "../Dependencies.h" #include "../FileNames.h" diff --git a/src/menus/LabelMenus.cpp b/src/menus/LabelMenus.cpp index 7b2dd4a23..26b300810 100644 --- a/src/menus/LabelMenus.cpp +++ b/src/menus/LabelMenus.cpp @@ -1,5 +1,6 @@ #include "../AudioIOBase.h" #include "../Clipboard.h" +#include "../CommonCommandFlags.h" #include "../LabelTrack.h" #include "../Menus.h" #include "../Prefs.h" diff --git a/src/menus/NavigationMenus.cpp b/src/menus/NavigationMenus.cpp index 045fc743b..a9d07cccc 100644 --- a/src/menus/NavigationMenus.cpp +++ b/src/menus/NavigationMenus.cpp @@ -1,5 +1,6 @@ #include "../Audacity.h" +#include "../CommonCommandFlags.h" #include "../Menus.h" #include "../Prefs.h" #include "../Project.h" diff --git a/src/menus/PluginMenus.cpp b/src/menus/PluginMenus.cpp index 26383e2db..c10bf4098 100644 --- a/src/menus/PluginMenus.cpp +++ b/src/menus/PluginMenus.cpp @@ -4,6 +4,7 @@ #include "../AudioIO.h" #include "../BatchProcessDialog.h" #include "../Benchmark.h" +#include "../CommonCommandFlags.h" #include "../FreqWindow.h" #include "../Menus.h" #include "../MissingAliasFileDialog.h" @@ -923,6 +924,13 @@ MenuTable::BaseItemPtr GenerateMenu( AudacityProject & ) ); } +const ReservedCommandFlag + IsRealtimeNotActiveFlag{ + [](const AudacityProject &){ + return !EffectManager::Get().RealtimeIsActive(); + } + }; //lll + MenuTable::BaseItemPtr EffectMenu( AudacityProject &project ) { using namespace MenuTable; diff --git a/src/menus/SelectMenus.cpp b/src/menus/SelectMenus.cpp index ad2c63dfc..34a7f8263 100644 --- a/src/menus/SelectMenus.cpp +++ b/src/menus/SelectMenus.cpp @@ -3,6 +3,7 @@ #include "../AdornedRulerPanel.h" #include "../AudioIO.h" +#include "../CommonCommandFlags.h" #include "../FreqWindow.h" #include "../Menus.h" // for PrefsListener #include "../Prefs.h" diff --git a/src/menus/TrackMenus.cpp b/src/menus/TrackMenus.cpp index 261ad06f1..9074a0319 100644 --- a/src/menus/TrackMenus.cpp +++ b/src/menus/TrackMenus.cpp @@ -1,6 +1,7 @@ #include "../Audacity.h" #include "../Experimental.h" +#include "../CommonCommandFlags.h" #include "../LabelTrack.h" #include "../Menus.h" #include "../MissingAliasFileDialog.h" diff --git a/src/menus/TransportMenus.cpp b/src/menus/TransportMenus.cpp index f1a600c6e..c0689c090 100644 --- a/src/menus/TransportMenus.cpp +++ b/src/menus/TransportMenus.cpp @@ -3,6 +3,7 @@ #include "../AdornedRulerPanel.h" #include "../AudioIO.h" +#include "../CommonCommandFlags.h" #include "../DeviceManager.h" #include "../LabelTrack.h" #include "../Menus.h" diff --git a/src/menus/ViewMenus.cpp b/src/menus/ViewMenus.cpp index 98584a22b..c78a3f4ae 100644 --- a/src/menus/ViewMenus.cpp +++ b/src/menus/ViewMenus.cpp @@ -1,6 +1,7 @@ #include "../Audacity.h" #include "../Experimental.h" +#include "../CommonCommandFlags.h" #include "../HistoryWindow.h" #include "../LyricsWindow.h" #include "../Menus.h" diff --git a/src/menus/WindowMenus.cpp b/src/menus/WindowMenus.cpp index f75b82474..7da14a17d 100644 --- a/src/menus/WindowMenus.cpp +++ b/src/menus/WindowMenus.cpp @@ -7,6 +7,7 @@ #ifdef __WXMAC__ +#include "../CommonCommandFlags.h" #include "../Menus.h" #include "../Project.h" #include "../commands/CommandContext.h" diff --git a/src/toolbars/ControlToolBar.cpp b/src/toolbars/ControlToolBar.cpp index b455f28f4..983914d8c 100644 --- a/src/toolbars/ControlToolBar.cpp +++ b/src/toolbars/ControlToolBar.cpp @@ -60,6 +60,7 @@ #include "../AdornedRulerPanel.h" #include "../AllThemeResources.h" #include "../AudioIO.h" +#include "../CommonCommandFlags.h" #include "../ImageManipulation.h" #include "../Menus.h" #include "../Prefs.h" @@ -1562,3 +1563,10 @@ static RegisteredToolbarFactory factory{ TransportBarID, []( AudacityProject &project ){ return ToolBar::Holder{ safenew ControlToolBar{ project } }; } }; + +const ReservedCommandFlag + CanStopAudioStreamFlag{ + [](const AudacityProject &project){ + return ControlToolBar::Get( project ).CanStopAudioStream(); + } + }; diff --git a/src/toolbars/ControlToolBar.h b/src/toolbars/ControlToolBar.h index d4d438490..13ff7bd9b 100644 --- a/src/toolbars/ControlToolBar.h +++ b/src/toolbars/ControlToolBar.h @@ -210,5 +210,10 @@ class ControlToolBar final : public ToolBar { DECLARE_EVENT_TABLE() }; +#include "../commands/CommandFlag.h" + +extern const ReservedCommandFlag + CanStopAudioStreamFlag; + #endif diff --git a/src/tracks/ui/Scrubbing.cpp b/src/tracks/ui/Scrubbing.cpp index afaf6b5fd..d55779abd 100644 --- a/src/tracks/ui/Scrubbing.cpp +++ b/src/tracks/ui/Scrubbing.cpp @@ -17,6 +17,7 @@ Paul Licameli split from TrackPanel.cpp #include "../../AdornedRulerPanel.h" #include "../../AudioIO.h" +#include "../../CommonCommandFlags.h" #include "../../Menus.h" #include "../../Project.h" #include "../../ProjectAudioIO.h" From c5065369122913fb74293d57cd152bd05789abd6 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Thu, 13 Jun 2019 15:24:17 -0400 Subject: [PATCH 20/23] Menus.cpp does not depend directly on ProjectWindow --- src/Menus.cpp | 10 ++++++++-- src/ProjectWindow.h | 2 -- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Menus.cpp b/src/Menus.cpp index c1274ac95..e6f2beb22 100644 --- a/src/Menus.cpp +++ b/src/Menus.cpp @@ -36,7 +36,6 @@ #include "Project.h" #include "ProjectHistory.h" #include "ProjectSettings.h" -#include "ProjectWindow.h" #include "UndoManager.h" #include "commands/CommandManager.h" #include "prefs/TracksPrefs.h" @@ -352,6 +351,13 @@ void MenuManager::ModifyUndoMenuItems(AudacityProject &project) } } +// Get hackcess to a protected method +class wxFrameEx : public wxFrame +{ +public: + using wxFrame::DetachMenuBar; +}; + void MenuCreator::RebuildMenuBar(AudacityProject &project) { // On OSX, we can't rebuild the menus while a modal dialog is being shown @@ -368,7 +374,7 @@ void MenuCreator::RebuildMenuBar(AudacityProject &project) // 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 ); + auto &window = static_cast( GetProjectFrame( project ) ); wxWindowPtr menuBar{ window.GetMenuBar() }; window.DetachMenuBar(); // menuBar gets deleted here diff --git a/src/ProjectWindow.h b/src/ProjectWindow.h index 6b58c59b8..8ef7fa16c 100644 --- a/src/ProjectWindow.h +++ b/src/ProjectWindow.h @@ -79,8 +79,6 @@ public: }; PlaybackScroller &GetPlaybackScroller() { return *mPlaybackScroller; } - using wxFrame::DetachMenuBar; - void SetNormalizedWindowState(wxRect pSizeAndLocation) { mNormalizedWindowState = pSizeAndLocation; } wxRect GetNormalizedWindowState() const { return mNormalizedWindowState; } From b84f5b66f0bb875116937889a1e2e4f4c20da99b Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Fri, 14 Jun 2019 00:50:21 -0400 Subject: [PATCH 21/23] CommandManager.cpp does not depend on EffectManager... ... some code lifted into BatchCommands.cpp to realize this. It also doesn't depend on PluginManager, but that is not important for breaking cycles. --- src/BatchCommands.cpp | 41 ++++++++++++++++++++++++++++-- src/BatchCommands.h | 5 ++++ src/commands/CommandManager.cpp | 39 +++++++--------------------- src/commands/CommandManager.h | 11 +++++++- src/commands/ScreenshotCommand.cpp | 4 ++- src/toolbars/EditToolBar.cpp | 4 ++- 6 files changed, 69 insertions(+), 35 deletions(-) diff --git a/src/BatchCommands.cpp b/src/BatchCommands.cpp index a6f0ad835..e748cf268 100644 --- a/src/BatchCommands.cpp +++ b/src/BatchCommands.cpp @@ -749,6 +749,41 @@ bool MacroCommands::ApplyEffectCommand( return res; } +bool MacroCommands::HandleTextualCommand( CommandManager &commandManager, + const CommandID & Str, + const CommandContext & context, CommandFlag flags, bool alwaysEnabled) +{ + switch ( commandManager.HandleTextualCommand( + Str, context, flags, alwaysEnabled) ) { + case CommandManager::CommandSuccess: + return true; + case CommandManager::CommandFailure: + return false; + case CommandManager::CommandNotFound: + default: + break; + } + + // Not one of the singleton commands. + // We could/should try all the list-style commands. + // instead we only try the effects. + PluginManager & pm = PluginManager::Get(); + EffectManager & em = EffectManager::Get(); + const PluginDescriptor *plug = pm.GetFirstPlugin(PluginTypeEffect); + while (plug) + { + if (em.GetCommandIdentifier(plug->GetID()) == Str) + { + return PluginActions::DoEffect( + plug->GetID(), context, + PluginActions::kConfigured); + } + plug = pm.GetNextPlugin(PluginTypeEffect); + } + + return false; +} + bool MacroCommands::ApplyCommand( const wxString &friendlyCommand, const CommandID & command, const wxString & params, CommandContext const * pContext) @@ -778,7 +813,8 @@ bool MacroCommands::ApplyCommand( const wxString &friendlyCommand, AudacityProject *project = GetActiveProject(); auto &manager = CommandManager::Get( *project ); if( pContext ){ - if( manager.HandleTextualCommand( command, *pContext, AlwaysEnabledFlag, true ) ) + if( HandleTextualCommand( + manager, command, *pContext, AlwaysEnabledFlag, true ) ) return true; pContext->Status( wxString::Format( _("Your batch command of %s was not recognized."), friendlyCommand )); @@ -787,7 +823,8 @@ bool MacroCommands::ApplyCommand( const wxString &friendlyCommand, else { const CommandContext context( *GetActiveProject() ); - if( manager.HandleTextualCommand( command, context, AlwaysEnabledFlag, true ) ) + if( HandleTextualCommand( + manager, command, context, AlwaysEnabledFlag, true ) ) return true; } diff --git a/src/BatchCommands.h b/src/BatchCommands.h index 1dee0f74f..642840950 100644 --- a/src/BatchCommands.h +++ b/src/BatchCommands.h @@ -15,10 +15,12 @@ #include #include "export/Export.h" +#include "commands/CommandFlag.h" class wxArrayString; class Effect; class CommandContext; +class CommandManager; class AudacityProject; class wxArrayStringEx; @@ -57,6 +59,9 @@ class MacroCommands final { public: bool ApplyMacro( const MacroCommandsCatalog &catalog, const wxString & filename = {}); + static bool HandleTextualCommand( CommandManager &commandManager, + const CommandID & Str, + const CommandContext & context, CommandFlag flags, bool alwaysEnabled); bool ApplyCommand( const wxString &friendlyCommand, const CommandID & command, const wxString & params, CommandContext const * pContext=NULL ); diff --git a/src/commands/CommandManager.cpp b/src/commands/CommandManager.cpp index babad283a..b29a65b8f 100644 --- a/src/commands/CommandManager.cpp +++ b/src/commands/CommandManager.cpp @@ -96,9 +96,7 @@ CommandManager. It holds the callback for one command. #include "../Menus.h" -#include "../PluginManager.h" #include "../Project.h" -#include "../effects/EffectManager.h" #include "../widgets/LinkingHtmlWindow.h" #include "../widgets/AudacityMessageBox.h" #include "../widgets/HelpSystem.h" @@ -1238,10 +1236,12 @@ bool CommandManager::HandleMenuID(int id, CommandFlag flags, bool alwaysEnabled) /// HandleTextualCommand() allows us a limitted version of script/batch /// behavior, since we can get from a string command name to the actual /// code to run. -bool CommandManager::HandleTextualCommand(const CommandID & Str, const CommandContext & context, CommandFlag flags, bool alwaysEnabled) +CommandManager::TextualCommandResult +CommandManager::HandleTextualCommand(const CommandID & Str, + const CommandContext & context, CommandFlag flags, bool alwaysEnabled) { if( Str.empty() ) - return false; + return CommandFailure; // Linear search for now... for (const auto &entry : mCommandList) { @@ -1254,7 +1254,8 @@ bool CommandManager::HandleTextualCommand(const CommandID & Str, const CommandCo // sub-menu name) Str == entry->labelPrefix ) { - return HandleCommandEntry( entry.get(), flags, alwaysEnabled); + return HandleCommandEntry( entry.get(), flags, alwaysEnabled) + ? CommandSuccess : CommandFailure; } } else @@ -1262,34 +1263,12 @@ bool CommandManager::HandleTextualCommand(const CommandID & Str, const CommandCo // Handle multis too... if( Str == entry->name ) { - return HandleCommandEntry( entry.get(), flags, alwaysEnabled); + return HandleCommandEntry( entry.get(), flags, alwaysEnabled) + ? CommandSuccess : CommandFailure; } } } - // Not one of the singleton commands. - // We could/should try all the list-style commands. - // instead we only try the effects. - AudacityProject * proj = GetActiveProject(); - if( !proj ) - { - return false; - } - - PluginManager & pm = PluginManager::Get(); - EffectManager & em = EffectManager::Get(); - const PluginDescriptor *plug = pm.GetFirstPlugin(PluginTypeEffect); - while (plug) - { - if (em.GetCommandIdentifier(plug->GetID()) == Str) - { - return PluginActions::DoEffect( - plug->GetID(), context, - PluginActions::kConfigured); - } - plug = pm.GetNextPlugin(PluginTypeEffect); - } - - return false; + return CommandNotFound; } void CommandManager::GetCategories(wxArrayString &cats) diff --git a/src/commands/CommandManager.h b/src/commands/CommandManager.h index 986175e35..28009f171 100644 --- a/src/commands/CommandManager.h +++ b/src/commands/CommandManager.h @@ -244,7 +244,16 @@ class AUDACITY_DLL_API CommandManager final // Lyrics and MixerTrackCluster classes use it. bool FilterKeyEvent(AudacityProject *project, const wxKeyEvent & evt, bool permit = false); bool HandleMenuID(int id, CommandFlag flags, bool alwaysEnabled); - bool HandleTextualCommand(const CommandID & Str, const CommandContext & context, CommandFlag flags, bool alwaysEnabled); + + enum TextualCommandResult { + CommandFailure, + CommandSuccess, + CommandNotFound + }; + + TextualCommandResult + HandleTextualCommand(const CommandID & Str, + const CommandContext & context, CommandFlag flags, bool alwaysEnabled); // // Accessing diff --git a/src/commands/ScreenshotCommand.cpp b/src/commands/ScreenshotCommand.cpp index 51aa901a8..4706a8c7f 100644 --- a/src/commands/ScreenshotCommand.cpp +++ b/src/commands/ScreenshotCommand.cpp @@ -32,6 +32,7 @@ small calculations of rectangles. #include #include "../AdornedRulerPanel.h" +#include "../BatchCommands.h" #include "../TrackPanel.h" #include "../effects/Effect.h" #include "../toolbars/ToolManager.h" @@ -454,7 +455,8 @@ void ScreenshotCommand::CapturePreferences( gPrefs->Flush(); CommandID Command{ wxT("Preferences") }; const CommandContext projectContext( *pProject ); - if( !commandManager.HandleTextualCommand( Command, projectContext, AlwaysEnabledFlag, true ) ) + if( !MacroCommands::HandleTextualCommand( commandManager, + Command, projectContext, AlwaysEnabledFlag, true ) ) { // using GET in a log message for devs' eyes only wxLogDebug("Command %s not found", Command.GET() ); diff --git a/src/toolbars/EditToolBar.cpp b/src/toolbars/EditToolBar.cpp index 098ee19e7..3bec6f26f 100644 --- a/src/toolbars/EditToolBar.cpp +++ b/src/toolbars/EditToolBar.cpp @@ -49,6 +49,7 @@ #endif #include "../AllThemeResources.h" +#include "../BatchCommands.h" #include "../ImageManipulation.h" #include "../Menus.h" #include "../Prefs.h" @@ -299,7 +300,8 @@ void EditToolBar::OnButton(wxCommandEvent &event) auto flags = MenuManager::Get(*p).GetUpdateFlags(); const CommandContext context( *p ); - cm.HandleTextualCommand(EditToolbarButtonList[id].commandName, context, flags, false); + MacroCommands::HandleTextualCommand( cm, + EditToolbarButtonList[id].commandName, context, flags, false); } static RegisteredToolbarFactory factory{ EditBarID, From 0b897c81b05893a403a2879a8e55fe4463d8bd82 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sat, 15 Jun 2019 09:03:30 -0400 Subject: [PATCH 22/23] ToolManager.cpp does not depend on Meter --- src/toolbars/ToolManager.cpp | 4 ++-- src/widgets/Meter.cpp | 9 +-------- src/widgets/Meter.h | 10 ---------- src/widgets/MeterPanelBase.cpp | 7 +++++++ src/widgets/MeterPanelBase.h | 10 ++++++++++ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/toolbars/ToolManager.cpp b/src/toolbars/ToolManager.cpp index 696c9d0cf..425b82be7 100644 --- a/src/toolbars/ToolManager.cpp +++ b/src/toolbars/ToolManager.cpp @@ -60,7 +60,7 @@ #include "../ProjectWindow.h" #include "../widgets/AButton.h" #include "../widgets/ASlider.h" -#include "../widgets/Meter.h" +#include "../widgets/MeterPanelBase.h" #include "../widgets/Grabber.h" //////////////////////////////////////////////////////////// @@ -1527,7 +1527,7 @@ bool ToolManager::RestoreFocus() if (mLastFocus) { auto temp1 = AButton::TemporarilyAllowFocus(); auto temp2 = ASlider::TemporarilyAllowFocus(); - auto temp3 = MeterPanel::TemporarilyAllowFocus(); + auto temp3 = MeterPanelBase::TemporarilyAllowFocus(); mLastFocus->SetFocus(); return true; } diff --git a/src/widgets/Meter.cpp b/src/widgets/Meter.cpp index 24b6f3336..3f452e596 100644 --- a/src/widgets/Meter.cpp +++ b/src/widgets/Meter.cpp @@ -268,7 +268,7 @@ enum { OnPreferencesID }; -BEGIN_EVENT_TABLE(MeterPanel, wxPanelWrapper) +BEGIN_EVENT_TABLE(MeterPanel, MeterPanelBase) EVT_TIMER(OnMeterUpdateID, MeterPanel::OnMeterUpdate) EVT_MOUSE_EVENTS(MeterPanel::OnMouse) EVT_CONTEXT_MENU(MeterPanel::OnContext) @@ -2123,13 +2123,6 @@ wxString MeterPanel::Key(const wxString & key) const return wxT("/Meter/Output/") + key; } -bool MeterPanel::s_AcceptsFocus{ false }; - -auto MeterPanel::TemporarilyAllowFocus() -> TempAllowFocus { - s_AcceptsFocus = true; - return TempAllowFocus{ &s_AcceptsFocus }; -} - // This compensates for a but in wxWidgets 3.0.2 for mac: // Couldn't set focus from keyboard when AcceptsFocus returns false; // this bypasses that limitation diff --git a/src/widgets/Meter.h b/src/widgets/Meter.h index e8dc551cd..c000ac8ce 100644 --- a/src/widgets/Meter.h +++ b/src/widgets/Meter.h @@ -117,9 +117,6 @@ class MeterPanel final : public MeterPanelBase, private PrefsListener Style style = HorizontalStereo, float fDecayRate = 60.0f); - bool AcceptsFocus() const override { return s_AcceptsFocus; } - bool AcceptsFocusFromKeyboard() const override { return true; } - void SetFocusFromKbd() override; void Clear() override; @@ -192,13 +189,6 @@ class MeterPanel final : public MeterPanelBase, private PrefsListener void UpdatePrefs() override; void UpdateSelectedPrefs( int ) override; - static bool s_AcceptsFocus; - struct Resetter { void operator () (bool *p) const { if(p) *p = false; } }; - using TempAllowFocus = std::unique_ptr; - - public: - static TempAllowFocus TemporarilyAllowFocus(); - private: // // Event handlers diff --git a/src/widgets/MeterPanelBase.cpp b/src/widgets/MeterPanelBase.cpp index 46eb81897..f85e13acf 100644 --- a/src/widgets/MeterPanelBase.cpp +++ b/src/widgets/MeterPanelBase.cpp @@ -13,3 +13,10 @@ Paul Licameli split from Meter.cpp MeterPanelBase::~MeterPanelBase() { } + +bool MeterPanelBase::s_AcceptsFocus{ false }; + +auto MeterPanelBase::TemporarilyAllowFocus() -> TempAllowFocus { + s_AcceptsFocus = true; + return TempAllowFocus{ &s_AcceptsFocus }; +} diff --git a/src/widgets/MeterPanelBase.h b/src/widgets/MeterPanelBase.h index c2b459f3e..5cdee6728 100644 --- a/src/widgets/MeterPanelBase.h +++ b/src/widgets/MeterPanelBase.h @@ -26,7 +26,17 @@ public: int numFrames, float *sampleData) = 0; virtual bool IsMeterDisabled() const = 0; virtual float GetMaxPeak() const = 0; + + bool AcceptsFocus() const override { return s_AcceptsFocus; } + bool AcceptsFocusFromKeyboard() const override { return true; } + private: + static bool s_AcceptsFocus; + struct Resetter { void operator () (bool *p) const { if(p) *p = false; } }; + using TempAllowFocus = std::unique_ptr; + +public: + static TempAllowFocus TemporarilyAllowFocus(); }; #endif From e1908ab8e20e9ddc5b4f9fa0beaab94dbe3937fe Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sat, 15 Jun 2019 09:27:23 -0400 Subject: [PATCH 23/23] ToolManager does not depend on ProjectWindow --- src/ProjectWindow.cpp | 9 +++++++++ src/toolbars/ToolManager.cpp | 30 ++++++++++++++++++++++++------ src/toolbars/ToolManager.h | 5 +++++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/ProjectWindow.cpp b/src/ProjectWindow.cpp index 0663da850..f8b0b78b8 100644 --- a/src/ProjectWindow.cpp +++ b/src/ProjectWindow.cpp @@ -1887,3 +1887,12 @@ void ProjectWindow::ZoomOutByFactor( double ZoomFactor ) // newh = (newh > 0) ? newh : 0; TP_ScrollWindow(newh); } + +static struct InstallTopPanelHook{ InstallTopPanelHook() { + ToolManager::SetGetTopPanelHook( + []( wxWindow &window ){ + auto pProjectWindow = dynamic_cast< ProjectWindow* >( &window ); + return pProjectWindow ? pProjectWindow->GetTopPanel() : nullptr; + } + ); +}} installTopPanelHook; diff --git a/src/toolbars/ToolManager.cpp b/src/toolbars/ToolManager.cpp index 425b82be7..1b98e9155 100644 --- a/src/toolbars/ToolManager.cpp +++ b/src/toolbars/ToolManager.cpp @@ -57,7 +57,6 @@ #include "../ImageManipulation.h" #include "../Prefs.h" #include "../Project.h" -#include "../ProjectWindow.h" #include "../widgets/AButton.h" #include "../widgets/ASlider.h" #include "../widgets/MeterPanelBase.h" @@ -73,7 +72,7 @@ // ToolFrame::ToolFrame ( AudacityProject *parent, ToolManager *manager, ToolBar *bar, wxPoint pos ) - : wxFrame( ProjectWindow::Find( parent ), + : wxFrame( FindProjectFrame( parent ), bar->GetId(), wxEmptyString, pos, @@ -312,10 +311,26 @@ BEGIN_EVENT_TABLE( ToolManager, wxEvtHandler ) EVT_TIMER( wxID_ANY, ToolManager::OnTimer ) END_EVENT_TABLE() +static ToolManager::GetTopPanelHook &getTopPanelHook() +{ + static ToolManager::GetTopPanelHook theHook; + return theHook; +} + +auto ToolManager::SetGetTopPanelHook( const GetTopPanelHook &hook ) + -> GetTopPanelHook +{ + auto &theHook = getTopPanelHook(); + auto result = theHook; + theHook = hook; + return result; +} + static const AudacityProject::AttachedObjects::RegisteredFactory key{ []( AudacityProject &parent ){ - auto &window = ProjectWindow::Get( parent ); - return std::make_shared< ToolManager >( &parent, window.GetTopPanel() ); } + auto &window = GetProjectFrame( parent ); + return std::make_shared< ToolManager >( + &parent, getTopPanelHook()( window ) ); } }; ToolManager &ToolManager::Get( AudacityProject &project ) @@ -334,7 +349,10 @@ const ToolManager &ToolManager::Get( const AudacityProject &project ) ToolManager::ToolManager( AudacityProject *parent, wxWindow *topDockParent ) : wxEvtHandler() { - auto &window = ProjectWindow::Get( *parent ); + if ( !topDockParent ) + THROW_INCONSISTENCY_EXCEPTION; + + auto &window = GetProjectFrame( *parent ); wxPoint pt[ 3 ]; #if defined(__WXMAC__) @@ -653,7 +671,7 @@ int ToolManager::FilterEvent(wxEvent &event) if ( window && !dynamic_cast( window ) && !dynamic_cast( window ) && - top == ProjectWindow::Find( mParent ) ) + top == FindProjectFrame( mParent ) ) // Note this is a dangle-proof wxWindowRef: mLastFocus = window; } diff --git a/src/toolbars/ToolManager.h b/src/toolbars/ToolManager.h index 820512645..1d1ede681 100644 --- a/src/toolbars/ToolManager.h +++ b/src/toolbars/ToolManager.h @@ -13,6 +13,8 @@ #ifndef __AUDACITY_TOOLMANAGER__ #define __AUDACITY_TOOLMANAGER__ +#include + #include #include // to inherit #include // to inherit @@ -48,6 +50,9 @@ class ToolManager final { public: + // a hook function to break dependency of ToolManager on ProjectWindow + using GetTopPanelHook = std::function< wxWindow*( wxWindow& ) >; + static GetTopPanelHook SetGetTopPanelHook( const GetTopPanelHook& ); static ToolManager &Get( AudacityProject &project ); static const ToolManager &Get( const AudacityProject &project );